From a6577856845c8eb90200fa241fda5dd374efb744 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 7 Jun 2022 13:58:03 -0400
Subject: [PATCH 001/108] Allow gates draggable

---
 example/script.js |   2 +-
 src/circuit.ts    |   8 +-
 src/editable.ts   | 302 ++++++++++++++++++++++++++++++++++++++++++++++
 src/index.ts      |   4 +-
 src/sqore.ts      |  23 ++--
 5 files changed, 323 insertions(+), 16 deletions(-)
 create mode 100644 src/editable.ts

diff --git a/example/script.js b/example/script.js
index f6d62df9..ae8c42c3 100644
--- a/example/script.js
+++ b/example/script.js
@@ -12,7 +12,7 @@ if (typeof qviz != 'undefined') {
 
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
-        qviz.draw(sample, sampleDiv, qviz.STYLES['Default']);
+        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, true);
     }
 
     const teleportDiv = document.getElementById('teleport');
diff --git a/src/circuit.ts b/src/circuit.ts
index 685215e3..2163c8e5 100644
--- a/src/circuit.ts
+++ b/src/circuit.ts
@@ -54,13 +54,13 @@ export interface Operation {
     /** Nested operations within this operation. */
     children?: Operation[];
     /** Whether gate is a measurement operation. */
-    isMeasurement: boolean;
+    isMeasurement?: boolean;
     /** Whether gate is a conditional operation. */
-    isConditional: boolean;
+    isConditional?: boolean;
     /** Whether gate is a controlled operation. */
-    isControlled: boolean;
+    isControlled?: boolean;
     /** Whether gate is an adjoint operation. */
-    isAdjoint: boolean;
+    isAdjoint?: boolean;
     /** Control registers the gate acts on. */
     controls?: Register[];
     /** Target registers the gate acts on. */
diff --git a/src/editable.ts b/src/editable.ts
new file mode 100644
index 00000000..5f56f04d
--- /dev/null
+++ b/src/editable.ts
@@ -0,0 +1,302 @@
+import { Operation } from './circuit';
+import { leftPadding } from './constants';
+import { box } from './formatters/formatUtils';
+import { Sqore } from './sqore';
+
+interface Context {
+    container: HTMLElement;
+    operations: Operation[];
+    wires: Wires;
+    renderFn: () => void;
+}
+
+interface Wires {
+    [y: string]: string;
+}
+
+let _sourceTarget: SVGElement | null;
+
+const addEditable = (container: HTMLElement, sqore: Sqore): void => {
+    const context: Context = {
+        container: container,
+        operations: sqore.circuit.operations,
+        wires: getWireElemsY(container),
+        renderFn: () => sqore.draw(container, 0, true),
+    };
+    addCustomStyles(container);
+    addDropzones(container);
+    addDocumentEvents(container);
+    addDropzoneEvents(context);
+    addMouseEvents(context);
+};
+
+// Commands
+
+const addCustomStyles = (container: HTMLElement) => {
+    const style = container.querySelector<HTMLStyleElement>('style');
+    if (style) {
+        style.innerHTML += `
+            .dropzone {
+                fill: transparent;
+                stroke: transparent;
+            }
+            .dropzone:hover{
+                fill: red;
+                opacity: 25%;
+            }
+            text {
+                user-select: none;
+            } 
+            .copying {
+                cursor: copy;
+            }
+            .moving {
+                cursor: move;
+            }
+            .detail-panel {
+                display: flex;
+                align-content: center;
+                gap: 12px;
+            }
+        `;
+    }
+};
+
+const addDropzones = (container: HTMLElement) => {
+    const gateElems = getGateElems(container);
+    gateElems.forEach((gateElem) => {
+        const { x, y, width, height } = gateElem.getBBox({ stroke: true });
+        const dataId = getDataId(gateElem);
+        gateElem.append(createLeftDropzone(x, y, height, dataId));
+        gateElem.append(createRightDropzone(x, y, width, height, dataId));
+    });
+};
+
+const addDocumentEvents = (container: HTMLElement) => {
+    container.addEventListener('click', (ev: MouseEvent) => {
+        _sourceTarget = null;
+        if (ev.ctrlKey) return;
+    });
+    container.addEventListener('contextmenu', (ev: MouseEvent) => {
+        ev.preventDefault();
+    });
+    container.addEventListener('mouseup', () => {
+        cursorCopy(container, false);
+        cursorMove(container, false);
+    });
+};
+
+const addDropzoneEvents = (context: Context) => {
+    const { container } = context;
+    const dropzoneElems = container.querySelectorAll<SVGRectElement>('.dropzone');
+    dropzoneElems.forEach((dropzoneElem) => {
+        dropzoneElem.addEventListener('mouseup', (ev: MouseEvent) => handleDropzoneMouseUp(ev, context));
+    });
+};
+
+const addMouseEvents = (context: Context) => {
+    const { container } = context;
+    const gateElems = getGateElems(container);
+    gateElems.forEach((gateElem) => {
+        gateElem.addEventListener('mousedown', (ev: MouseEvent) => handleGateMouseDown(ev, container));
+    });
+};
+
+// Event handlers
+const handleGateMouseDown = (ev: MouseEvent, container: HTMLElement) => {
+    ev.stopPropagation();
+    _sourceTarget = ev.currentTarget as SVGGElement;
+
+    // Ctrl + Mousedown to copy. Mousedown only to move.
+    ev.ctrlKey ? cursorCopy(container, true) : cursorMove(container, true);
+};
+
+const handleDropzoneMouseUp = (ev: MouseEvent, context: Context) => {
+    ev.stopPropagation();
+
+    const { container, operations, wires, renderFn } = context;
+
+    const currentTarget = ev.currentTarget as SVGGElement;
+
+    if (!currentTarget) return false;
+
+    const dataId = getDataId(currentTarget);
+    const parent = getParent(dataId, operations);
+    const index = splitDataId(dataId).pop();
+    const position = getDropzonePosition(currentTarget);
+
+    if (_sourceTarget == null) return false;
+
+    const sourceDataId = getDataId(_sourceTarget);
+    const sourceParent = getParent(sourceDataId, operations);
+    const sourceIndex = splitDataId(sourceDataId).pop();
+
+    if (index == null || sourceIndex == null) return false;
+
+    const newGate = getGate(sourceDataId, operations);
+    const wireY = getClosestWireY(ev.offsetY, wires);
+
+    // Not allow Measure gate to move vertically
+    if (wireY != null && newGate.gate !== 'measure') {
+        console.log(wires[wireY]);
+        // wires[wireY] returns qubit name (i.e: 'q0')
+        // this remove 'q' and assign an index (i.e: 0)
+        const index = Number(wires[wireY].slice(1));
+        const [firstTarget, ...targetsExceptFirst] = newGate.targets;
+        // Reserve all other properties, only change qId
+        Object.assign(firstTarget, { ...firstTarget, qId: index });
+        // Reserve all other targets, only change first target
+        Object.assign(newGate, { ...newGate, targets: [firstTarget, ...targetsExceptFirst] });
+    }
+
+    // Remove source element if moving using Ctrl + Mousedown
+    if (!ev.ctrlKey) {
+        deleteAt(sourceParent, sourceIndex);
+    }
+
+    // If dropzone is left of gate, insert before gate.
+    // Otherwise, insert after.
+    if (position === 'left') {
+        insertBefore(parent, index, newGate);
+    } else {
+        insertAfter(parent, index, newGate);
+    }
+
+    // Remove cursor styles
+    cursorCopy(container, false);
+    cursorMove(container, false);
+
+    // Redraw the circuit
+    renderFn();
+};
+
+// Element getters
+
+const getGateElems = (container: HTMLElement): SVGGElement[] => {
+    return Array.from(container.querySelectorAll('g.gate'));
+};
+
+const getWireElems = (container: HTMLElement) => {
+    // elems include qubit wires and lines of measure gates
+    const elems = container.querySelectorAll<SVGGElement>('svg > g:nth-child(3) > g');
+    // filter out <g> elements having more than 2 elements because
+    // qubit wires contain only 2 elements: <line> and <text>
+    // lines of measure gates contain 4 <line> elements
+    return Array.from(elems).filter((elem) => elem.childElementCount < 3);
+};
+
+// Element creators
+
+const createDropzone = (
+    x: number,
+    y: number,
+    width: number,
+    height: number,
+    dataId: string,
+    position: 'left' | 'right',
+): SVGElement => {
+    const dropzone = box(x, y, width, height, `dropzone`);
+    dropzone.setAttribute('data-id', dataId);
+    dropzone.setAttribute('data-dropzone-position', position);
+    return dropzone;
+};
+
+const createLeftDropzone = (x: number, y: number, height: number, dataId: string): SVGElement => {
+    return createDropzone(x - leftPadding / 2, y, leftPadding / 2, height, dataId, 'left');
+};
+const createRightDropzone = (x: number, y: number, width: number, height: number, dataId: string): SVGElement => {
+    return createDropzone(x + width, y, leftPadding / 2, height, dataId, 'right');
+};
+
+// Operation getters
+
+const getParent = (dataId: string, operations: Operation[]): Operation[] => {
+    const segments = splitDataId(dataId);
+    // Remove last segment to navigate to parent instead of child
+    segments.pop();
+
+    let parent = operations;
+    for (const segment of segments) {
+        parent = parent[segment].children || parent;
+    }
+    return parent;
+};
+
+const getGate = (dataId: string, operations: Operation[]): Operation => {
+    const parent = getParent(dataId, operations);
+    const index = splitDataId(dataId).pop();
+
+    if (index == null) {
+        throw new Error('Gate not found');
+    }
+
+    return parent[index];
+};
+
+// Utilities
+const getDataId = (element: Element): string => {
+    return element.getAttribute('data-id') || '';
+};
+
+const splitDataId = (dataId: string): number[] => {
+    return dataId.split('-').map(Number);
+};
+
+const getWireElemsY = (container: HTMLElement): Wires => {
+    const wireElems = getWireElems(container);
+    return wireElems.reduce((previous, current) => {
+        const y = getWireElemY(current);
+        const text = getWireElemText(current);
+        return { ...previous, [`${y}`]: text };
+    }, {});
+};
+
+const getWireElemY = (wireElem: SVGGElement): number => {
+    const lineElem = wireElem.querySelector<SVGLineElement>('line');
+    if (lineElem == null || lineElem.y1.baseVal.value == null) throw Error('y not found');
+    return lineElem.y1.baseVal.value;
+};
+const getWireElemText = (wireElem: SVGGElement): string => {
+    const textElem = wireElem.querySelector<SVGTextElement>('text');
+    if (textElem == null || textElem.textContent == null) throw new Error('Text not found');
+    return textElem.textContent;
+};
+
+const getClosestWireY = (offsetY: number, wires: { [y: number]: string }) => {
+    let wireY;
+    Object.entries(wires).forEach((wire) => {
+        const y = wire[0];
+        const distance = Math.abs(offsetY - Number(y));
+        // 15 is a magic number
+        if (distance < 15) {
+            wireY = y;
+        }
+    });
+    return wireY || null;
+};
+
+const getDropzonePosition = (element: SVGElement) => {
+    return element.getAttribute('data-dropzone-position');
+};
+
+const insertBefore = (parent: Operation[], index: number, newGate: Operation): void => {
+    parent.splice(index, 0, newGate);
+};
+
+const insertAfter = (parent: Operation[], index: number, newGate: Operation): void => {
+    parent.splice(index + 1, 0, newGate);
+};
+
+const deleteAt = (parent: Operation[], index: number): void => {
+    parent.splice(index, 1);
+};
+
+const cursorMove = (container: HTMLElement, value: boolean) => {
+    value ? container.classList.add('moving') : container.classList.remove('moving');
+};
+
+const cursorCopy = (container: HTMLElement, value: boolean) => {
+    value ? container.classList.add('copying') : container.classList.remove('copying');
+};
+
+export { addEditable };
diff --git a/src/index.ts b/src/index.ts
index 0a040268..8a9093bc 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -18,9 +18,11 @@ export const draw = (
     container: HTMLElement,
     style: StyleConfig | string = {},
     renderDepth = 0,
+    editable = false,
 ): void => {
     const sqore = new Sqore(circuit, style);
-    sqore.draw(container, renderDepth);
+
+    sqore.draw(container, renderDepth, editable);
 };
 
 export { STYLES } from './styles';
diff --git a/src/sqore.ts b/src/sqore.ts
index c20ba65c..e0d85dfc 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -10,6 +10,7 @@ import { Metadata, GateType } from './metadata';
 import { StyleConfig, style, STYLES } from './styles';
 import { createUUID } from './utils';
 import { svgNS } from './constants';
+import { addEditable } from './editable';
 
 /**
  * Contains metadata for visualization.
@@ -56,7 +57,7 @@ export class Sqore {
      * @param container HTML element for rendering visualization into.
      * @param renderDepth Initial layer depth at which to render gates.
      */
-    draw(container: HTMLElement, renderDepth = 0): void {
+    draw(container: HTMLElement, renderDepth = 0, editable = false): void {
         // Inject into container
         if (container == null) throw new Error(`Container not provided.`);
 
@@ -78,8 +79,7 @@ export class Sqore {
             const id: string = circuit.operations[0].dataAttributes['id'];
             this.expandOperation(circuit.operations, id);
         }
-
-        this.renderCircuit(container, circuit);
+        this.renderCircuit(container, circuit, editable);
     }
 
     /**
@@ -107,13 +107,17 @@ export class Sqore {
      * @param container HTML element for rendering visualization into.
      * @param circuit Circuit object to be rendered.
      */
-    private renderCircuit(container: HTMLElement, circuit: Circuit): void {
+    public renderCircuit(container: HTMLElement, circuit: Circuit, editable: boolean): void {
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);
         container.innerHTML = '';
         container.appendChild(svg);
-        this.addGateClickHandlers(container, circuit);
+        this.addGateClickHandlers(container, circuit, editable);
+
+        if (editable) {
+            addEditable(container, this);
+        }
     }
 
     /**
@@ -231,9 +235,9 @@ export class Sqore {
      * @param circuit Circuit to be visualized.
      *
      */
-    private addGateClickHandlers(container: HTMLElement, circuit: Circuit): void {
+    private addGateClickHandlers(container: HTMLElement, circuit: Circuit, editable: boolean): void {
         this.addClassicalControlHandlers(container);
-        this.addZoomHandlers(container, circuit);
+        this.addZoomHandlers(container, circuit, editable);
     }
 
     /**
@@ -291,7 +295,7 @@ export class Sqore {
      * @param circuit Circuit to be visualized.
      *
      */
-    private addZoomHandlers(container: HTMLElement, circuit: Circuit): void {
+    private addZoomHandlers(container: HTMLElement, circuit: Circuit, editable: boolean): void {
         container.querySelectorAll('.gate .gate-control').forEach((ctrl) => {
             // Zoom in on clicked gate
             ctrl.addEventListener('click', (ev: Event) => {
@@ -302,8 +306,7 @@ export class Sqore {
                     } else if (ctrl.classList.contains('gate-expand')) {
                         this.expandOperation(circuit.operations, gateId);
                     }
-                    this.renderCircuit(container, circuit);
-
+                    this.renderCircuit(container, circuit, editable);
                     ev.stopPropagation();
                 }
             });

From 9a66bc84a6e4b5854bc0a3da9ef77330b8285edb Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 7 Jun 2022 14:08:48 -0400
Subject: [PATCH 002/108] Add description to addEditable function

---
 src/editable.ts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/editable.ts b/src/editable.ts
index 5f56f04d..68c0b340 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -16,6 +16,14 @@ interface Wires {
 
 let _sourceTarget: SVGElement | null;
 
+/**
+ * Add editable elements and events.
+ *
+ * @param container     HTML element for rendering visualization into.
+ * @param sqore         Sqore object
+ *
+ */
+
 const addEditable = (container: HTMLElement, sqore: Sqore): void => {
     const context: Context = {
         container: container,

From d831c48717d4cb063e53a595b83acd01e9f6d1d5 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 7 Jun 2022 14:09:07 -0400
Subject: [PATCH 003/108] Add return types

---
 src/editable.ts | 28 +++++++++++++++-------------
 1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/src/editable.ts b/src/editable.ts
index 68c0b340..9e900205 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -40,7 +40,7 @@ const addEditable = (container: HTMLElement, sqore: Sqore): void => {
 
 // Commands
 
-const addCustomStyles = (container: HTMLElement) => {
+const addCustomStyles = (container: HTMLElement): void => {
     const style = container.querySelector<HTMLStyleElement>('style');
     if (style) {
         style.innerHTML += `
@@ -70,7 +70,7 @@ const addCustomStyles = (container: HTMLElement) => {
     }
 };
 
-const addDropzones = (container: HTMLElement) => {
+const addDropzones = (container: HTMLElement): void => {
     const gateElems = getGateElems(container);
     gateElems.forEach((gateElem) => {
         const { x, y, width, height } = gateElem.getBBox({ stroke: true });
@@ -80,7 +80,7 @@ const addDropzones = (container: HTMLElement) => {
     });
 };
 
-const addDocumentEvents = (container: HTMLElement) => {
+const addDocumentEvents = (container: HTMLElement): void => {
     container.addEventListener('click', (ev: MouseEvent) => {
         _sourceTarget = null;
         if (ev.ctrlKey) return;
@@ -94,7 +94,7 @@ const addDocumentEvents = (container: HTMLElement) => {
     });
 };
 
-const addDropzoneEvents = (context: Context) => {
+const addDropzoneEvents = (context: Context): void => {
     const { container } = context;
     const dropzoneElems = container.querySelectorAll<SVGRectElement>('.dropzone');
     dropzoneElems.forEach((dropzoneElem) => {
@@ -102,7 +102,7 @@ const addDropzoneEvents = (context: Context) => {
     });
 };
 
-const addMouseEvents = (context: Context) => {
+const addMouseEvents = (context: Context): void => {
     const { container } = context;
     const gateElems = getGateElems(container);
     gateElems.forEach((gateElem) => {
@@ -111,7 +111,7 @@ const addMouseEvents = (context: Context) => {
 };
 
 // Event handlers
-const handleGateMouseDown = (ev: MouseEvent, container: HTMLElement) => {
+const handleGateMouseDown = (ev: MouseEvent, container: HTMLElement): void => {
     ev.stopPropagation();
     _sourceTarget = ev.currentTarget as SVGGElement;
 
@@ -119,7 +119,7 @@ const handleGateMouseDown = (ev: MouseEvent, container: HTMLElement) => {
     ev.ctrlKey ? cursorCopy(container, true) : cursorMove(container, true);
 };
 
-const handleDropzoneMouseUp = (ev: MouseEvent, context: Context) => {
+const handleDropzoneMouseUp = (ev: MouseEvent, context: Context): void | false => {
     ev.stopPropagation();
 
     const { container, operations, wires, renderFn } = context;
@@ -184,7 +184,7 @@ const getGateElems = (container: HTMLElement): SVGGElement[] => {
     return Array.from(container.querySelectorAll('g.gate'));
 };
 
-const getWireElems = (container: HTMLElement) => {
+const getWireElems = (container: HTMLElement): SVGGElement[] => {
     // elems include qubit wires and lines of measure gates
     const elems = container.querySelectorAll<SVGGElement>('svg > g:nth-child(3) > g');
     // filter out <g> elements having more than 2 elements because
@@ -270,7 +270,7 @@ const getWireElemText = (wireElem: SVGGElement): string => {
     return textElem.textContent;
 };
 
-const getClosestWireY = (offsetY: number, wires: { [y: number]: string }) => {
+const getClosestWireY = (offsetY: number, wires: { [y: number]: string }): number | null => {
     let wireY;
     Object.entries(wires).forEach((wire) => {
         const y = wire[0];
@@ -283,8 +283,10 @@ const getClosestWireY = (offsetY: number, wires: { [y: number]: string }) => {
     return wireY || null;
 };
 
-const getDropzonePosition = (element: SVGElement) => {
-    return element.getAttribute('data-dropzone-position');
+const getDropzonePosition = (element: SVGElement): string => {
+    const position = element.getAttribute('data-dropzone-position');
+    if (position == null) throw new Error('Position not found');
+    return position;
 };
 
 const insertBefore = (parent: Operation[], index: number, newGate: Operation): void => {
@@ -299,11 +301,11 @@ const deleteAt = (parent: Operation[], index: number): void => {
     parent.splice(index, 1);
 };
 
-const cursorMove = (container: HTMLElement, value: boolean) => {
+const cursorMove = (container: HTMLElement, value: boolean): void => {
     value ? container.classList.add('moving') : container.classList.remove('moving');
 };
 
-const cursorCopy = (container: HTMLElement, value: boolean) => {
+const cursorCopy = (container: HTMLElement, value: boolean): void => {
     value ? container.classList.add('copying') : container.classList.remove('copying');
 };
 

From 13108145ce9b22e6b5b87b976077e075a6c0d214 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 7 Jun 2022 20:30:26 -0400
Subject: [PATCH 004/108] Add tests

---
 __tests__/__snapshots__/editable.test.ts.snap |  65 ++++
 __tests__/editable.test.ts                    | 361 ++++++++++++++++++
 src/editable.ts                               |  19 +-
 3 files changed, 442 insertions(+), 3 deletions(-)
 create mode 100644 __tests__/__snapshots__/editable.test.ts.snap
 create mode 100644 __tests__/editable.test.ts

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
new file mode 100644
index 00000000..637f61b6
--- /dev/null
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -0,0 +1,65 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Testing cursorCopy turn off copy cursor 1`] = `
+<div
+  class="copying"
+/>
+`;
+
+exports[`Testing cursorCopy turn off copy cursor 2`] = `
+<div
+  class=""
+/>
+`;
+
+exports[`Testing cursorCopy turn on and off copy cursor 1`] = `
+<div
+  class=""
+/>
+`;
+
+exports[`Testing cursorCopy turn on and off copy cursor 2`] = `
+<div
+  class=""
+/>
+`;
+
+exports[`Testing cursorCopy turn on copy cursor 1`] = `<div />`;
+
+exports[`Testing cursorCopy turn on copy cursor 2`] = `
+<div
+  class="copying"
+/>
+`;
+
+exports[`Testing cursorMove turn off move cursor 1`] = `
+<div
+  class="moving"
+/>
+`;
+
+exports[`Testing cursorMove turn off move cursor 2`] = `
+<div
+  class=""
+/>
+`;
+
+exports[`Testing cursorMove turn on and off move cursor 1`] = `
+<div
+  class=""
+/>
+`;
+
+exports[`Testing cursorMove turn on and off move cursor 2`] = `
+<div
+  class=""
+/>
+`;
+
+exports[`Testing cursorMove turn on move cursor 1`] = `<div />`;
+
+exports[`Testing cursorMove turn on move cursor 2`] = `
+<div
+  class="moving"
+/>
+`;
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
new file mode 100644
index 00000000..34dd2be2
--- /dev/null
+++ b/__tests__/editable.test.ts
@@ -0,0 +1,361 @@
+import { Operation } from '../src/circuit';
+import { exportedForTesting } from '../src/editable';
+import { RegisterType } from '../src/register';
+
+const {
+    getDataId,
+    splitDataId,
+    cursorMove,
+    cursorCopy,
+    deleteAt,
+    insertBefore,
+    insertAfter,
+    getDropzonePosition,
+    getWireElemText,
+} = exportedForTesting;
+
+// Utlities
+describe('Testing getDataId', () => {
+    const elem = document.createElement('div');
+    test('with with no data-id', () => {
+        expect(getDataId(elem)).toBe('');
+    });
+    test('with level 0 data-id', () => {
+        elem.setAttribute('data-id', '0');
+        expect(getDataId(elem)).toBe('0');
+    });
+
+    test('with level 1 data-id', () => {
+        elem.setAttribute('data-id', '0-1');
+        expect(getDataId(elem)).toBe('0-1');
+    });
+});
+
+describe('Testing splitDataId', () => {
+    test('with empty dataId', () => {
+        expect(splitDataId('')).toStrictEqual([]);
+    });
+    test('with level 0 data-id', () => {
+        expect(splitDataId('1')).toStrictEqual([1]);
+    });
+
+    test('with level 1 data-id', () => {
+        expect(splitDataId('1-2')).toStrictEqual([1, 2]);
+    });
+});
+
+describe('Testing cursorMove', () => {
+    const container = document.createElement('div');
+    test('turn on move cursor', () => {
+        expect(container).toMatchSnapshot();
+        cursorMove(container, true);
+        expect(container).toMatchSnapshot();
+    });
+    test('turn off move cursor', () => {
+        expect(container).toMatchSnapshot();
+        cursorMove(container, false);
+        expect(container).toMatchSnapshot();
+    });
+    test('turn on and off move cursor', () => {
+        expect(container).toMatchSnapshot();
+        cursorMove(container, true);
+        cursorMove(container, false);
+        expect(container).toMatchSnapshot();
+    });
+});
+
+describe('Testing cursorCopy', () => {
+    const container = document.createElement('div');
+    test('turn on copy cursor', () => {
+        expect(container).toMatchSnapshot();
+        cursorCopy(container, true);
+        expect(container).toMatchSnapshot();
+    });
+    test('turn off copy cursor', () => {
+        expect(container).toMatchSnapshot();
+        cursorCopy(container, false);
+        expect(container).toMatchSnapshot();
+    });
+    test('turn on and off copy cursor', () => {
+        expect(container).toMatchSnapshot();
+        cursorCopy(container, true);
+        cursorCopy(container, false);
+        expect(container).toMatchSnapshot();
+    });
+});
+
+describe('Testing deleteAt', () => {
+    const operations: Operation[] = [];
+    beforeEach(() => {
+        Object.assign(operations, [
+            {
+                gate: 'X',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Y',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Z',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ]);
+    });
+    test('delete X at index 0', () => {
+        deleteAt(operations, 0);
+        expect(operations).toStrictEqual([
+            {
+                gate: 'Y',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Z',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ]);
+    });
+    test('delete Y at index 1', () => {
+        deleteAt(operations, 1);
+        expect(operations).toStrictEqual([
+            {
+                gate: 'X',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Z',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ]);
+    });
+    test('delete Z and X at index 2, 0', () => {
+        deleteAt(operations, 2);
+        deleteAt(operations, 0);
+        expect(operations).toStrictEqual([
+            {
+                gate: 'Y',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ]);
+    });
+});
+
+describe('Testing insertBefore', () => {
+    test('insert before X', () => {
+        const operations = [
+            {
+                gate: 'X',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Z',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ];
+        const newGate = {
+            gate: 'Y',
+            isMeasurement: false,
+            isConditional: false,
+            isControlled: false,
+            isAdjoint: false,
+            controls: [],
+            targets: [{ type: RegisterType.Qubit, qId: 0 }],
+        };
+        insertBefore(operations, 0, newGate);
+        expect(operations).toStrictEqual([
+            {
+                gate: 'Y',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'X',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Z',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ]);
+    });
+});
+
+describe('Testing insertAfter', () => {
+    test('insert after X', () => {
+        const operations = [
+            {
+                gate: 'X',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Z',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ];
+        const newGate = {
+            gate: 'Y',
+            isMeasurement: false,
+            isConditional: false,
+            isControlled: false,
+            isAdjoint: false,
+            controls: [],
+            targets: [{ type: RegisterType.Qubit, qId: 0 }],
+        };
+        insertAfter(operations, 0, newGate);
+        expect(operations).toStrictEqual([
+            {
+                gate: 'X',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Y',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+            {
+                gate: 'Z',
+                isMeasurement: false,
+                isConditional: false,
+                isControlled: false,
+                isAdjoint: false,
+                controls: [],
+                targets: [{ type: RegisterType.Qubit, qId: 0 }],
+            },
+        ]);
+    });
+});
+
+describe('Testing getDropzonePosition', () => {
+    let svgElem: SVGElement;
+    let dropzoneElem: SVGElement;
+    beforeEach(() => {
+        svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGElement;
+        dropzoneElem = document.createElementNS('http://www.w3.org/2000/svg', 'rect') as SVGElement;
+        svgElem.append(dropzoneElem);
+    });
+    test('get position of non-dropzone', () => {
+        expect(() => getDropzonePosition(dropzoneElem)).toThrowError('Position not found');
+    });
+    test('get position of dropzone on the left', () => {
+        dropzoneElem.setAttribute('data-dropzone-position', 'left');
+        expect(getDropzonePosition(dropzoneElem)).toBe('left');
+    });
+    test('get position of dropzone on the right', () => {
+        dropzoneElem.setAttribute('data-dropzone-position', 'right');
+        expect(getDropzonePosition(dropzoneElem)).toBe('right');
+    });
+});
+
+describe('Testing getWireElementText', () => {
+    let svgElem: SVGElement;
+    let groupElem: SVGGElement;
+    let textElem: SVGGElement;
+    beforeEach(() => {
+        svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGElement;
+        groupElem = document.createElementNS('http://www.w3.org/2000/svg', 'g') as SVGGElement;
+        textElem = document.createElementNS('http://www.w3.org/2000/svg', 'text') as SVGTextElement;
+        groupElem.append(textElem);
+        svgElem.append(groupElem);
+    });
+    test('text element not exists', () => {
+        textElem.remove();
+        expect(() => getWireElemText(groupElem)).toThrowError('Text not found');
+    });
+    test('get text element without textContent', () => {
+        expect(() => getWireElemText(groupElem)).toThrowError('Text not found');
+    });
+    test('get text element empty textContent', () => {
+        expect(() => getWireElemText(groupElem)).toThrowError('Text not found');
+    });
+    test('should return q0', () => {
+        textElem.textContent = 'q0';
+        expect(getWireElemText(groupElem)).toEqual('q0');
+    });
+    test('should return q1', () => {
+        textElem.textContent = 'q1';
+        expect(getWireElemText(groupElem)).toEqual('q1');
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 9e900205..0cfa2adf 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -247,7 +247,7 @@ const getDataId = (element: Element): string => {
 };
 
 const splitDataId = (dataId: string): number[] => {
-    return dataId.split('-').map(Number);
+    return dataId === '' ? [] : dataId.split('-').map(Number);
 };
 
 const getWireElemsY = (container: HTMLElement): Wires => {
@@ -266,7 +266,8 @@ const getWireElemY = (wireElem: SVGGElement): number => {
 };
 const getWireElemText = (wireElem: SVGGElement): string => {
     const textElem = wireElem.querySelector<SVGTextElement>('text');
-    if (textElem == null || textElem.textContent == null) throw new Error('Text not found');
+    if (textElem == null || textElem.textContent == null || textElem.textContent === '')
+        throw new Error('Text not found');
     return textElem.textContent;
 };
 
@@ -309,4 +310,16 @@ const cursorCopy = (container: HTMLElement, value: boolean): void => {
     value ? container.classList.add('copying') : container.classList.remove('copying');
 };
 
-export { addEditable };
+const exportedForTesting = {
+    getDataId,
+    splitDataId,
+    cursorMove,
+    cursorCopy,
+    deleteAt,
+    insertBefore,
+    insertAfter,
+    getDropzonePosition,
+    getWireElemText,
+};
+
+export { addEditable, exportedForTesting };

From 7c3d7329d92f4e39d4440cd8048cade877a40fbf Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 8 Jun 2022 12:57:51 -0400
Subject: [PATCH 005/108] Revert renderCircuit to private

---
 src/sqore.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sqore.ts b/src/sqore.ts
index e0d85dfc..bfb63e98 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -107,7 +107,7 @@ export class Sqore {
      * @param container HTML element for rendering visualization into.
      * @param circuit Circuit object to be rendered.
      */
-    public renderCircuit(container: HTMLElement, circuit: Circuit, editable: boolean): void {
+    private renderCircuit(container: HTMLElement, circuit: Circuit, editable: boolean): void {
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);

From 2a6a82570d4d24e5adb74c8ca323de8e1c33e82c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 8 Jun 2022 13:34:45 -0400
Subject: [PATCH 006/108] Trim trailing whitespace

---
 src/editable.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editable.ts b/src/editable.ts
index 0cfa2adf..f3a217a6 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -54,7 +54,7 @@ const addCustomStyles = (container: HTMLElement): void => {
             }
             text {
                 user-select: none;
-            } 
+            }
             .copying {
                 cursor: copy;
             }

From 623a977ec61debf8346853f880c1b0eb62ab73bd Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 8 Jun 2022 16:55:06 -0400
Subject: [PATCH 007/108] Add license

---
 src/editable.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/editable.ts b/src/editable.ts
index f3a217a6..0b530672 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -1,3 +1,6 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
 import { Operation } from './circuit';
 import { leftPadding } from './constants';
 import { box } from './formatters/formatUtils';

From 6f560124d65bc2f8f1a8aa8a9bd112e68b063a25 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 8 Jun 2022 17:25:37 -0400
Subject: [PATCH 008/108] Fix + add tests to getWireElemY

---
 __tests__/editable.test.ts | 32 ++++++++++++++++++++++++++++++++
 src/editable.ts            |  6 ++++--
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 34dd2be2..d23f7a4d 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -12,6 +12,7 @@ const {
     insertAfter,
     getDropzonePosition,
     getWireElemText,
+    getWireElemY,
 } = exportedForTesting;
 
 // Utlities
@@ -359,3 +360,34 @@ describe('Testing getWireElementText', () => {
         expect(getWireElemText(groupElem)).toEqual('q1');
     });
 });
+
+describe('Testing getWireElemY', () => {
+    let svgElem: SVGElement;
+    let groupElem: SVGGElement;
+    let lineElem: SVGGElement;
+    beforeEach(() => {
+        svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGElement;
+        groupElem = document.createElementNS('http://www.w3.org/2000/svg', 'g') as SVGGElement;
+        lineElem = document.createElementNS('http://www.w3.org/2000/svg', 'line') as SVGLineElement;
+        groupElem.append(lineElem);
+        svgElem.append(groupElem);
+    });
+    test('line element not exists', () => {
+        lineElem.remove();
+        expect(() => getWireElemY(groupElem)).toThrowError('y not found');
+    });
+    test('get y element without y value', () => {
+        expect(() => getWireElemY(groupElem)).toThrowError('y not found');
+    });
+    test('get text element empty textContent', () => {
+        expect(() => getWireElemY(groupElem)).toThrowError('y not found');
+    });
+    test('should return 40', () => {
+        lineElem.setAttribute('y1', '40');
+        expect(getWireElemY(groupElem)).toEqual(40);
+    });
+    test('should return 99', () => {
+        lineElem.setAttribute('y1', '99');
+        expect(getWireElemY(groupElem)).toEqual(99);
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 0b530672..0a72b6e2 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -264,9 +264,10 @@ const getWireElemsY = (container: HTMLElement): Wires => {
 
 const getWireElemY = (wireElem: SVGGElement): number => {
     const lineElem = wireElem.querySelector<SVGLineElement>('line');
-    if (lineElem == null || lineElem.y1.baseVal.value == null) throw Error('y not found');
-    return lineElem.y1.baseVal.value;
+    if (lineElem == null || lineElem.getAttribute('y1') == null) throw Error('y not found');
+    return Number(lineElem.getAttribute('y1'));
 };
+
 const getWireElemText = (wireElem: SVGGElement): string => {
     const textElem = wireElem.querySelector<SVGTextElement>('text');
     if (textElem == null || textElem.textContent == null || textElem.textContent === '')
@@ -323,6 +324,7 @@ const exportedForTesting = {
     insertAfter,
     getDropzonePosition,
     getWireElemText,
+    getWireElemY,
 };
 
 export { addEditable, exportedForTesting };

From 2a06b9555bf4f5ccf55d45fed94f827000768493 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 8 Jun 2022 19:10:18 -0400
Subject: [PATCH 009/108] Add tests for getGate + getParent

---
 __tests__/editable.test.ts | 358 +++++++++++++++++++++++++++++++++++++
 src/editable.ts            |   2 +
 2 files changed, 360 insertions(+)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index d23f7a4d..46b0091f 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -13,6 +13,8 @@ const {
     getDropzonePosition,
     getWireElemText,
     getWireElemY,
+    getGate,
+    getParent,
 } = exportedForTesting;
 
 // Utlities
@@ -391,3 +393,359 @@ describe('Testing getWireElemY', () => {
         expect(getWireElemY(groupElem)).toEqual(99);
     });
 });
+
+describe('Testing getParent', () => {
+    test('with level 0 gate', () => {
+        const operations = [
+            {
+                gate: 'H',
+                targets: [{ qId: 0 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 0 }],
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'Measure',
+                isMeasurement: true,
+                controls: [{ qId: 1 }],
+                targets: [{ type: 1, qId: 1, cId: 0 }],
+            },
+        ];
+        expect(getParent('0', operations)).toStrictEqual([
+            {
+                gate: 'H',
+                targets: [{ qId: 0 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 0 }],
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'Measure',
+                isMeasurement: true,
+                controls: [{ qId: 1 }],
+                targets: [{ type: 1, qId: 1, cId: 0 }],
+            },
+        ]);
+    });
+    test('with level 1 gate', () => {
+        const operations = [
+            {
+                gate: 'Foo',
+                conditionalRender: 3,
+                targets: [{ qId: 0 }, { qId: 1 }],
+                children: [
+                    {
+                        gate: 'H',
+                        targets: [{ qId: 1 }],
+                    },
+                    {
+                        gate: 'RX',
+                        displayArgs: '(0.25)',
+                        isControlled: true,
+                        controls: [{ qId: 1 }],
+                        targets: [{ qId: 0 }],
+                    },
+                ],
+            },
+            {
+                gate: 'X',
+                targets: [{ qId: 3 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 1 }],
+                targets: [{ qId: 2 }, { qId: 3 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 2 }, { qId: 3 }],
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 1 }, { qId: 3 }],
+                targets: [{ qId: 2 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 2 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'measure',
+                isMeasurement: true,
+                controls: [{ qId: 0 }],
+                targets: [{ type: 1, qId: 0, cId: 0 }],
+            },
+            {
+                gate: 'ApplyIfElseR',
+                isConditional: true,
+                controls: [{ type: 1, qId: 0, cId: 0 }],
+                targets: [],
+                children: [
+                    {
+                        gate: 'H',
+                        targets: [{ qId: 1 }],
+                        conditionalRender: 1,
+                    },
+                    {
+                        gate: 'X',
+                        targets: [{ qId: 1 }],
+                        conditionalRender: 1,
+                    },
+                    {
+                        gate: 'X',
+                        isControlled: true,
+                        controls: [{ qId: 0 }],
+                        targets: [{ qId: 1 }],
+                        conditionalRender: 2,
+                    },
+                    {
+                        gate: 'Foo',
+                        targets: [{ qId: 3 }],
+                        conditionalRender: 2,
+                    },
+                ],
+            },
+            {
+                gate: 'SWAP',
+                targets: [{ qId: 0 }, { qId: 2 }],
+                children: [
+                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                    { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
+                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                ],
+            },
+            {
+                gate: 'ZZ',
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'ZZ',
+                targets: [{ qId: 0 }, { qId: 1 }],
+            },
+            {
+                gate: 'XX',
+                isControlled: true,
+                controls: [{ qId: 0 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'XX',
+                isControlled: true,
+                controls: [{ qId: 2 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'XX',
+                isControlled: true,
+                controls: [{ qId: 0 }, { qId: 2 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+        ];
+        expect(getParent('0-1', operations)).toStrictEqual([
+            {
+                gate: 'H',
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'RX',
+                displayArgs: '(0.25)',
+                isControlled: true,
+                controls: [{ qId: 1 }],
+                targets: [{ qId: 0 }],
+            },
+        ]);
+    });
+});
+
+describe('Testing getGate', () => {
+    test('should return H gate', () => {
+        const operations = [
+            {
+                gate: 'H',
+                targets: [{ qId: 0 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 0 }],
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'Measure',
+                isMeasurement: true,
+                controls: [{ qId: 1 }],
+                targets: [{ type: 1, qId: 1, cId: 0 }],
+            },
+        ];
+        expect(getGate('0', operations)).toStrictEqual({
+            gate: 'H',
+            targets: [{ qId: 0 }],
+        });
+    });
+    test('should return X gate', () => {
+        const operations = [
+            {
+                gate: 'H',
+                targets: [{ qId: 0 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 0 }],
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'Measure',
+                isMeasurement: true,
+                controls: [{ qId: 1 }],
+                targets: [{ type: 1, qId: 1, cId: 0 }],
+            },
+        ];
+        expect(getGate('1', operations)).toStrictEqual({
+            gate: 'X',
+            isControlled: true,
+            controls: [{ qId: 0 }],
+            targets: [{ qId: 1 }],
+        });
+    });
+    test('should return RX', () => {
+        const operations = [
+            {
+                gate: 'Foo',
+                conditionalRender: 3,
+                targets: [{ qId: 0 }, { qId: 1 }],
+                children: [
+                    {
+                        gate: 'H',
+                        targets: [{ qId: 1 }],
+                    },
+                    {
+                        gate: 'RX',
+                        displayArgs: '(0.25)',
+                        isControlled: true,
+                        controls: [{ qId: 1 }],
+                        targets: [{ qId: 0 }],
+                    },
+                ],
+            },
+            {
+                gate: 'X',
+                targets: [{ qId: 3 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 1 }],
+                targets: [{ qId: 2 }, { qId: 3 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 2 }, { qId: 3 }],
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 1 }, { qId: 3 }],
+                targets: [{ qId: 2 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 2 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'measure',
+                isMeasurement: true,
+                controls: [{ qId: 0 }],
+                targets: [{ type: 1, qId: 0, cId: 0 }],
+            },
+            {
+                gate: 'ApplyIfElseR',
+                isConditional: true,
+                controls: [{ type: 1, qId: 0, cId: 0 }],
+                targets: [],
+                children: [
+                    {
+                        gate: 'H',
+                        targets: [{ qId: 1 }],
+                        conditionalRender: 1,
+                    },
+                    {
+                        gate: 'X',
+                        targets: [{ qId: 1 }],
+                        conditionalRender: 1,
+                    },
+                    {
+                        gate: 'X',
+                        isControlled: true,
+                        controls: [{ qId: 0 }],
+                        targets: [{ qId: 1 }],
+                        conditionalRender: 2,
+                    },
+                    {
+                        gate: 'Foo',
+                        targets: [{ qId: 3 }],
+                        conditionalRender: 2,
+                    },
+                ],
+            },
+            {
+                gate: 'SWAP',
+                targets: [{ qId: 0 }, { qId: 2 }],
+                children: [
+                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                    { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
+                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                ],
+            },
+            {
+                gate: 'ZZ',
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'ZZ',
+                targets: [{ qId: 0 }, { qId: 1 }],
+            },
+            {
+                gate: 'XX',
+                isControlled: true,
+                controls: [{ qId: 0 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'XX',
+                isControlled: true,
+                controls: [{ qId: 2 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+            {
+                gate: 'XX',
+                isControlled: true,
+                controls: [{ qId: 0 }, { qId: 2 }],
+                targets: [{ qId: 1 }, { qId: 3 }],
+            },
+        ];
+        expect(getGate('0-1', operations)).toStrictEqual({
+            gate: 'RX',
+            displayArgs: '(0.25)',
+            isControlled: true,
+            controls: [{ qId: 1 }],
+            targets: [{ qId: 0 }],
+        });
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 0a72b6e2..8a55cd4a 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -325,6 +325,8 @@ const exportedForTesting = {
     getDropzonePosition,
     getWireElemText,
     getWireElemY,
+    getGate,
+    getParent,
 };
 
 export { addEditable, exportedForTesting };

From 3e7dd43769a57b74e529bba8dc59cc0f80d557c3 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 8 Jun 2022 19:35:35 -0400
Subject: [PATCH 010/108] Add test for addCustomStyles

---
 __tests__/__snapshots__/editable.test.ts.snap | 35 ++++++++++++++
 __tests__/editable.test.ts                    | 12 +++++
 src/editable.ts                               | 47 +++++++++----------
 3 files changed, 70 insertions(+), 24 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 637f61b6..7be5921b 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -1,5 +1,40 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`Testing addCustomStyles verify css 1`] = `
+<div>
+  <style />
+</div>
+`;
+
+exports[`Testing addCustomStyles verify css 2`] = `
+<div>
+  <style>
+    .dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
+    }
+  </style>
+</div>
+`;
+
 exports[`Testing cursorCopy turn off copy cursor 1`] = `
 <div
   class="copying"
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 46b0091f..6d00da2f 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -15,6 +15,7 @@ const {
     getWireElemY,
     getGate,
     getParent,
+    addCustomStyles,
 } = exportedForTesting;
 
 // Utlities
@@ -749,3 +750,14 @@ describe('Testing getGate', () => {
         });
     });
 });
+
+describe('Testing addCustomStyles', () => {
+    test('verify css', () => {
+        const container = document.createElement('div');
+        const style = document.createElement('style');
+        container.append(style);
+        expect(container).toMatchSnapshot();
+        addCustomStyles(container);
+        expect(container).toMatchSnapshot();
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 8a55cd4a..bcc1d683 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -46,30 +46,28 @@ const addEditable = (container: HTMLElement, sqore: Sqore): void => {
 const addCustomStyles = (container: HTMLElement): void => {
     const style = container.querySelector<HTMLStyleElement>('style');
     if (style) {
-        style.innerHTML += `
-            .dropzone {
-                fill: transparent;
-                stroke: transparent;
-            }
-            .dropzone:hover{
-                fill: red;
-                opacity: 25%;
-            }
-            text {
-                user-select: none;
-            }
-            .copying {
-                cursor: copy;
-            }
-            .moving {
-                cursor: move;
-            }
-            .detail-panel {
-                display: flex;
-                align-content: center;
-                gap: 12px;
-            }
-        `;
+        style.innerHTML += `.dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
+    }`;
     }
 };
 
@@ -327,6 +325,7 @@ const exportedForTesting = {
     getWireElemY,
     getGate,
     getParent,
+    addCustomStyles,
 };
 
 export { addEditable, exportedForTesting };

From b478d567ac1e2971cdc0d38ec8ee447ba57235c6 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 8 Jun 2022 19:41:00 -0400
Subject: [PATCH 011/108] Add test for addDocumentEvents

---
 __tests__/__snapshots__/editable.test.ts.snap |  4 ++++
 __tests__/editable.test.ts                    | 11 +++++++++++
 src/editable.ts                               |  1 +
 3 files changed, 16 insertions(+)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 7be5921b..9d3bfe1e 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -35,6 +35,10 @@ exports[`Testing addCustomStyles verify css 2`] = `
 </div>
 `;
 
+exports[`Testing addDocumentEvents verify document events 1`] = `<div />`;
+
+exports[`Testing addDocumentEvents verify document events 2`] = `<div />`;
+
 exports[`Testing cursorCopy turn off copy cursor 1`] = `
 <div
   class="copying"
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 6d00da2f..22e249a3 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -16,6 +16,7 @@ const {
     getGate,
     getParent,
     addCustomStyles,
+    addDocumentEvents,
 } = exportedForTesting;
 
 // Utlities
@@ -761,3 +762,13 @@ describe('Testing addCustomStyles', () => {
         expect(container).toMatchSnapshot();
     });
 });
+
+// Untestable
+describe('Testing addDocumentEvents', () => {
+    test('verify document events', () => {
+        const container = document.createElement('div');
+        expect(container).toMatchSnapshot();
+        addDocumentEvents(container);
+        expect(container).toMatchSnapshot();
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index bcc1d683..6b5d1871 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -326,6 +326,7 @@ const exportedForTesting = {
     getGate,
     getParent,
     addCustomStyles,
+    addDocumentEvents,
 };
 
 export { addEditable, exportedForTesting };

From 42fb70250ae4344d7948b2936e634ac671470fa9 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 10:04:00 -0400
Subject: [PATCH 012/108] Add more tests

---
 __tests__/__snapshots__/editable.test.ts.snap | 251 ++++++++++++++++++
 __tests__/editable.test.ts                    | 127 +++++++++
 src/editable.ts                               |  22 +-
 3 files changed, 395 insertions(+), 5 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 9d3bfe1e..0d888290 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -39,6 +39,54 @@ exports[`Testing addDocumentEvents verify document events 1`] = `<div />`;
 
 exports[`Testing addDocumentEvents verify document events 2`] = `<div />`;
 
+exports[`Testing createDropzone create dropzone on the left 1`] = `
+<rect
+  class="dropzone"
+  data-dropzone-position="left"
+  data-id="0"
+  height="20"
+  width="20"
+  x="0"
+  y="0"
+/>
+`;
+
+exports[`Testing createDropzone create dropzone on the right 1`] = `
+<rect
+  class="dropzone"
+  data-dropzone-position="right"
+  data-id="0"
+  height="20"
+  width="20"
+  x="0"
+  y="0"
+/>
+`;
+
+exports[`Testing createLeftDropzone create left dropzone 1`] = `
+<rect
+  class="dropzone"
+  data-dropzone-position="left"
+  data-id="0"
+  height="20"
+  width="10"
+  x="-10"
+  y="0"
+/>
+`;
+
+exports[`Testing createRightDropzone create dropzone right 1`] = `
+<rect
+  class="dropzone"
+  data-dropzone-position="right"
+  data-id="0"
+  height="20"
+  width="10"
+  x="20"
+  y="0"
+/>
+`;
+
 exports[`Testing cursorCopy turn off copy cursor 1`] = `
 <div
   class="copying"
@@ -102,3 +150,206 @@ exports[`Testing cursorMove turn on move cursor 2`] = `
   class="moving"
 />
 `;
+
+exports[`Testing getGateElems get 2 gates 1`] = `
+Array [
+  <g
+    class="gate"
+    data-id="0"
+    data-zoom-in="false"
+    data-zoom-out="false"
+  >
+    <g>
+      <g>
+        <rect
+          class="gate-unitary"
+          height="40"
+          width="40"
+          x="80"
+          y="20"
+        />
+        <text
+          font-size="14"
+          x="100"
+          y="40"
+        >
+          H
+        </text>
+      </g>
+    </g>
+  </g>,
+  <g
+    class="gate"
+    data-id="1"
+    data-zoom-in="false"
+    data-zoom-out="false"
+  >
+    <g>
+      <rect
+        class="gate-measure"
+        height="40"
+        width="40"
+        x="80"
+        y="80"
+      />
+      <path
+        class="arc-measure"
+        d="M 115 102 A 15 12 0 0 0 85 102"
+      />
+      <line
+        x1="100"
+        x2="112"
+        y1="108"
+        y2="88"
+      />
+    </g>
+  </g>,
+]
+`;
+
+exports[`Testing getGateElems get 3 gates 1`] = `
+Array [
+  <g
+    class="gate"
+    data-id="0"
+    data-zoom-in="false"
+    data-zoom-out="false"
+  >
+    <g>
+      <g>
+        <rect
+          class="gate-unitary"
+          height="40"
+          width="40"
+          x="80"
+          y="20"
+        />
+        <text
+          font-size="14"
+          x="100"
+          y="40"
+        >
+          H
+        </text>
+      </g>
+    </g>
+  </g>,
+  <g
+    class="gate"
+    data-id="1"
+    data-zoom-in="false"
+    data-zoom-out="false"
+  >
+    <line
+      x1="160"
+      x2="160"
+      y1="40"
+      y2="100"
+    />
+    <circle
+      class="control-dot"
+      cx="160"
+      cy="40"
+      r="5"
+    />
+    <g
+      class="oplus"
+    >
+      <circle
+        cx="160"
+        cy="100"
+        r="15"
+      />
+      <line
+        x1="160"
+        x2="160"
+        y1="85"
+        y2="115"
+      />
+      <line
+        x1="145"
+        x2="175"
+        y1="100"
+        y2="100"
+      />
+    </g>
+  </g>,
+  <g
+    class="gate"
+    data-id="2"
+    data-zoom-in="false"
+    data-zoom-out="false"
+  >
+    <g>
+      <rect
+        class="gate-measure"
+        height="40"
+        width="40"
+        x="200"
+        y="80"
+      />
+      <path
+        class="arc-measure"
+        d="M 235 102 A 15 12 0 0 0 205 102"
+      />
+      <line
+        x1="220"
+        x2="232"
+        y1="108"
+        y2="88"
+      />
+    </g>
+  </g>,
+]
+`;
+
+exports[`Testing getWireElems get 2 wires 1`] = `
+Array [
+  <g>
+    <line
+      x1="40"
+      x2="260"
+      y1="40"
+      y2="40"
+    />
+    <text
+      dominant-baseline="hanging"
+      font-size="75%"
+      text-anchor="start"
+      x="40"
+      y="24"
+    >
+      q0
+    </text>
+  </g>,
+  <g>
+    <line
+      x1="40"
+      x2="260"
+      y1="100"
+      y2="100"
+    />
+    <text
+      dominant-baseline="hanging"
+      font-size="75%"
+      text-anchor="start"
+      x="40"
+      y="84"
+    >
+      q1
+    </text>
+  </g>,
+]
+`;
+
+exports[`Testing handleGateMouseDown copying, ctrlKey is true 1`] = `
+<div
+  class="copying"
+/>
+`;
+
+exports[`Testing handleGateMouseDown moving, ctrlKey is false 1`] = `
+<div
+  class="moving"
+/>
+`;
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 22e249a3..f50dd1a0 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -1,6 +1,7 @@
 import { Operation } from '../src/circuit';
 import { exportedForTesting } from '../src/editable';
 import { RegisterType } from '../src/register';
+import { draw, STYLES } from '../src/index';
 
 const {
     getDataId,
@@ -17,6 +18,12 @@ const {
     getParent,
     addCustomStyles,
     addDocumentEvents,
+    handleGateMouseDown,
+    getGateElems,
+    getWireElems,
+    createDropzone,
+    createLeftDropzone,
+    createRightDropzone,
 } = exportedForTesting;
 
 // Utlities
@@ -772,3 +779,123 @@ describe('Testing addDocumentEvents', () => {
         expect(container).toMatchSnapshot();
     });
 });
+
+describe('Testing handleGateMouseDown', () => {
+    test('copying, ctrlKey is true', () => {
+        const container = document.createElement('div');
+        const ev = new MouseEvent('mousedown', { ctrlKey: true });
+        handleGateMouseDown(ev, container);
+        expect(container).toMatchSnapshot();
+    });
+    test('moving, ctrlKey is false', () => {
+        const container = document.createElement('div');
+        const ev = new MouseEvent('mousedown', { ctrlKey: false });
+        handleGateMouseDown(ev, container);
+        expect(container).toMatchSnapshot();
+    });
+});
+
+describe('Testing getGateElems', () => {
+    test('get 2 gates', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+        const gateElems = getGateElems(container);
+        expect(gateElems).toHaveLength(2);
+        expect(gateElems).toMatchSnapshot();
+    });
+    test('get 3 gates', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+        const gateElems = getGateElems(container);
+        expect(gateElems).toHaveLength(3);
+        expect(gateElems).toMatchSnapshot();
+    });
+});
+
+describe('Testing getWireElems', () => {
+    test('get 2 wires', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+        const wireElems = getWireElems(container);
+        expect(wireElems).toHaveLength(2);
+        expect(wireElems).toMatchSnapshot();
+    });
+});
+
+describe('Testing createDropzone', () => {
+    test('create dropzone on the left', () => {
+        expect(createDropzone(0, 0, 20, 20, '0', 'left')).toMatchSnapshot();
+    });
+    test('create dropzone on the right', () => {
+        expect(createDropzone(0, 0, 20, 20, '0', 'right')).toMatchSnapshot();
+    });
+});
+
+describe('Testing createLeftDropzone', () => {
+    test('create left dropzone', () => {
+        expect(createLeftDropzone(0, 0, 20, '0')).toMatchSnapshot();
+    });
+});
+
+describe('Testing createRightDropzone', () => {
+    test('create dropzone right', () => {
+        expect(createRightDropzone(0, 0, 20, 20, '0')).toMatchSnapshot();
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 6b5d1871..2b395cca 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -210,11 +210,17 @@ const createDropzone = (
     return dropzone;
 };
 
-const createLeftDropzone = (x: number, y: number, height: number, dataId: string): SVGElement => {
-    return createDropzone(x - leftPadding / 2, y, leftPadding / 2, height, dataId, 'left');
+const createLeftDropzone = (gateX: number, gateY: number, gateHeight: number, dataId: string): SVGElement => {
+    return createDropzone(gateX - leftPadding / 2, gateY, leftPadding / 2, gateHeight, dataId, 'left');
 };
-const createRightDropzone = (x: number, y: number, width: number, height: number, dataId: string): SVGElement => {
-    return createDropzone(x + width, y, leftPadding / 2, height, dataId, 'right');
+const createRightDropzone = (
+    gateX: number,
+    gateY: number,
+    gateWidth: number,
+    gateHeight: number,
+    dataId: string,
+): SVGElement => {
+    return createDropzone(gateX + gateWidth, gateY, leftPadding / 2, gateHeight, dataId, 'right');
 };
 
 // Operation getters
@@ -327,6 +333,12 @@ const exportedForTesting = {
     getParent,
     addCustomStyles,
     addDocumentEvents,
+    handleGateMouseDown,
+    getGateElems,
+    getWireElems,
+    createDropzone,
+    createLeftDropzone,
+    createRightDropzone,
 };
 
-export { addEditable, exportedForTesting };
+export { addEditable, exportedForTesting, getGateElems };

From be0685980bc658a86fa9813f73940abcf0bc6d5a Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 10:23:32 -0400
Subject: [PATCH 013/108] add tests for getClosestWireY

---
 __tests__/editable.test.ts | 85 ++++++++++++++++++++++++++++++++++++++
 src/editable.ts            | 44 +++++++++++---------
 2 files changed, 110 insertions(+), 19 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index f50dd1a0..7ea1e307 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -24,6 +24,7 @@ const {
     createDropzone,
     createLeftDropzone,
     createRightDropzone,
+    getClosestWireY,
 } = exportedForTesting;
 
 // Utlities
@@ -899,3 +900,87 @@ describe('Testing createRightDropzone', () => {
         expect(createRightDropzone(0, 0, 20, 20, '0')).toMatchSnapshot();
     });
 });
+
+describe('Testing getClosestWireY', () => {
+    test('should return 40', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        const wires = { '40': 'q0', '100': 'q1' };
+        draw(circuit, container, STYLES['default']);
+        expect(getClosestWireY(50, wires)).toEqual(40);
+    });
+    test('should return 100', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        const wires = { '40': 'q0', '100': 'q1' };
+        draw(circuit, container, STYLES['default']);
+        expect(getClosestWireY(85, wires)).toEqual(100);
+    });
+    test('should return null', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        const wires = { '40': 'q0', '100': 'q1' };
+        draw(circuit, container, STYLES['default']);
+        expect(getClosestWireY(120, wires)).toEqual(null);
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 2b395cca..6b07be81 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -280,16 +280,15 @@ const getWireElemText = (wireElem: SVGGElement): string => {
 };
 
 const getClosestWireY = (offsetY: number, wires: { [y: number]: string }): number | null => {
-    let wireY;
-    Object.entries(wires).forEach((wire) => {
+    for (const wire of Object.entries(wires)) {
         const y = wire[0];
         const distance = Math.abs(offsetY - Number(y));
-        // 15 is a magic number
-        if (distance < 15) {
-            wireY = y;
+        // 15 is an arbitrary number to determine the closeness of distance
+        if (distance <= 15) {
+            return Number(y);
         }
-    });
-    return wireY || null;
+    }
+    return null;
 };
 
 const getDropzonePosition = (element: SVGElement): string => {
@@ -319,26 +318,33 @@ const cursorCopy = (container: HTMLElement, value: boolean): void => {
 };
 
 const exportedForTesting = {
-    getDataId,
-    splitDataId,
-    cursorMove,
-    cursorCopy,
-    deleteAt,
-    insertBefore,
-    insertAfter,
-    getDropzonePosition,
-    getWireElemText,
-    getWireElemY,
-    getGate,
-    getParent,
+    // addEditable
     addCustomStyles,
+    // addDropzones
     addDocumentEvents,
+    // addDropzoneEvents
+    // addMouseEvents
     handleGateMouseDown,
+    // handleDropzoneMouseUp
     getGateElems,
     getWireElems,
     createDropzone,
     createLeftDropzone,
     createRightDropzone,
+    getParent,
+    getGate,
+    getDataId,
+    splitDataId,
+    // getWireElemsY
+    getWireElemY,
+    getWireElemText,
+    getClosestWireY,
+    getDropzonePosition,
+    insertBefore,
+    insertAfter,
+    deleteAt,
+    cursorMove,
+    cursorCopy,
 };
 
 export { addEditable, exportedForTesting, getGateElems };

From 41bbf6897745bf2f871c741a7b0d1c21f71d457a Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 10:47:08 -0400
Subject: [PATCH 014/108] Add tests for getWireElemsY + addDropzoneEvents

---
 __tests__/__snapshots__/editable.test.ts.snap |  26 ++
 __tests__/editable.test.ts                    | 224 ++++++++++++++++++
 src/editable.ts                               |   6 +-
 3 files changed, 253 insertions(+), 3 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 0d888290..34cc9398 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -39,6 +39,32 @@ exports[`Testing addDocumentEvents verify document events 1`] = `<div />`;
 
 exports[`Testing addDocumentEvents verify document events 2`] = `<div />`;
 
+exports[`Testing addDropzoneEvents add 1 event 1`] = `
+<div>
+  <svg>
+    <rect />
+  </svg>
+</div>
+`;
+
+exports[`Testing addDropzoneEvents add 1 event 2`] = `
+<div>
+  <svg>
+    <rect />
+    <rect />
+  </svg>
+</div>
+`;
+
+exports[`Testing addDropzoneEvents add 2 events 1`] = `
+<div>
+  <svg>
+    <rect />
+    <rect />
+  </svg>
+</div>
+`;
+
 exports[`Testing createDropzone create dropzone on the left 1`] = `
 <rect
   class="dropzone"
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 7ea1e307..2c09a384 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -25,6 +25,8 @@ const {
     createLeftDropzone,
     createRightDropzone,
     getClosestWireY,
+    getWireElemsY,
+    addDropzoneEvents,
 } = exportedForTesting;
 
 // Utlities
@@ -984,3 +986,225 @@ describe('Testing getClosestWireY', () => {
         expect(getClosestWireY(120, wires)).toEqual(null);
     });
 });
+
+describe('test getWireElemsY', () => {
+    test('get 2 wires', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        const expected = { '40': 'q0', '100': 'q1' };
+        draw(circuit, container, STYLES['default']);
+        expect(getWireElemsY(container)).toStrictEqual(expected);
+    });
+    test('get 4 wires', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
+            operations: [
+                {
+                    gate: 'Foo',
+                    conditionalRender: 3,
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                        },
+                        {
+                            gate: 'RX',
+                            displayArgs: '(0.25)',
+                            isControlled: true,
+                            controls: [{ qId: 1 }],
+                            targets: [{ qId: 0 }],
+                        },
+                    ],
+                },
+                {
+                    gate: 'X',
+                    targets: [{ qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ qId: 2 }, { qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }, { qId: 3 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }, { qId: 3 }],
+                    targets: [{ qId: 2 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ type: 1, qId: 0, cId: 0 }],
+                },
+                {
+                    gate: 'ApplyIfElseR',
+                    isConditional: true,
+                    controls: [{ type: 1, qId: 0, cId: 0 }],
+                    targets: [],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            isControlled: true,
+                            controls: [{ qId: 0 }],
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 2,
+                        },
+                        {
+                            gate: 'Foo',
+                            targets: [{ qId: 3 }],
+                            conditionalRender: 2,
+                        },
+                    ],
+                },
+                {
+                    gate: 'SWAP',
+                    targets: [{ qId: 0 }, { qId: 2 }],
+                    children: [
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                    ],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }, { qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+            ],
+        };
+        const expected = { '40': 'q0', '120': 'q1', '180': 'q2', '240': 'q3' };
+        draw(circuit, container, STYLES['default']);
+        expect(getWireElemsY(container)).toStrictEqual(expected);
+    });
+});
+
+describe('Testing addDropzoneEvents', () => {
+    interface Context {
+        container: HTMLElement;
+        operations: Operation[];
+        wires: Wires;
+        renderFn: () => void;
+    }
+
+    interface Wires {
+        [y: string]: string;
+    }
+
+    test('add 1 event', () => {
+        const container = document.createElement('div');
+        const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+        const dropzoneElem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+        svgElem.append(dropzoneElem);
+        container.append(svgElem);
+        interface Context {
+            container: HTMLElement;
+            operations: Operation[];
+            wires: Wires;
+            renderFn: () => void;
+        }
+
+        const context: Context = {
+            container: container,
+            operations: [],
+            wires: {},
+            renderFn: () => {
+                return;
+            },
+        };
+        addDropzoneEvents(context);
+        expect(container).toMatchSnapshot();
+    });
+    test('add 2 events', () => {
+        const container = document.createElement('div');
+        const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+        const dropzoneElem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+        const dropzoneElem1 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+        svgElem.append(dropzoneElem);
+        svgElem.append(dropzoneElem1);
+        container.append(svgElem);
+        interface Context {
+            container: HTMLElement;
+            operations: Operation[];
+            wires: Wires;
+            renderFn: () => void;
+        }
+
+        const context: Context = {
+            container: container,
+            operations: [],
+            wires: {},
+            renderFn: () => {
+                return;
+            },
+        };
+        addDropzoneEvents(context);
+        expect(container).toMatchSnapshot();
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 6b07be81..2e2a978f 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -322,7 +322,7 @@ const exportedForTesting = {
     addCustomStyles,
     // addDropzones
     addDocumentEvents,
-    // addDropzoneEvents
+    addDropzoneEvents,
     // addMouseEvents
     handleGateMouseDown,
     // handleDropzoneMouseUp
@@ -335,7 +335,7 @@ const exportedForTesting = {
     getGate,
     getDataId,
     splitDataId,
-    // getWireElemsY
+    getWireElemsY,
     getWireElemY,
     getWireElemText,
     getClosestWireY,
@@ -347,4 +347,4 @@ const exportedForTesting = {
     cursorCopy,
 };
 
-export { addEditable, exportedForTesting, getGateElems };
+export { addEditable, exportedForTesting };

From 6d9b5d58980a1aa3f0de42badb9beecc3ec2f7ec Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 10:54:07 -0400
Subject: [PATCH 015/108] Add tests for addMouseEvents

---
 __tests__/__snapshots__/editable.test.ts.snap | 282 +++++++++++++++++-
 __tests__/editable.test.ts                    |  79 +++--
 src/editable.ts                               |   2 +-
 3 files changed, 337 insertions(+), 26 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 34cc9398..00bc4da5 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -47,7 +47,7 @@ exports[`Testing addDropzoneEvents add 1 event 1`] = `
 </div>
 `;
 
-exports[`Testing addDropzoneEvents add 1 event 2`] = `
+exports[`Testing addDropzoneEvents add 2 events 1`] = `
 <div>
   <svg>
     <rect />
@@ -56,11 +56,283 @@ exports[`Testing addDropzoneEvents add 1 event 2`] = `
 </div>
 `;
 
-exports[`Testing addDropzoneEvents add 2 events 1`] = `
+exports[`Testing addMouseEvents verify mouse events 1`] = `
 <div>
-  <svg>
-    <rect />
-    <rect />
+  <svg
+    class="qviz"
+    height="180"
+    id="6252074d-6246-4fd9-8dc4-f4e412dc688c"
+    style="max-width: fit-content;"
+    width="140"
+  >
+    <style>
+      
+    line,
+    circle,
+    rect {
+        stroke: #000000;
+        stroke-width: 1;
+    }
+    text {
+        fill: #000000;
+        dominant-baseline: middle;
+        text-anchor: middle;
+        font-family: Arial;
+    }
+    .control-dot {
+        fill: #000000;
+    }
+    .oplus line, .oplus circle {
+        fill: #FFFFFF;
+        stroke-width: 2;
+    }
+    .gate-unitary {
+        fill: #D9F1FA;
+    }
+    .gate-measure {
+        fill: #FFDE86;
+    }
+    rect.gate-swap {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .arc-measure {
+        stroke: #000000;
+        fill: none;
+        stroke-width: 1;
+    }
+    .register-classical {
+        stroke-width: 0.5;
+    }
+    
+    .hidden {
+        display: none;
+    }
+    .classically-controlled-unknown {
+        opacity: 0.25;
+    }
+
+    
+    .classically-controlled-one .classical-container,
+    .classically-controlled-one .classical-line {
+        stroke: #4059BD;
+        stroke-width: 1.3;
+        fill: #4059BD;
+        fill-opacity: 0.1;
+    }
+    .classically-controlled-zero .classical-container,
+    .classically-controlled-zero .classical-line {
+        stroke: #C40000;
+        stroke-width: 1.3;
+        fill: #C40000;
+        fill-opacity: 0.1;
+    }
+    
+    .classically-controlled-btn {
+        cursor: pointer;
+    }
+    .classically-controlled-unknown .classically-controlled-btn {
+        fill: #E5E5E5;
+    }
+    .classically-controlled-one .classically-controlled-btn {
+        fill: #4059BD;
+    }
+    .classically-controlled-zero .classically-controlled-btn {
+        fill: #C40000;
+    }
+    
+    .classically-controlled-btn text {
+        dominant-baseline: middle;
+        text-anchor: middle;
+        stroke: none;
+        font-family: Arial;
+    }
+    .classically-controlled-unknown .classically-controlled-btn text {
+        fill: #000000;
+    }
+    .classically-controlled-one .classically-controlled-btn text {
+        fill: #FFFFFF;
+    }
+    .classically-controlled-zero .classically-controlled-btn text {
+        fill: #FFFFFF;
+    }
+    
+    .qviz .gate-collapse,
+    .qviz .gate-expand {
+        opacity: 0;
+        transition: opacity 1s;
+    }
+
+    .qviz:hover .gate-collapse,
+    .qviz:hover .gate-expand {
+        visibility: visible;
+        opacity: 0.2;
+        transition: visibility 1s;
+        transition: opacity 1s;
+    }
+
+    .gate-expand, .gate-collapse {
+        cursor: pointer;
+    }
+
+    .gate-collapse circle,
+    .gate-expand circle {
+        fill: white;
+        stroke-width: 2px;
+        stroke: black;
+    }
+    .gate-collapse path,
+    .gate-expand path {
+        stroke-width: 4px;
+        stroke: black;
+    }
+
+    .gate:hover &gt; .gate-collapse,
+    .gate:hover &gt; .gate-expand {
+        visibility: visible;
+        opacity: 1;
+        transition: opacity 1s;
+    }
+    </style>
+    <g>
+      <text
+        dominant-baseline="middle"
+        font-size="16"
+        text-anchor="start"
+        x="20"
+        y="40"
+      >
+        |0⟩
+      </text>
+      <text
+        dominant-baseline="middle"
+        font-size="16"
+        text-anchor="start"
+        x="20"
+        y="100"
+      >
+        |0⟩
+      </text>
+    </g>
+    <g>
+      <g>
+        <line
+          x1="40"
+          x2="140"
+          y1="40"
+          y2="40"
+        />
+        <text
+          dominant-baseline="hanging"
+          font-size="75%"
+          text-anchor="start"
+          x="40"
+          y="24"
+        >
+          q0
+        </text>
+      </g>
+      <g>
+        <line
+          x1="40"
+          x2="140"
+          y1="100"
+          y2="100"
+        />
+        <text
+          dominant-baseline="hanging"
+          font-size="75%"
+          text-anchor="start"
+          x="40"
+          y="84"
+        >
+          q1
+        </text>
+      </g>
+      <g>
+        <line
+          class="register-classical"
+          x1="101"
+          x2="101"
+          y1="100"
+          y2="139"
+        />
+        <line
+          class="register-classical"
+          x1="99"
+          x2="99"
+          y1="100"
+          y2="141"
+        />
+        <line
+          class="register-classical"
+          x1="101"
+          x2="140"
+          y1="139"
+          y2="139"
+        />
+        <line
+          class="register-classical"
+          x1="99"
+          x2="140"
+          y1="141"
+          y2="141"
+        />
+      </g>
+    </g>
+    <g>
+      <g
+        class="gate"
+        data-id="0"
+        data-zoom-in="false"
+        data-zoom-out="false"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="80"
+              y="20"
+            />
+            <text
+              font-size="14"
+              x="100"
+              y="40"
+            >
+              H
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+        data-id="1"
+        data-zoom-in="false"
+        data-zoom-out="false"
+      >
+        <g>
+          <rect
+            class="gate-measure"
+            height="40"
+            width="40"
+            x="80"
+            y="80"
+          />
+          <path
+            class="arc-measure"
+            d="M 115 102 A 15 12 0 0 0 85 102"
+          />
+          <line
+            x1="100"
+            x2="112"
+            y1="108"
+            y2="88"
+          />
+        </g>
+      </g>
+    </g>
   </svg>
 </div>
 `;
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 2c09a384..9d9af652 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -4,29 +4,33 @@ import { RegisterType } from '../src/register';
 import { draw, STYLES } from '../src/index';
 
 const {
-    getDataId,
-    splitDataId,
-    cursorMove,
-    cursorCopy,
-    deleteAt,
-    insertBefore,
-    insertAfter,
-    getDropzonePosition,
-    getWireElemText,
-    getWireElemY,
-    getGate,
-    getParent,
+    // addEditable
     addCustomStyles,
+    // addDropzones
     addDocumentEvents,
+    addDropzoneEvents,
+    addMouseEvents,
     handleGateMouseDown,
+    // handleDropzoneMouseUp
     getGateElems,
     getWireElems,
     createDropzone,
     createLeftDropzone,
     createRightDropzone,
-    getClosestWireY,
+    getParent,
+    getGate,
+    getDataId,
+    splitDataId,
     getWireElemsY,
-    addDropzoneEvents,
+    getWireElemY,
+    getWireElemText,
+    getClosestWireY,
+    getDropzonePosition,
+    insertBefore,
+    insertAfter,
+    deleteAt,
+    cursorMove,
+    cursorCopy,
 } = exportedForTesting;
 
 // Utlities
@@ -1163,12 +1167,6 @@ describe('Testing addDropzoneEvents', () => {
         const dropzoneElem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
         svgElem.append(dropzoneElem);
         container.append(svgElem);
-        interface Context {
-            container: HTMLElement;
-            operations: Operation[];
-            wires: Wires;
-            renderFn: () => void;
-        }
 
         const context: Context = {
             container: container,
@@ -1208,3 +1206,44 @@ describe('Testing addDropzoneEvents', () => {
         expect(container).toMatchSnapshot();
     });
 });
+
+describe('Testing addMouseEvents', () => {
+    interface Context {
+        container: HTMLElement;
+        operations: Operation[];
+        wires: Wires;
+        renderFn: () => void;
+    }
+    interface Wires {
+        [y: string]: string;
+    }
+    test('verify mouse events', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+        const context: Context = {
+            container: container,
+            operations: [],
+            wires: {},
+            renderFn: () => {
+                return;
+            },
+        };
+        addMouseEvents(context);
+        expect(container).toMatchSnapshot();
+    });
+});
diff --git a/src/editable.ts b/src/editable.ts
index 2e2a978f..687c799b 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -323,7 +323,7 @@ const exportedForTesting = {
     // addDropzones
     addDocumentEvents,
     addDropzoneEvents,
-    // addMouseEvents
+    addMouseEvents,
     handleGateMouseDown,
     // handleDropzoneMouseUp
     getGateElems,

From 692250c3039deaed4c927bba27c0d01cce660860 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 11:03:28 -0400
Subject: [PATCH 016/108] Remove svg id in snapshot

---
 __tests__/__snapshots__/editable.test.ts.snap | 1 -
 __tests__/editable.test.ts                    | 2 ++
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 00bc4da5..b6ae0cf6 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -61,7 +61,6 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
   <svg
     class="qviz"
     height="180"
-    id="6252074d-6246-4fd9-8dc4-f4e412dc688c"
     style="max-width: fit-content;"
     width="140"
   >
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 9d9af652..31c5cacb 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -1243,6 +1243,8 @@ describe('Testing addMouseEvents', () => {
                 return;
             },
         };
+        const svgElem = container.querySelector('svg');
+        if (svgElem != null) svgElem.removeAttribute('id');
         addMouseEvents(context);
         expect(container).toMatchSnapshot();
     });

From 0dc44fcd49f2194a811f08b56a0ea895f1ab56f2 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 11:12:03 -0400
Subject: [PATCH 017/108] Trim trailing whitespace

---
 __tests__/__snapshots__/editable.test.ts.snap | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index b6ae0cf6..51dcc84a 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -65,7 +65,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     width="140"
   >
     <style>
-      
+
     line,
     circle,
     rect {
@@ -103,7 +103,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     .register-classical {
         stroke-width: 0.5;
     }
-    
+
     .hidden {
         display: none;
     }
@@ -111,7 +111,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
         opacity: 0.25;
     }
 
-    
+
     .classically-controlled-one .classical-container,
     .classically-controlled-one .classical-line {
         stroke: #4059BD;
@@ -126,7 +126,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
         fill: #C40000;
         fill-opacity: 0.1;
     }
-    
+
     .classically-controlled-btn {
         cursor: pointer;
     }
@@ -139,7 +139,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     .classically-controlled-zero .classically-controlled-btn {
         fill: #C40000;
     }
-    
+
     .classically-controlled-btn text {
         dominant-baseline: middle;
         text-anchor: middle;
@@ -155,7 +155,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     .classically-controlled-zero .classically-controlled-btn text {
         fill: #FFFFFF;
     }
-    
+
     .qviz .gate-collapse,
     .qviz .gate-expand {
         opacity: 0;

From c397bfd59976f8debaaff67419176bfd1df72be6 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 11:20:28 -0400
Subject: [PATCH 018/108] Trim trailing whitespace

---
 __tests__/__snapshots__/editable.test.ts.snap | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 51dcc84a..b6ae0cf6 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -65,7 +65,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     width="140"
   >
     <style>
-
+      
     line,
     circle,
     rect {
@@ -103,7 +103,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     .register-classical {
         stroke-width: 0.5;
     }
-
+    
     .hidden {
         display: none;
     }
@@ -111,7 +111,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
         opacity: 0.25;
     }
 
-
+    
     .classically-controlled-one .classical-container,
     .classically-controlled-one .classical-line {
         stroke: #4059BD;
@@ -126,7 +126,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
         fill: #C40000;
         fill-opacity: 0.1;
     }
-
+    
     .classically-controlled-btn {
         cursor: pointer;
     }
@@ -139,7 +139,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     .classically-controlled-zero .classically-controlled-btn {
         fill: #C40000;
     }
-
+    
     .classically-controlled-btn text {
         dominant-baseline: middle;
         text-anchor: middle;
@@ -155,7 +155,7 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
     .classically-controlled-zero .classically-controlled-btn text {
         fill: #FFFFFF;
     }
-
+    
     .qviz .gate-collapse,
     .qviz .gate-expand {
         opacity: 0;

From e544eeec791f8a8024562b21a184beec0a00b852 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 14:43:57 -0400
Subject: [PATCH 019/108] Exclude __tests__/ from pre-commit hook

---
 .pre-commit-config.yaml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 53148a4a..e2e23075 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -39,3 +39,4 @@ repos:
         language: system
         types: [text]
         stages: [commit, push, manual]
+        exclude: ^__tests__/

From f0507427153204d69c6319aab1faf98997493c1a Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 19:31:21 -0400
Subject: [PATCH 020/108] Add callbackFn + documentation + tests

---
 __tests__/editable.test.ts |   47 +-
 example/script.js          |    2 +-
 pnpm-lock.yaml             | 6261 ++++++++++++++++++++++++++++++++++++
 src/editable.ts            |   18 +-
 src/index.ts               |    7 +-
 src/sqore.ts               |   43 +-
 6 files changed, 6359 insertions(+), 19 deletions(-)
 create mode 100644 pnpm-lock.yaml

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 31c5cacb..156439c7 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -1,7 +1,8 @@
-import { Operation } from '../src/circuit';
+import { Circuit, Operation } from '../src/circuit';
 import { exportedForTesting } from '../src/editable';
 import { RegisterType } from '../src/register';
 import { draw, STYLES } from '../src/index';
+import { Sqore } from '../src/sqore';
 
 const {
     // addEditable
@@ -19,6 +20,7 @@ const {
     createRightDropzone,
     getParent,
     getGate,
+    getRenderFn,
     getDataId,
     splitDataId,
     getWireElemsY,
@@ -1249,3 +1251,46 @@ describe('Testing addMouseEvents', () => {
         expect(container).toMatchSnapshot();
     });
 });
+
+describe('Testing getRenderFn', () => {
+    test('check console.log displaying "callbackFn is triggered"', () => {
+        Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
+            writable: true,
+            value: () => ({
+                x: 0,
+                y: 0,
+                width: 0,
+                height: 0,
+            }),
+        });
+        const container = document.createElement('div');
+        const circuit: Circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        const sqore = new Sqore(circuit, STYLES['default']);
+        const callbackFn = () => console.log('callbackFn is triggered');
+        const renderFn = getRenderFn(container, sqore, callbackFn);
+
+        jest.spyOn(console, 'log');
+        renderFn();
+        expect(console.log).toHaveBeenCalledWith('callbackFn is triggered');
+    });
+});
diff --git a/example/script.js b/example/script.js
index ae8c42c3..ef3ef492 100644
--- a/example/script.js
+++ b/example/script.js
@@ -12,7 +12,7 @@ if (typeof qviz != 'undefined') {
 
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
-        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, true);
+        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, true, () => console.log('callbackFn'));
     }
 
     const teleportDiv = document.getElementById('teleport');
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 00000000..fda0d629
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,6261 @@
+lockfileVersion: 5.4
+
+specifiers:
+  '@types/jest': ^26.0.4
+  '@types/prettier': 2.6.0
+  '@typescript-eslint/eslint-plugin': ^3.9.0
+  '@typescript-eslint/parser': ^3.9.0
+  canvas: ^2.6.1
+  eslint: ^7.6.0
+  eslint-config-prettier: ^6.11.0
+  eslint-plugin-jest: ^23.20.0
+  eslint-plugin-prettier: ^3.1.4
+  jest: ^26.6.3
+  prettier: 2.6.0
+  terser-webpack-plugin: ^4.1.0
+  ts-jest: ^26.1.2
+  ts-loader: ^8.0.2
+  typescript: ^3.9.6
+  webpack: ^4.46.0
+  webpack-cli: ^3.3.12
+
+devDependencies:
+  '@types/jest': 26.0.24
+  '@types/prettier': 2.6.0
+  '@typescript-eslint/eslint-plugin': 3.10.1_kxujzhw6vbtdri44htsqryf25e
+  '@typescript-eslint/parser': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
+  canvas: 2.9.1
+  eslint: 7.32.0
+  eslint-config-prettier: 6.15.0_eslint@7.32.0
+  eslint-plugin-jest: 23.20.0_2de3j2mqba4wgeuiaqz2k7syrm
+  eslint-plugin-prettier: 3.4.1_dnozcfbeb5wiozlftdrslmvmgu
+  jest: 26.6.3_canvas@2.9.1
+  prettier: 2.6.0
+  terser-webpack-plugin: 4.2.3_webpack@4.46.0
+  ts-jest: 26.5.6_eee4nuuc45mdmx5rmbx7hzjjtq
+  ts-loader: 8.4.0_ezrl6vhczkqqw35uifasf5iyga
+  typescript: 3.9.10
+  webpack: 4.46.0_webpack-cli@3.3.12
+  webpack-cli: 3.3.12_webpack@4.46.0
+
+packages:
+
+  /@ampproject/remapping/2.2.0:
+    resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/gen-mapping': 0.1.1
+      '@jridgewell/trace-mapping': 0.3.13
+    dev: true
+
+  /@babel/code-frame/7.12.11:
+    resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==}
+    dependencies:
+      '@babel/highlight': 7.17.12
+    dev: true
+
+  /@babel/code-frame/7.16.7:
+    resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/highlight': 7.17.12
+    dev: true
+
+  /@babel/compat-data/7.17.10:
+    resolution: {integrity: sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/core/7.18.2:
+    resolution: {integrity: sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@ampproject/remapping': 2.2.0
+      '@babel/code-frame': 7.16.7
+      '@babel/generator': 7.18.2
+      '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2
+      '@babel/helper-module-transforms': 7.18.0
+      '@babel/helpers': 7.18.2
+      '@babel/parser': 7.18.4
+      '@babel/template': 7.16.7
+      '@babel/traverse': 7.18.2
+      '@babel/types': 7.18.4
+      convert-source-map: 1.8.0
+      debug: 4.3.4
+      gensync: 1.0.0-beta.2
+      json5: 2.2.1
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/generator/7.18.2:
+    resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.18.4
+      '@jridgewell/gen-mapping': 0.3.1
+      jsesc: 2.5.2
+    dev: true
+
+  /@babel/helper-compilation-targets/7.18.2_@babel+core@7.18.2:
+    resolution: {integrity: sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/compat-data': 7.17.10
+      '@babel/core': 7.18.2
+      '@babel/helper-validator-option': 7.16.7
+      browserslist: 4.20.4
+      semver: 6.3.0
+    dev: true
+
+  /@babel/helper-environment-visitor/7.18.2:
+    resolution: {integrity: sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-function-name/7.17.9:
+    resolution: {integrity: sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.16.7
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@babel/helper-hoist-variables/7.16.7:
+    resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@babel/helper-module-imports/7.16.7:
+    resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@babel/helper-module-transforms/7.18.0:
+    resolution: {integrity: sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-environment-visitor': 7.18.2
+      '@babel/helper-module-imports': 7.16.7
+      '@babel/helper-simple-access': 7.18.2
+      '@babel/helper-split-export-declaration': 7.16.7
+      '@babel/helper-validator-identifier': 7.16.7
+      '@babel/template': 7.16.7
+      '@babel/traverse': 7.18.2
+      '@babel/types': 7.18.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-plugin-utils/7.17.12:
+    resolution: {integrity: sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-simple-access/7.18.2:
+    resolution: {integrity: sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@babel/helper-split-export-declaration/7.16.7:
+    resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@babel/helper-validator-identifier/7.16.7:
+    resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-validator-option/7.16.7:
+    resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helpers/7.18.2:
+    resolution: {integrity: sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.16.7
+      '@babel/traverse': 7.18.2
+      '@babel/types': 7.18.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/highlight/7.17.12:
+    resolution: {integrity: sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-validator-identifier': 7.16.7
+      chalk: 2.4.2
+      js-tokens: 4.0.0
+    dev: true
+
+  /@babel/parser/7.18.4:
+    resolution: {integrity: sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.18.2:
+    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.18.2:
+    resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.18.2:
+    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.18.2:
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.18.2:
+    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.18.2:
+    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.18.2:
+    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.18.2:
+    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.18.2:
+    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.18.2:
+    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.18.2:
+    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.18.2:
+    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/helper-plugin-utils': 7.17.12
+    dev: true
+
+  /@babel/template/7.16.7:
+    resolution: {integrity: sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.16.7
+      '@babel/parser': 7.18.4
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@babel/traverse/7.18.2:
+    resolution: {integrity: sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.16.7
+      '@babel/generator': 7.18.2
+      '@babel/helper-environment-visitor': 7.18.2
+      '@babel/helper-function-name': 7.17.9
+      '@babel/helper-hoist-variables': 7.16.7
+      '@babel/helper-split-export-declaration': 7.16.7
+      '@babel/parser': 7.18.4
+      '@babel/types': 7.18.4
+      debug: 4.3.4
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/types/7.18.4:
+    resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-validator-identifier': 7.16.7
+      to-fast-properties: 2.0.0
+    dev: true
+
+  /@bcoe/v8-coverage/0.2.3:
+    resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+    dev: true
+
+  /@cnakazawa/watch/1.0.4:
+    resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==}
+    engines: {node: '>=0.1.95'}
+    hasBin: true
+    dependencies:
+      exec-sh: 0.3.6
+      minimist: 1.2.6
+    dev: true
+
+  /@eslint/eslintrc/0.4.3:
+    resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.3.4
+      espree: 7.3.1
+      globals: 13.15.0
+      ignore: 4.0.6
+      import-fresh: 3.3.0
+      js-yaml: 3.14.1
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@gar/promisify/1.1.3:
+    resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
+    dev: true
+
+  /@humanwhocodes/config-array/0.5.0:
+    resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==}
+    engines: {node: '>=10.10.0'}
+    dependencies:
+      '@humanwhocodes/object-schema': 1.2.1
+      debug: 4.3.4
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@humanwhocodes/object-schema/1.2.1:
+    resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
+    dev: true
+
+  /@istanbuljs/load-nyc-config/1.1.0:
+    resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      camelcase: 5.3.1
+      find-up: 4.1.0
+      get-package-type: 0.1.0
+      js-yaml: 3.14.1
+      resolve-from: 5.0.0
+    dev: true
+
+  /@istanbuljs/schema/0.1.3:
+    resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /@jest/console/26.6.2:
+    resolution: {integrity: sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      chalk: 4.1.2
+      jest-message-util: 26.6.2
+      jest-util: 26.6.2
+      slash: 3.0.0
+    dev: true
+
+  /@jest/core/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/console': 26.6.2
+      '@jest/reporters': 26.6.2
+      '@jest/test-result': 26.6.2
+      '@jest/transform': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      exit: 0.1.2
+      graceful-fs: 4.2.10
+      jest-changed-files: 26.6.2
+      jest-config: 26.6.3_canvas@2.9.1
+      jest-haste-map: 26.6.2
+      jest-message-util: 26.6.2
+      jest-regex-util: 26.0.0
+      jest-resolve: 26.6.2
+      jest-resolve-dependencies: 26.6.3
+      jest-runner: 26.6.3_canvas@2.9.1
+      jest-runtime: 26.6.3_canvas@2.9.1
+      jest-snapshot: 26.6.2
+      jest-util: 26.6.2
+      jest-validate: 26.6.2
+      jest-watcher: 26.6.2
+      micromatch: 4.0.5
+      p-each-series: 2.2.0
+      rimraf: 3.0.2
+      slash: 3.0.0
+      strip-ansi: 6.0.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /@jest/environment/26.6.2:
+    resolution: {integrity: sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/fake-timers': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      jest-mock: 26.6.2
+    dev: true
+
+  /@jest/fake-timers/26.6.2:
+    resolution: {integrity: sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      '@sinonjs/fake-timers': 6.0.1
+      '@types/node': 17.0.41
+      jest-message-util: 26.6.2
+      jest-mock: 26.6.2
+      jest-util: 26.6.2
+    dev: true
+
+  /@jest/globals/26.6.2:
+    resolution: {integrity: sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/environment': 26.6.2
+      '@jest/types': 26.6.2
+      expect: 26.6.2
+    dev: true
+
+  /@jest/reporters/26.6.2:
+    resolution: {integrity: sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@bcoe/v8-coverage': 0.2.3
+      '@jest/console': 26.6.2
+      '@jest/test-result': 26.6.2
+      '@jest/transform': 26.6.2
+      '@jest/types': 26.6.2
+      chalk: 4.1.2
+      collect-v8-coverage: 1.0.1
+      exit: 0.1.2
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      istanbul-lib-coverage: 3.2.0
+      istanbul-lib-instrument: 4.0.3
+      istanbul-lib-report: 3.0.0
+      istanbul-lib-source-maps: 4.0.1
+      istanbul-reports: 3.1.4
+      jest-haste-map: 26.6.2
+      jest-resolve: 26.6.2
+      jest-util: 26.6.2
+      jest-worker: 26.6.2
+      slash: 3.0.0
+      source-map: 0.6.1
+      string-length: 4.0.2
+      terminal-link: 2.1.1
+      v8-to-istanbul: 7.1.2
+    optionalDependencies:
+      node-notifier: 8.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@jest/source-map/26.6.2:
+    resolution: {integrity: sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      callsites: 3.1.0
+      graceful-fs: 4.2.10
+      source-map: 0.6.1
+    dev: true
+
+  /@jest/test-result/26.6.2:
+    resolution: {integrity: sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/console': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/istanbul-lib-coverage': 2.0.4
+      collect-v8-coverage: 1.0.1
+    dev: true
+
+  /@jest/test-sequencer/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/test-result': 26.6.2
+      graceful-fs: 4.2.10
+      jest-haste-map: 26.6.2
+      jest-runner: 26.6.3_canvas@2.9.1
+      jest-runtime: 26.6.3_canvas@2.9.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /@jest/transform/26.6.2:
+    resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@babel/core': 7.18.2
+      '@jest/types': 26.6.2
+      babel-plugin-istanbul: 6.1.1
+      chalk: 4.1.2
+      convert-source-map: 1.8.0
+      fast-json-stable-stringify: 2.1.0
+      graceful-fs: 4.2.10
+      jest-haste-map: 26.6.2
+      jest-regex-util: 26.0.0
+      jest-util: 26.6.2
+      micromatch: 4.0.5
+      pirates: 4.0.5
+      slash: 3.0.0
+      source-map: 0.6.1
+      write-file-atomic: 3.0.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@jest/types/26.6.2:
+    resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.4
+      '@types/istanbul-reports': 3.0.1
+      '@types/node': 17.0.41
+      '@types/yargs': 15.0.14
+      chalk: 4.1.2
+    dev: true
+
+  /@jridgewell/gen-mapping/0.1.1:
+    resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.1.1
+      '@jridgewell/sourcemap-codec': 1.4.13
+    dev: true
+
+  /@jridgewell/gen-mapping/0.3.1:
+    resolution: {integrity: sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.1.1
+      '@jridgewell/sourcemap-codec': 1.4.13
+      '@jridgewell/trace-mapping': 0.3.13
+    dev: true
+
+  /@jridgewell/resolve-uri/3.0.7:
+    resolution: {integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
+  /@jridgewell/set-array/1.1.1:
+    resolution: {integrity: sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
+  /@jridgewell/source-map/0.3.2:
+    resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.1
+      '@jridgewell/trace-mapping': 0.3.13
+    dev: true
+
+  /@jridgewell/sourcemap-codec/1.4.13:
+    resolution: {integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==}
+    dev: true
+
+  /@jridgewell/trace-mapping/0.3.13:
+    resolution: {integrity: sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==}
+    dependencies:
+      '@jridgewell/resolve-uri': 3.0.7
+      '@jridgewell/sourcemap-codec': 1.4.13
+    dev: true
+
+  /@mapbox/node-pre-gyp/1.0.9:
+    resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
+    hasBin: true
+    dependencies:
+      detect-libc: 2.0.1
+      https-proxy-agent: 5.0.1
+      make-dir: 3.1.0
+      node-fetch: 2.6.7
+      nopt: 5.0.0
+      npmlog: 5.0.1
+      rimraf: 3.0.2
+      semver: 7.3.7
+      tar: 6.1.11
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: true
+
+  /@npmcli/fs/1.1.1:
+    resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==}
+    dependencies:
+      '@gar/promisify': 1.1.3
+      semver: 7.3.7
+    dev: true
+
+  /@npmcli/move-file/1.1.2:
+    resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
+    engines: {node: '>=10'}
+    dependencies:
+      mkdirp: 1.0.4
+      rimraf: 3.0.2
+    dev: true
+
+  /@sinonjs/commons/1.8.3:
+    resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==}
+    dependencies:
+      type-detect: 4.0.8
+    dev: true
+
+  /@sinonjs/fake-timers/6.0.1:
+    resolution: {integrity: sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==}
+    dependencies:
+      '@sinonjs/commons': 1.8.3
+    dev: true
+
+  /@tootallnate/once/1.1.2:
+    resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /@types/babel__core/7.1.19:
+    resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==}
+    dependencies:
+      '@babel/parser': 7.18.4
+      '@babel/types': 7.18.4
+      '@types/babel__generator': 7.6.4
+      '@types/babel__template': 7.4.1
+      '@types/babel__traverse': 7.17.1
+    dev: true
+
+  /@types/babel__generator/7.6.4:
+    resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
+    dependencies:
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@types/babel__template/7.4.1:
+    resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
+    dependencies:
+      '@babel/parser': 7.18.4
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@types/babel__traverse/7.17.1:
+    resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==}
+    dependencies:
+      '@babel/types': 7.18.4
+    dev: true
+
+  /@types/eslint-visitor-keys/1.0.0:
+    resolution: {integrity: sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==}
+    dev: true
+
+  /@types/graceful-fs/4.1.5:
+    resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==}
+    dependencies:
+      '@types/node': 17.0.41
+    dev: true
+
+  /@types/istanbul-lib-coverage/2.0.4:
+    resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
+    dev: true
+
+  /@types/istanbul-lib-report/3.0.0:
+    resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==}
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.4
+    dev: true
+
+  /@types/istanbul-reports/3.0.1:
+    resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==}
+    dependencies:
+      '@types/istanbul-lib-report': 3.0.0
+    dev: true
+
+  /@types/jest/26.0.24:
+    resolution: {integrity: sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==}
+    dependencies:
+      jest-diff: 26.6.2
+      pretty-format: 26.6.2
+    dev: true
+
+  /@types/json-schema/7.0.11:
+    resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
+    dev: true
+
+  /@types/node/17.0.41:
+    resolution: {integrity: sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==}
+    dev: true
+
+  /@types/normalize-package-data/2.4.1:
+    resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
+    dev: true
+
+  /@types/prettier/2.6.0:
+    resolution: {integrity: sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw==}
+    dev: true
+
+  /@types/stack-utils/2.0.1:
+    resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
+    dev: true
+
+  /@types/yargs-parser/21.0.0:
+    resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
+    dev: true
+
+  /@types/yargs/15.0.14:
+    resolution: {integrity: sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==}
+    dependencies:
+      '@types/yargs-parser': 21.0.0
+    dev: true
+
+  /@typescript-eslint/eslint-plugin/3.10.1_kxujzhw6vbtdri44htsqryf25e:
+    resolution: {integrity: sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^3.0.0
+      eslint: ^5.0.0 || ^6.0.0 || ^7.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/experimental-utils': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
+      '@typescript-eslint/parser': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
+      debug: 4.3.4
+      eslint: 7.32.0
+      functional-red-black-tree: 1.0.1
+      regexpp: 3.2.0
+      semver: 7.3.7
+      tsutils: 3.21.0_typescript@3.9.10
+      typescript: 3.9.10
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/experimental-utils/2.34.0_2de3j2mqba4wgeuiaqz2k7syrm:
+    resolution: {integrity: sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==}
+    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
+    peerDependencies:
+      eslint: '*'
+    dependencies:
+      '@types/json-schema': 7.0.11
+      '@typescript-eslint/typescript-estree': 2.34.0_typescript@3.9.10
+      eslint: 7.32.0
+      eslint-scope: 5.1.1
+      eslint-utils: 2.1.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+    dev: true
+
+  /@typescript-eslint/experimental-utils/3.10.1_2de3j2mqba4wgeuiaqz2k7syrm:
+    resolution: {integrity: sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    peerDependencies:
+      eslint: '*'
+    dependencies:
+      '@types/json-schema': 7.0.11
+      '@typescript-eslint/types': 3.10.1
+      '@typescript-eslint/typescript-estree': 3.10.1_typescript@3.9.10
+      eslint: 7.32.0
+      eslint-scope: 5.1.1
+      eslint-utils: 2.1.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+    dev: true
+
+  /@typescript-eslint/parser/3.10.1_2de3j2mqba4wgeuiaqz2k7syrm:
+    resolution: {integrity: sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    peerDependencies:
+      eslint: ^5.0.0 || ^6.0.0 || ^7.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@types/eslint-visitor-keys': 1.0.0
+      '@typescript-eslint/experimental-utils': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
+      '@typescript-eslint/types': 3.10.1
+      '@typescript-eslint/typescript-estree': 3.10.1_typescript@3.9.10
+      eslint: 7.32.0
+      eslint-visitor-keys: 1.3.0
+      typescript: 3.9.10
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/types/3.10.1:
+    resolution: {integrity: sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==}
+    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
+    dev: true
+
+  /@typescript-eslint/typescript-estree/2.34.0_typescript@3.9.10:
+    resolution: {integrity: sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==}
+    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      debug: 4.3.4
+      eslint-visitor-keys: 1.3.0
+      glob: 7.2.3
+      is-glob: 4.0.3
+      lodash: 4.17.21
+      semver: 7.3.7
+      tsutils: 3.21.0_typescript@3.9.10
+      typescript: 3.9.10
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/typescript-estree/3.10.1_typescript@3.9.10:
+    resolution: {integrity: sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/types': 3.10.1
+      '@typescript-eslint/visitor-keys': 3.10.1
+      debug: 4.3.4
+      glob: 7.2.3
+      is-glob: 4.0.3
+      lodash: 4.17.21
+      semver: 7.3.7
+      tsutils: 3.21.0_typescript@3.9.10
+      typescript: 3.9.10
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/visitor-keys/3.10.1:
+    resolution: {integrity: sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==}
+    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
+    dependencies:
+      eslint-visitor-keys: 1.3.0
+    dev: true
+
+  /@webassemblyjs/ast/1.9.0:
+    resolution: {integrity: sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==}
+    dependencies:
+      '@webassemblyjs/helper-module-context': 1.9.0
+      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
+      '@webassemblyjs/wast-parser': 1.9.0
+    dev: true
+
+  /@webassemblyjs/floating-point-hex-parser/1.9.0:
+    resolution: {integrity: sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==}
+    dev: true
+
+  /@webassemblyjs/helper-api-error/1.9.0:
+    resolution: {integrity: sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==}
+    dev: true
+
+  /@webassemblyjs/helper-buffer/1.9.0:
+    resolution: {integrity: sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==}
+    dev: true
+
+  /@webassemblyjs/helper-code-frame/1.9.0:
+    resolution: {integrity: sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==}
+    dependencies:
+      '@webassemblyjs/wast-printer': 1.9.0
+    dev: true
+
+  /@webassemblyjs/helper-fsm/1.9.0:
+    resolution: {integrity: sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==}
+    dev: true
+
+  /@webassemblyjs/helper-module-context/1.9.0:
+    resolution: {integrity: sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+    dev: true
+
+  /@webassemblyjs/helper-wasm-bytecode/1.9.0:
+    resolution: {integrity: sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==}
+    dev: true
+
+  /@webassemblyjs/helper-wasm-section/1.9.0:
+    resolution: {integrity: sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/helper-buffer': 1.9.0
+      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
+      '@webassemblyjs/wasm-gen': 1.9.0
+    dev: true
+
+  /@webassemblyjs/ieee754/1.9.0:
+    resolution: {integrity: sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==}
+    dependencies:
+      '@xtuc/ieee754': 1.2.0
+    dev: true
+
+  /@webassemblyjs/leb128/1.9.0:
+    resolution: {integrity: sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==}
+    dependencies:
+      '@xtuc/long': 4.2.2
+    dev: true
+
+  /@webassemblyjs/utf8/1.9.0:
+    resolution: {integrity: sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==}
+    dev: true
+
+  /@webassemblyjs/wasm-edit/1.9.0:
+    resolution: {integrity: sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/helper-buffer': 1.9.0
+      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
+      '@webassemblyjs/helper-wasm-section': 1.9.0
+      '@webassemblyjs/wasm-gen': 1.9.0
+      '@webassemblyjs/wasm-opt': 1.9.0
+      '@webassemblyjs/wasm-parser': 1.9.0
+      '@webassemblyjs/wast-printer': 1.9.0
+    dev: true
+
+  /@webassemblyjs/wasm-gen/1.9.0:
+    resolution: {integrity: sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
+      '@webassemblyjs/ieee754': 1.9.0
+      '@webassemblyjs/leb128': 1.9.0
+      '@webassemblyjs/utf8': 1.9.0
+    dev: true
+
+  /@webassemblyjs/wasm-opt/1.9.0:
+    resolution: {integrity: sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/helper-buffer': 1.9.0
+      '@webassemblyjs/wasm-gen': 1.9.0
+      '@webassemblyjs/wasm-parser': 1.9.0
+    dev: true
+
+  /@webassemblyjs/wasm-parser/1.9.0:
+    resolution: {integrity: sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/helper-api-error': 1.9.0
+      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
+      '@webassemblyjs/ieee754': 1.9.0
+      '@webassemblyjs/leb128': 1.9.0
+      '@webassemblyjs/utf8': 1.9.0
+    dev: true
+
+  /@webassemblyjs/wast-parser/1.9.0:
+    resolution: {integrity: sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/floating-point-hex-parser': 1.9.0
+      '@webassemblyjs/helper-api-error': 1.9.0
+      '@webassemblyjs/helper-code-frame': 1.9.0
+      '@webassemblyjs/helper-fsm': 1.9.0
+      '@xtuc/long': 4.2.2
+    dev: true
+
+  /@webassemblyjs/wast-printer/1.9.0:
+    resolution: {integrity: sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==}
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/wast-parser': 1.9.0
+      '@xtuc/long': 4.2.2
+    dev: true
+
+  /@xtuc/ieee754/1.2.0:
+    resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
+    dev: true
+
+  /@xtuc/long/4.2.2:
+    resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
+    dev: true
+
+  /abab/2.0.6:
+    resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
+    dev: true
+
+  /abbrev/1.1.1:
+    resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
+    dev: true
+
+  /acorn-globals/6.0.0:
+    resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
+    dependencies:
+      acorn: 7.4.1
+      acorn-walk: 7.2.0
+    dev: true
+
+  /acorn-jsx/5.3.2_acorn@7.4.1:
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      acorn: 7.4.1
+    dev: true
+
+  /acorn-walk/7.2.0:
+    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
+    engines: {node: '>=0.4.0'}
+    dev: true
+
+  /acorn/6.4.2:
+    resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /acorn/7.4.1:
+    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /acorn/8.7.1:
+    resolution: {integrity: sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /agent-base/6.0.2:
+    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+    engines: {node: '>= 6.0.0'}
+    dependencies:
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /aggregate-error/3.1.0:
+    resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
+    engines: {node: '>=8'}
+    dependencies:
+      clean-stack: 2.2.0
+      indent-string: 4.0.0
+    dev: true
+
+  /ajv-errors/1.0.1_ajv@6.12.6:
+    resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==}
+    peerDependencies:
+      ajv: '>=5.0.0'
+    dependencies:
+      ajv: 6.12.6
+    dev: true
+
+  /ajv-keywords/3.5.2_ajv@6.12.6:
+    resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
+    peerDependencies:
+      ajv: ^6.9.1
+    dependencies:
+      ajv: 6.12.6
+    dev: true
+
+  /ajv/6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+    dev: true
+
+  /ajv/8.11.0:
+    resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      json-schema-traverse: 1.0.0
+      require-from-string: 2.0.2
+      uri-js: 4.4.1
+    dev: true
+
+  /ansi-colors/4.1.3:
+    resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /ansi-escapes/4.3.2:
+    resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      type-fest: 0.21.3
+    dev: true
+
+  /ansi-regex/4.1.1:
+    resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /ansi-regex/5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ansi-styles/3.2.1:
+    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+    engines: {node: '>=4'}
+    dependencies:
+      color-convert: 1.9.3
+    dev: true
+
+  /ansi-styles/4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: true
+
+  /anymatch/2.0.0:
+    resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==}
+    dependencies:
+      micromatch: 3.1.10
+      normalize-path: 2.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /anymatch/3.1.2:
+    resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+    dev: true
+
+  /aproba/1.2.0:
+    resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
+    dev: true
+
+  /aproba/2.0.0:
+    resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
+    dev: true
+
+  /are-we-there-yet/2.0.0:
+    resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
+    engines: {node: '>=10'}
+    dependencies:
+      delegates: 1.0.0
+      readable-stream: 3.6.0
+    dev: true
+
+  /argparse/1.0.10:
+    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+    dependencies:
+      sprintf-js: 1.0.3
+    dev: true
+
+  /arr-diff/4.0.0:
+    resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /arr-flatten/1.1.0:
+    resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /arr-union/3.1.0:
+    resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /array-unique/0.3.2:
+    resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /asn1.js/5.4.1:
+    resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
+    dependencies:
+      bn.js: 4.12.0
+      inherits: 2.0.4
+      minimalistic-assert: 1.0.1
+      safer-buffer: 2.1.2
+    dev: true
+
+  /assert/1.5.0:
+    resolution: {integrity: sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==}
+    dependencies:
+      object-assign: 4.1.1
+      util: 0.10.3
+    dev: true
+
+  /assign-symbols/1.0.0:
+    resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /astral-regex/2.0.0:
+    resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /async-each/1.0.3:
+    resolution: {integrity: sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==}
+    dev: true
+    optional: true
+
+  /asynckit/0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+    dev: true
+
+  /atob/2.1.2:
+    resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
+    engines: {node: '>= 4.5.0'}
+    hasBin: true
+    dev: true
+
+  /babel-jest/26.6.3_@babel+core@7.18.2:
+    resolution: {integrity: sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==}
+    engines: {node: '>= 10.14.2'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@jest/transform': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/babel__core': 7.1.19
+      babel-plugin-istanbul: 6.1.1
+      babel-preset-jest: 26.6.2_@babel+core@7.18.2
+      chalk: 4.1.2
+      graceful-fs: 4.2.10
+      slash: 3.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /babel-plugin-istanbul/6.1.1:
+    resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@babel/helper-plugin-utils': 7.17.12
+      '@istanbuljs/load-nyc-config': 1.1.0
+      '@istanbuljs/schema': 0.1.3
+      istanbul-lib-instrument: 5.2.0
+      test-exclude: 6.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /babel-plugin-jest-hoist/26.6.2:
+    resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@babel/template': 7.16.7
+      '@babel/types': 7.18.4
+      '@types/babel__core': 7.1.19
+      '@types/babel__traverse': 7.17.1
+    dev: true
+
+  /babel-preset-current-node-syntax/1.0.1_@babel+core@7.18.2:
+    resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.18.2
+      '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.18.2
+      '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.18.2
+      '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.18.2
+      '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.18.2
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.18.2
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.18.2
+      '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.18.2
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.18.2
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.18.2
+      '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.18.2
+      '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.18.2
+    dev: true
+
+  /babel-preset-jest/26.6.2_@babel+core@7.18.2:
+    resolution: {integrity: sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==}
+    engines: {node: '>= 10.14.2'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.18.2
+      babel-plugin-jest-hoist: 26.6.2
+      babel-preset-current-node-syntax: 1.0.1_@babel+core@7.18.2
+    dev: true
+
+  /balanced-match/1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+    dev: true
+
+  /base/0.11.2:
+    resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      cache-base: 1.0.1
+      class-utils: 0.3.6
+      component-emitter: 1.3.0
+      define-property: 1.0.0
+      isobject: 3.0.1
+      mixin-deep: 1.3.2
+      pascalcase: 0.1.1
+    dev: true
+
+  /base64-js/1.5.1:
+    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+    dev: true
+
+  /big.js/5.2.2:
+    resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
+    dev: true
+
+  /binary-extensions/1.13.1:
+    resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+    optional: true
+
+  /binary-extensions/2.2.0:
+    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+    engines: {node: '>=8'}
+    dev: true
+    optional: true
+
+  /bindings/1.5.0:
+    resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
+    dependencies:
+      file-uri-to-path: 1.0.0
+    dev: true
+    optional: true
+
+  /bluebird/3.7.2:
+    resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
+    dev: true
+
+  /bn.js/4.12.0:
+    resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
+    dev: true
+
+  /bn.js/5.2.1:
+    resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==}
+    dev: true
+
+  /brace-expansion/1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+    dev: true
+
+  /braces/2.3.2:
+    resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-flatten: 1.1.0
+      array-unique: 0.3.2
+      extend-shallow: 2.0.1
+      fill-range: 4.0.0
+      isobject: 3.0.1
+      repeat-element: 1.1.4
+      snapdragon: 0.8.2
+      snapdragon-node: 2.1.1
+      split-string: 3.1.0
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /braces/2.3.2_supports-color@6.1.0:
+    resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-flatten: 1.1.0
+      array-unique: 0.3.2
+      extend-shallow: 2.0.1
+      fill-range: 4.0.0
+      isobject: 3.0.1
+      repeat-element: 1.1.4
+      snapdragon: 0.8.2_supports-color@6.1.0
+      snapdragon-node: 2.1.1
+      split-string: 3.1.0
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /braces/3.0.2:
+    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+    engines: {node: '>=8'}
+    dependencies:
+      fill-range: 7.0.1
+    dev: true
+
+  /brorand/1.1.0:
+    resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==}
+    dev: true
+
+  /browser-process-hrtime/1.0.0:
+    resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
+    dev: true
+
+  /browserify-aes/1.2.0:
+    resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==}
+    dependencies:
+      buffer-xor: 1.0.3
+      cipher-base: 1.0.4
+      create-hash: 1.2.0
+      evp_bytestokey: 1.0.3
+      inherits: 2.0.4
+      safe-buffer: 5.2.1
+    dev: true
+
+  /browserify-cipher/1.0.1:
+    resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==}
+    dependencies:
+      browserify-aes: 1.2.0
+      browserify-des: 1.0.2
+      evp_bytestokey: 1.0.3
+    dev: true
+
+  /browserify-des/1.0.2:
+    resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==}
+    dependencies:
+      cipher-base: 1.0.4
+      des.js: 1.0.1
+      inherits: 2.0.4
+      safe-buffer: 5.2.1
+    dev: true
+
+  /browserify-rsa/4.1.0:
+    resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==}
+    dependencies:
+      bn.js: 5.2.1
+      randombytes: 2.1.0
+    dev: true
+
+  /browserify-sign/4.2.1:
+    resolution: {integrity: sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==}
+    dependencies:
+      bn.js: 5.2.1
+      browserify-rsa: 4.1.0
+      create-hash: 1.2.0
+      create-hmac: 1.1.7
+      elliptic: 6.5.4
+      inherits: 2.0.4
+      parse-asn1: 5.1.6
+      readable-stream: 3.6.0
+      safe-buffer: 5.2.1
+    dev: true
+
+  /browserify-zlib/0.2.0:
+    resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==}
+    dependencies:
+      pako: 1.0.11
+    dev: true
+
+  /browserslist/4.20.4:
+    resolution: {integrity: sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      caniuse-lite: 1.0.30001352
+      electron-to-chromium: 1.4.150
+      escalade: 3.1.1
+      node-releases: 2.0.5
+      picocolors: 1.0.0
+    dev: true
+
+  /bs-logger/0.2.6:
+    resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
+    engines: {node: '>= 6'}
+    dependencies:
+      fast-json-stable-stringify: 2.1.0
+    dev: true
+
+  /bser/2.1.1:
+    resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+    dependencies:
+      node-int64: 0.4.0
+    dev: true
+
+  /buffer-from/1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+    dev: true
+
+  /buffer-xor/1.0.3:
+    resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==}
+    dev: true
+
+  /buffer/4.9.2:
+    resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==}
+    dependencies:
+      base64-js: 1.5.1
+      ieee754: 1.2.1
+      isarray: 1.0.0
+    dev: true
+
+  /builtin-status-codes/3.0.0:
+    resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
+    dev: true
+
+  /cacache/12.0.4:
+    resolution: {integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==}
+    dependencies:
+      bluebird: 3.7.2
+      chownr: 1.1.4
+      figgy-pudding: 3.5.2
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      infer-owner: 1.0.4
+      lru-cache: 5.1.1
+      mississippi: 3.0.0
+      mkdirp: 0.5.6
+      move-concurrently: 1.0.1
+      promise-inflight: 1.0.1_bluebird@3.7.2
+      rimraf: 2.7.1
+      ssri: 6.0.2
+      unique-filename: 1.1.1
+      y18n: 4.0.3
+    dev: true
+
+  /cacache/15.3.0:
+    resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==}
+    engines: {node: '>= 10'}
+    dependencies:
+      '@npmcli/fs': 1.1.1
+      '@npmcli/move-file': 1.1.2
+      chownr: 2.0.0
+      fs-minipass: 2.1.0
+      glob: 7.2.3
+      infer-owner: 1.0.4
+      lru-cache: 6.0.0
+      minipass: 3.1.6
+      minipass-collect: 1.0.2
+      minipass-flush: 1.0.5
+      minipass-pipeline: 1.2.4
+      mkdirp: 1.0.4
+      p-map: 4.0.0
+      promise-inflight: 1.0.1_bluebird@3.7.2
+      rimraf: 3.0.2
+      ssri: 8.0.1
+      tar: 6.1.11
+      unique-filename: 1.1.1
+    transitivePeerDependencies:
+      - bluebird
+    dev: true
+
+  /cache-base/1.0.1:
+    resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      collection-visit: 1.0.0
+      component-emitter: 1.3.0
+      get-value: 2.0.6
+      has-value: 1.0.0
+      isobject: 3.0.1
+      set-value: 2.0.1
+      to-object-path: 0.3.0
+      union-value: 1.0.1
+      unset-value: 1.0.0
+    dev: true
+
+  /callsites/3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /camelcase/5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /camelcase/6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /caniuse-lite/1.0.30001352:
+    resolution: {integrity: sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==}
+    dev: true
+
+  /canvas/2.9.1:
+    resolution: {integrity: sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==}
+    engines: {node: '>=6'}
+    requiresBuild: true
+    dependencies:
+      '@mapbox/node-pre-gyp': 1.0.9
+      nan: 2.16.0
+      simple-get: 3.1.1
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: true
+
+  /capture-exit/2.0.0:
+    resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+    dependencies:
+      rsvp: 4.8.5
+    dev: true
+
+  /chalk/2.4.2:
+    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+    engines: {node: '>=4'}
+    dependencies:
+      ansi-styles: 3.2.1
+      escape-string-regexp: 1.0.5
+      supports-color: 5.5.0
+    dev: true
+
+  /chalk/4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+    dev: true
+
+  /char-regex/1.0.2:
+    resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /chokidar/2.1.8:
+    resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==}
+    deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
+    dependencies:
+      anymatch: 2.0.0
+      async-each: 1.0.3
+      braces: 2.3.2
+      glob-parent: 3.1.0
+      inherits: 2.0.4
+      is-binary-path: 1.0.1
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      path-is-absolute: 1.0.1
+      readdirp: 2.2.1
+      upath: 1.2.0
+    optionalDependencies:
+      fsevents: 1.2.13
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+    optional: true
+
+  /chokidar/3.5.3:
+    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+    engines: {node: '>= 8.10.0'}
+    requiresBuild: true
+    dependencies:
+      anymatch: 3.1.2
+      braces: 3.0.2
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+    optional: true
+
+  /chownr/1.1.4:
+    resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+    dev: true
+
+  /chownr/2.0.0:
+    resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /chrome-trace-event/1.0.3:
+    resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
+    engines: {node: '>=6.0'}
+    dev: true
+
+  /ci-info/2.0.0:
+    resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
+    dev: true
+
+  /cipher-base/1.0.4:
+    resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==}
+    dependencies:
+      inherits: 2.0.4
+      safe-buffer: 5.2.1
+    dev: true
+
+  /cjs-module-lexer/0.6.0:
+    resolution: {integrity: sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==}
+    dev: true
+
+  /class-utils/0.3.6:
+    resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-union: 3.1.0
+      define-property: 0.2.5
+      isobject: 3.0.1
+      static-extend: 0.1.2
+    dev: true
+
+  /clean-stack/2.2.0:
+    resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /cliui/5.0.0:
+    resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==}
+    dependencies:
+      string-width: 3.1.0
+      strip-ansi: 5.2.0
+      wrap-ansi: 5.1.0
+    dev: true
+
+  /cliui/6.0.0:
+    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 6.2.0
+    dev: true
+
+  /co/4.6.0:
+    resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
+    engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
+    dev: true
+
+  /collect-v8-coverage/1.0.1:
+    resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
+    dev: true
+
+  /collection-visit/1.0.0:
+    resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      map-visit: 1.0.0
+      object-visit: 1.0.1
+    dev: true
+
+  /color-convert/1.9.3:
+    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+    dependencies:
+      color-name: 1.1.3
+    dev: true
+
+  /color-convert/2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: true
+
+  /color-name/1.1.3:
+    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+    dev: true
+
+  /color-name/1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: true
+
+  /color-support/1.1.3:
+    resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
+    hasBin: true
+    dev: true
+
+  /combined-stream/1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      delayed-stream: 1.0.0
+    dev: true
+
+  /commander/2.20.3:
+    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+    dev: true
+
+  /commondir/1.0.1:
+    resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
+    dev: true
+
+  /component-emitter/1.3.0:
+    resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
+    dev: true
+
+  /concat-map/0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+    dev: true
+
+  /concat-stream/1.6.2:
+    resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
+    engines: {'0': node >= 0.8}
+    dependencies:
+      buffer-from: 1.1.2
+      inherits: 2.0.4
+      readable-stream: 2.3.7
+      typedarray: 0.0.6
+    dev: true
+
+  /console-browserify/1.2.0:
+    resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==}
+    dev: true
+
+  /console-control-strings/1.1.0:
+    resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
+    dev: true
+
+  /constants-browserify/1.0.0:
+    resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==}
+    dev: true
+
+  /convert-source-map/1.8.0:
+    resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==}
+    dependencies:
+      safe-buffer: 5.1.2
+    dev: true
+
+  /copy-concurrently/1.0.5:
+    resolution: {integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==}
+    dependencies:
+      aproba: 1.2.0
+      fs-write-stream-atomic: 1.0.10
+      iferr: 0.1.5
+      mkdirp: 0.5.6
+      rimraf: 2.7.1
+      run-queue: 1.0.3
+    dev: true
+
+  /copy-descriptor/0.1.1:
+    resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /core-util-is/1.0.3:
+    resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+    dev: true
+
+  /create-ecdh/4.0.4:
+    resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
+    dependencies:
+      bn.js: 4.12.0
+      elliptic: 6.5.4
+    dev: true
+
+  /create-hash/1.2.0:
+    resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
+    dependencies:
+      cipher-base: 1.0.4
+      inherits: 2.0.4
+      md5.js: 1.3.5
+      ripemd160: 2.0.2
+      sha.js: 2.4.11
+    dev: true
+
+  /create-hmac/1.1.7:
+    resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
+    dependencies:
+      cipher-base: 1.0.4
+      create-hash: 1.2.0
+      inherits: 2.0.4
+      ripemd160: 2.0.2
+      safe-buffer: 5.2.1
+      sha.js: 2.4.11
+    dev: true
+
+  /cross-spawn/6.0.5:
+    resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
+    engines: {node: '>=4.8'}
+    dependencies:
+      nice-try: 1.0.5
+      path-key: 2.0.1
+      semver: 5.7.1
+      shebang-command: 1.2.0
+      which: 1.3.1
+    dev: true
+
+  /cross-spawn/7.0.3:
+    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+    engines: {node: '>= 8'}
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+    dev: true
+
+  /crypto-browserify/3.12.0:
+    resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==}
+    dependencies:
+      browserify-cipher: 1.0.1
+      browserify-sign: 4.2.1
+      create-ecdh: 4.0.4
+      create-hash: 1.2.0
+      create-hmac: 1.1.7
+      diffie-hellman: 5.0.3
+      inherits: 2.0.4
+      pbkdf2: 3.1.2
+      public-encrypt: 4.0.3
+      randombytes: 2.1.0
+      randomfill: 1.0.4
+    dev: true
+
+  /cssom/0.3.8:
+    resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
+    dev: true
+
+  /cssom/0.4.4:
+    resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==}
+    dev: true
+
+  /cssstyle/2.3.0:
+    resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
+    engines: {node: '>=8'}
+    dependencies:
+      cssom: 0.3.8
+    dev: true
+
+  /cyclist/1.0.1:
+    resolution: {integrity: sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==}
+    dev: true
+
+  /data-urls/2.0.0:
+    resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      abab: 2.0.6
+      whatwg-mimetype: 2.3.0
+      whatwg-url: 8.7.0
+    dev: true
+
+  /debug/2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.0.0
+    dev: true
+
+  /debug/2.6.9_supports-color@6.1.0:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.0.0
+      supports-color: 6.1.0
+    dev: true
+
+  /debug/4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+    dev: true
+
+  /decamelize/1.2.0:
+    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /decimal.js/10.3.1:
+    resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==}
+    dev: true
+
+  /decode-uri-component/0.2.0:
+    resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==}
+    engines: {node: '>=0.10'}
+    dev: true
+
+  /decompress-response/4.2.1:
+    resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
+    engines: {node: '>=8'}
+    dependencies:
+      mimic-response: 2.1.0
+    dev: true
+
+  /deep-is/0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+    dev: true
+
+  /deepmerge/4.2.2:
+    resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /define-property/0.2.5:
+    resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-descriptor: 0.1.6
+    dev: true
+
+  /define-property/1.0.0:
+    resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-descriptor: 1.0.2
+    dev: true
+
+  /define-property/2.0.2:
+    resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-descriptor: 1.0.2
+      isobject: 3.0.1
+    dev: true
+
+  /delayed-stream/1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+    dev: true
+
+  /delegates/1.0.0:
+    resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
+    dev: true
+
+  /des.js/1.0.1:
+    resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==}
+    dependencies:
+      inherits: 2.0.4
+      minimalistic-assert: 1.0.1
+    dev: true
+
+  /detect-file/1.0.0:
+    resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /detect-libc/2.0.1:
+    resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /detect-newline/3.1.0:
+    resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /diff-sequences/26.6.2:
+    resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==}
+    engines: {node: '>= 10.14.2'}
+    dev: true
+
+  /diffie-hellman/5.0.3:
+    resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
+    dependencies:
+      bn.js: 4.12.0
+      miller-rabin: 4.0.1
+      randombytes: 2.1.0
+    dev: true
+
+  /doctrine/3.0.0:
+    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      esutils: 2.0.3
+    dev: true
+
+  /domain-browser/1.2.0:
+    resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
+    engines: {node: '>=0.4', npm: '>=1.2'}
+    dev: true
+
+  /domexception/2.0.1:
+    resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==}
+    engines: {node: '>=8'}
+    dependencies:
+      webidl-conversions: 5.0.0
+    dev: true
+
+  /duplexify/3.7.1:
+    resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==}
+    dependencies:
+      end-of-stream: 1.4.4
+      inherits: 2.0.4
+      readable-stream: 2.3.7
+      stream-shift: 1.0.1
+    dev: true
+
+  /electron-to-chromium/1.4.150:
+    resolution: {integrity: sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA==}
+    dev: true
+
+  /elliptic/6.5.4:
+    resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
+    dependencies:
+      bn.js: 4.12.0
+      brorand: 1.1.0
+      hash.js: 1.1.7
+      hmac-drbg: 1.0.1
+      inherits: 2.0.4
+      minimalistic-assert: 1.0.1
+      minimalistic-crypto-utils: 1.0.1
+    dev: true
+
+  /emittery/0.7.2:
+    resolution: {integrity: sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /emoji-regex/7.0.3:
+    resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==}
+    dev: true
+
+  /emoji-regex/8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+    dev: true
+
+  /emojis-list/3.0.0:
+    resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
+    engines: {node: '>= 4'}
+    dev: true
+
+  /end-of-stream/1.4.4:
+    resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+    dependencies:
+      once: 1.4.0
+    dev: true
+
+  /enhanced-resolve/4.5.0:
+    resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      graceful-fs: 4.2.10
+      memory-fs: 0.5.0
+      tapable: 1.1.3
+    dev: true
+
+  /enquirer/2.3.6:
+    resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      ansi-colors: 4.1.3
+    dev: true
+
+  /errno/0.1.8:
+    resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
+    hasBin: true
+    dependencies:
+      prr: 1.0.1
+    dev: true
+
+  /error-ex/1.3.2:
+    resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+    dependencies:
+      is-arrayish: 0.2.1
+    dev: true
+
+  /escalade/3.1.1:
+    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /escape-string-regexp/1.0.5:
+    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+    engines: {node: '>=0.8.0'}
+    dev: true
+
+  /escape-string-regexp/2.0.0:
+    resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /escape-string-regexp/4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /escodegen/2.0.0:
+    resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==}
+    engines: {node: '>=6.0'}
+    hasBin: true
+    dependencies:
+      esprima: 4.0.1
+      estraverse: 5.3.0
+      esutils: 2.0.3
+      optionator: 0.8.3
+    optionalDependencies:
+      source-map: 0.6.1
+    dev: true
+
+  /eslint-config-prettier/6.15.0_eslint@7.32.0:
+    resolution: {integrity: sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=3.14.1'
+    dependencies:
+      eslint: 7.32.0
+      get-stdin: 6.0.0
+    dev: true
+
+  /eslint-plugin-jest/23.20.0_2de3j2mqba4wgeuiaqz2k7syrm:
+    resolution: {integrity: sha512-+6BGQt85OREevBDWCvhqj1yYA4+BFK4XnRZSGJionuEYmcglMZYLNNBBemwzbqUAckURaHdJSBcjHPyrtypZOw==}
+    engines: {node: '>=8'}
+    peerDependencies:
+      eslint: '>=5'
+    dependencies:
+      '@typescript-eslint/experimental-utils': 2.34.0_2de3j2mqba4wgeuiaqz2k7syrm
+      eslint: 7.32.0
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+    dev: true
+
+  /eslint-plugin-prettier/3.4.1_dnozcfbeb5wiozlftdrslmvmgu:
+    resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==}
+    engines: {node: '>=6.0.0'}
+    peerDependencies:
+      eslint: '>=5.0.0'
+      eslint-config-prettier: '*'
+      prettier: '>=1.13.0'
+    peerDependenciesMeta:
+      eslint-config-prettier:
+        optional: true
+    dependencies:
+      eslint: 7.32.0
+      eslint-config-prettier: 6.15.0_eslint@7.32.0
+      prettier: 2.6.0
+      prettier-linter-helpers: 1.0.0
+    dev: true
+
+  /eslint-scope/4.0.3:
+    resolution: {integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==}
+    engines: {node: '>=4.0.0'}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 4.3.0
+    dev: true
+
+  /eslint-scope/5.1.1:
+    resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+    engines: {node: '>=8.0.0'}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 4.3.0
+    dev: true
+
+  /eslint-utils/2.1.0:
+    resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==}
+    engines: {node: '>=6'}
+    dependencies:
+      eslint-visitor-keys: 1.3.0
+    dev: true
+
+  /eslint-visitor-keys/1.3.0:
+    resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /eslint-visitor-keys/2.1.0:
+    resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /eslint/7.32.0:
+    resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    hasBin: true
+    dependencies:
+      '@babel/code-frame': 7.12.11
+      '@eslint/eslintrc': 0.4.3
+      '@humanwhocodes/config-array': 0.5.0
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.4
+      doctrine: 3.0.0
+      enquirer: 2.3.6
+      escape-string-regexp: 4.0.0
+      eslint-scope: 5.1.1
+      eslint-utils: 2.1.0
+      eslint-visitor-keys: 2.1.0
+      espree: 7.3.1
+      esquery: 1.4.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 6.0.1
+      functional-red-black-tree: 1.0.1
+      glob-parent: 5.1.2
+      globals: 13.15.0
+      ignore: 4.0.6
+      import-fresh: 3.3.0
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      js-yaml: 3.14.1
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.1
+      progress: 2.0.3
+      regexpp: 3.2.0
+      semver: 7.3.7
+      strip-ansi: 6.0.1
+      strip-json-comments: 3.1.1
+      table: 6.8.0
+      text-table: 0.2.0
+      v8-compile-cache: 2.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /espree/7.3.1:
+    resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      acorn: 7.4.1
+      acorn-jsx: 5.3.2_acorn@7.4.1
+      eslint-visitor-keys: 1.3.0
+    dev: true
+
+  /esprima/4.0.1:
+    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /esquery/1.4.0:
+    resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
+    engines: {node: '>=0.10'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /esrecurse/4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /estraverse/4.3.0:
+    resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /estraverse/5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /esutils/2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /events/3.3.0:
+    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+    engines: {node: '>=0.8.x'}
+    dev: true
+
+  /evp_bytestokey/1.0.3:
+    resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==}
+    dependencies:
+      md5.js: 1.3.5
+      safe-buffer: 5.2.1
+    dev: true
+
+  /exec-sh/0.3.6:
+    resolution: {integrity: sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==}
+    dev: true
+
+  /execa/1.0.0:
+    resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
+    engines: {node: '>=6'}
+    dependencies:
+      cross-spawn: 6.0.5
+      get-stream: 4.1.0
+      is-stream: 1.1.0
+      npm-run-path: 2.0.2
+      p-finally: 1.0.0
+      signal-exit: 3.0.7
+      strip-eof: 1.0.0
+    dev: true
+
+  /execa/4.1.0:
+    resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
+    engines: {node: '>=10'}
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 5.2.0
+      human-signals: 1.1.1
+      is-stream: 2.0.1
+      merge-stream: 2.0.0
+      npm-run-path: 4.0.1
+      onetime: 5.1.2
+      signal-exit: 3.0.7
+      strip-final-newline: 2.0.0
+    dev: true
+
+  /exit/0.1.2:
+    resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /expand-brackets/2.1.4:
+    resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      debug: 2.6.9
+      define-property: 0.2.5
+      extend-shallow: 2.0.1
+      posix-character-classes: 0.1.1
+      regex-not: 1.0.2
+      snapdragon: 0.8.2
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /expand-brackets/2.1.4_supports-color@6.1.0:
+    resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      debug: 2.6.9_supports-color@6.1.0
+      define-property: 0.2.5
+      extend-shallow: 2.0.1
+      posix-character-classes: 0.1.1
+      regex-not: 1.0.2
+      snapdragon: 0.8.2_supports-color@6.1.0
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /expand-tilde/2.0.2:
+    resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      homedir-polyfill: 1.0.3
+    dev: true
+
+  /expect/26.6.2:
+    resolution: {integrity: sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      ansi-styles: 4.3.0
+      jest-get-type: 26.3.0
+      jest-matcher-utils: 26.6.2
+      jest-message-util: 26.6.2
+      jest-regex-util: 26.0.0
+    dev: true
+
+  /extend-shallow/2.0.1:
+    resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extendable: 0.1.1
+    dev: true
+
+  /extend-shallow/3.0.2:
+    resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      assign-symbols: 1.0.0
+      is-extendable: 1.0.1
+    dev: true
+
+  /extglob/2.0.4:
+    resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      array-unique: 0.3.2
+      define-property: 1.0.0
+      expand-brackets: 2.1.4
+      extend-shallow: 2.0.1
+      fragment-cache: 0.2.1
+      regex-not: 1.0.2
+      snapdragon: 0.8.2
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /extglob/2.0.4_supports-color@6.1.0:
+    resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      array-unique: 0.3.2
+      define-property: 1.0.0
+      expand-brackets: 2.1.4_supports-color@6.1.0
+      extend-shallow: 2.0.1
+      fragment-cache: 0.2.1
+      regex-not: 1.0.2
+      snapdragon: 0.8.2_supports-color@6.1.0
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /fast-deep-equal/3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+    dev: true
+
+  /fast-diff/1.2.0:
+    resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==}
+    dev: true
+
+  /fast-json-stable-stringify/2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+    dev: true
+
+  /fast-levenshtein/2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+    dev: true
+
+  /fb-watchman/2.0.1:
+    resolution: {integrity: sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==}
+    dependencies:
+      bser: 2.1.1
+    dev: true
+
+  /figgy-pudding/3.5.2:
+    resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==}
+    dev: true
+
+  /file-entry-cache/6.0.1:
+    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flat-cache: 3.0.4
+    dev: true
+
+  /file-uri-to-path/1.0.0:
+    resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
+    dev: true
+    optional: true
+
+  /fill-range/4.0.0:
+    resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      extend-shallow: 2.0.1
+      is-number: 3.0.0
+      repeat-string: 1.6.1
+      to-regex-range: 2.1.1
+    dev: true
+
+  /fill-range/7.0.1:
+    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      to-regex-range: 5.0.1
+    dev: true
+
+  /find-cache-dir/2.1.0:
+    resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      commondir: 1.0.1
+      make-dir: 2.1.0
+      pkg-dir: 3.0.0
+    dev: true
+
+  /find-cache-dir/3.3.2:
+    resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
+    engines: {node: '>=8'}
+    dependencies:
+      commondir: 1.0.1
+      make-dir: 3.1.0
+      pkg-dir: 4.2.0
+    dev: true
+
+  /find-up/3.0.0:
+    resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
+    engines: {node: '>=6'}
+    dependencies:
+      locate-path: 3.0.0
+    dev: true
+
+  /find-up/4.1.0:
+    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+    engines: {node: '>=8'}
+    dependencies:
+      locate-path: 5.0.0
+      path-exists: 4.0.0
+    dev: true
+
+  /findup-sync/3.0.0_supports-color@6.1.0:
+    resolution: {integrity: sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==}
+    engines: {node: '>= 0.10'}
+    dependencies:
+      detect-file: 1.0.0
+      is-glob: 4.0.3
+      micromatch: 3.1.10_supports-color@6.1.0
+      resolve-dir: 1.0.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /flat-cache/3.0.4:
+    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flatted: 3.2.5
+      rimraf: 3.0.2
+    dev: true
+
+  /flatted/3.2.5:
+    resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==}
+    dev: true
+
+  /flush-write-stream/1.1.1:
+    resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
+    dependencies:
+      inherits: 2.0.4
+      readable-stream: 2.3.7
+    dev: true
+
+  /for-in/1.0.2:
+    resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /form-data/3.0.1:
+    resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
+    engines: {node: '>= 6'}
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      mime-types: 2.1.35
+    dev: true
+
+  /fragment-cache/0.2.1:
+    resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      map-cache: 0.2.2
+    dev: true
+
+  /from2/2.3.0:
+    resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==}
+    dependencies:
+      inherits: 2.0.4
+      readable-stream: 2.3.7
+    dev: true
+
+  /fs-minipass/2.1.0:
+    resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.1.6
+    dev: true
+
+  /fs-write-stream-atomic/1.0.10:
+    resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==}
+    dependencies:
+      graceful-fs: 4.2.10
+      iferr: 0.1.5
+      imurmurhash: 0.1.4
+      readable-stream: 2.3.7
+    dev: true
+
+  /fs.realpath/1.0.0:
+    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+    dev: true
+
+  /fsevents/1.2.13:
+    resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==}
+    engines: {node: '>= 4.0'}
+    os: [darwin]
+    deprecated: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
+    requiresBuild: true
+    dependencies:
+      bindings: 1.5.0
+      nan: 2.16.0
+    dev: true
+    optional: true
+
+  /fsevents/2.3.2:
+    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /function-bind/1.1.1:
+    resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+    dev: true
+
+  /functional-red-black-tree/1.0.1:
+    resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
+    dev: true
+
+  /gauge/3.0.2:
+    resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
+    engines: {node: '>=10'}
+    dependencies:
+      aproba: 2.0.0
+      color-support: 1.1.3
+      console-control-strings: 1.1.0
+      has-unicode: 2.0.1
+      object-assign: 4.1.1
+      signal-exit: 3.0.7
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wide-align: 1.1.5
+    dev: true
+
+  /gensync/1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /get-caller-file/2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+    dev: true
+
+  /get-package-type/0.1.0:
+    resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+    engines: {node: '>=8.0.0'}
+    dev: true
+
+  /get-stdin/6.0.0:
+    resolution: {integrity: sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /get-stream/4.1.0:
+    resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
+    engines: {node: '>=6'}
+    dependencies:
+      pump: 3.0.0
+    dev: true
+
+  /get-stream/5.2.0:
+    resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
+    engines: {node: '>=8'}
+    dependencies:
+      pump: 3.0.0
+    dev: true
+
+  /get-value/2.0.6:
+    resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /glob-parent/3.1.0:
+    resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==}
+    dependencies:
+      is-glob: 3.1.0
+      path-dirname: 1.0.2
+    dev: true
+    optional: true
+
+  /glob-parent/5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob/7.2.3:
+    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 3.1.2
+      once: 1.4.0
+      path-is-absolute: 1.0.1
+    dev: true
+
+  /global-modules/1.0.0:
+    resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      global-prefix: 1.0.2
+      is-windows: 1.0.2
+      resolve-dir: 1.0.1
+    dev: true
+
+  /global-modules/2.0.0:
+    resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==}
+    engines: {node: '>=6'}
+    dependencies:
+      global-prefix: 3.0.0
+    dev: true
+
+  /global-prefix/1.0.2:
+    resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      expand-tilde: 2.0.2
+      homedir-polyfill: 1.0.3
+      ini: 1.3.8
+      is-windows: 1.0.2
+      which: 1.3.1
+    dev: true
+
+  /global-prefix/3.0.0:
+    resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
+    engines: {node: '>=6'}
+    dependencies:
+      ini: 1.3.8
+      kind-of: 6.0.3
+      which: 1.3.1
+    dev: true
+
+  /globals/11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /globals/13.15.0:
+    resolution: {integrity: sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==}
+    engines: {node: '>=8'}
+    dependencies:
+      type-fest: 0.20.2
+    dev: true
+
+  /graceful-fs/4.2.10:
+    resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
+    dev: true
+
+  /growly/1.3.0:
+    resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==}
+    dev: true
+    optional: true
+
+  /has-flag/3.0.0:
+    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /has-flag/4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /has-unicode/2.0.1:
+    resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
+    dev: true
+
+  /has-value/0.3.1:
+    resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      get-value: 2.0.6
+      has-values: 0.1.4
+      isobject: 2.1.0
+    dev: true
+
+  /has-value/1.0.0:
+    resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      get-value: 2.0.6
+      has-values: 1.0.0
+      isobject: 3.0.1
+    dev: true
+
+  /has-values/0.1.4:
+    resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /has-values/1.0.0:
+    resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-number: 3.0.0
+      kind-of: 4.0.0
+    dev: true
+
+  /has/1.0.3:
+    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+    engines: {node: '>= 0.4.0'}
+    dependencies:
+      function-bind: 1.1.1
+    dev: true
+
+  /hash-base/3.1.0:
+    resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==}
+    engines: {node: '>=4'}
+    dependencies:
+      inherits: 2.0.4
+      readable-stream: 3.6.0
+      safe-buffer: 5.2.1
+    dev: true
+
+  /hash.js/1.1.7:
+    resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
+    dependencies:
+      inherits: 2.0.4
+      minimalistic-assert: 1.0.1
+    dev: true
+
+  /hmac-drbg/1.0.1:
+    resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
+    dependencies:
+      hash.js: 1.1.7
+      minimalistic-assert: 1.0.1
+      minimalistic-crypto-utils: 1.0.1
+    dev: true
+
+  /homedir-polyfill/1.0.3:
+    resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      parse-passwd: 1.0.0
+    dev: true
+
+  /hosted-git-info/2.8.9:
+    resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+    dev: true
+
+  /html-encoding-sniffer/2.0.1:
+    resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      whatwg-encoding: 1.0.5
+    dev: true
+
+  /html-escaper/2.0.2:
+    resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+    dev: true
+
+  /http-proxy-agent/4.0.1:
+    resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
+    engines: {node: '>= 6'}
+    dependencies:
+      '@tootallnate/once': 1.1.2
+      agent-base: 6.0.2
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /https-browserify/1.0.0:
+    resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==}
+    dev: true
+
+  /https-proxy-agent/5.0.1:
+    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+    engines: {node: '>= 6'}
+    dependencies:
+      agent-base: 6.0.2
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /human-signals/1.1.1:
+    resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
+    engines: {node: '>=8.12.0'}
+    dev: true
+
+  /iconv-lite/0.4.24:
+    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      safer-buffer: 2.1.2
+    dev: true
+
+  /ieee754/1.2.1:
+    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+    dev: true
+
+  /iferr/0.1.5:
+    resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==}
+    dev: true
+
+  /ignore/4.0.6:
+    resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==}
+    engines: {node: '>= 4'}
+    dev: true
+
+  /import-fresh/3.3.0:
+    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+    engines: {node: '>=6'}
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+    dev: true
+
+  /import-local/2.0.0:
+    resolution: {integrity: sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==}
+    engines: {node: '>=6'}
+    hasBin: true
+    dependencies:
+      pkg-dir: 3.0.0
+      resolve-cwd: 2.0.0
+    dev: true
+
+  /import-local/3.1.0:
+    resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
+    engines: {node: '>=8'}
+    hasBin: true
+    dependencies:
+      pkg-dir: 4.2.0
+      resolve-cwd: 3.0.0
+    dev: true
+
+  /imurmurhash/0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+    dev: true
+
+  /indent-string/4.0.0:
+    resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /infer-owner/1.0.4:
+    resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
+    dev: true
+
+  /inflight/1.0.6:
+    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+    dependencies:
+      once: 1.4.0
+      wrappy: 1.0.2
+    dev: true
+
+  /inherits/2.0.1:
+    resolution: {integrity: sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==}
+    dev: true
+
+  /inherits/2.0.3:
+    resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==}
+    dev: true
+
+  /inherits/2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+    dev: true
+
+  /ini/1.3.8:
+    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+    dev: true
+
+  /interpret/1.4.0:
+    resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
+    engines: {node: '>= 0.10'}
+    dev: true
+
+  /is-accessor-descriptor/0.1.6:
+    resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      kind-of: 3.2.2
+    dev: true
+
+  /is-accessor-descriptor/1.0.0:
+    resolution: {integrity: sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      kind-of: 6.0.3
+    dev: true
+
+  /is-arrayish/0.2.1:
+    resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+    dev: true
+
+  /is-binary-path/1.0.1:
+    resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      binary-extensions: 1.13.1
+    dev: true
+    optional: true
+
+  /is-binary-path/2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+    dependencies:
+      binary-extensions: 2.2.0
+    dev: true
+    optional: true
+
+  /is-buffer/1.1.6:
+    resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
+    dev: true
+
+  /is-ci/2.0.0:
+    resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==}
+    hasBin: true
+    dependencies:
+      ci-info: 2.0.0
+    dev: true
+
+  /is-core-module/2.9.0:
+    resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==}
+    dependencies:
+      has: 1.0.3
+    dev: true
+
+  /is-data-descriptor/0.1.4:
+    resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      kind-of: 3.2.2
+    dev: true
+
+  /is-data-descriptor/1.0.0:
+    resolution: {integrity: sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      kind-of: 6.0.3
+    dev: true
+
+  /is-descriptor/0.1.6:
+    resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-accessor-descriptor: 0.1.6
+      is-data-descriptor: 0.1.4
+      kind-of: 5.1.0
+    dev: true
+
+  /is-descriptor/1.0.2:
+    resolution: {integrity: sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-accessor-descriptor: 1.0.0
+      is-data-descriptor: 1.0.0
+      kind-of: 6.0.3
+    dev: true
+
+  /is-docker/2.2.1:
+    resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+    engines: {node: '>=8'}
+    hasBin: true
+    dev: true
+    optional: true
+
+  /is-extendable/0.1.1:
+    resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-extendable/1.0.1:
+    resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-plain-object: 2.0.4
+    dev: true
+
+  /is-extglob/2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-fullwidth-code-point/2.0.0:
+    resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /is-fullwidth-code-point/3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-generator-fn/2.1.0:
+    resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /is-glob/3.1.0:
+    resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+    dev: true
+    optional: true
+
+  /is-glob/4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+    dev: true
+
+  /is-number/3.0.0:
+    resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      kind-of: 3.2.2
+    dev: true
+
+  /is-number/7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+    dev: true
+
+  /is-plain-object/2.0.4:
+    resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      isobject: 3.0.1
+    dev: true
+
+  /is-potential-custom-element-name/1.0.1:
+    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+    dev: true
+
+  /is-stream/1.1.0:
+    resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-stream/2.0.1:
+    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-typedarray/1.0.0:
+    resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
+    dev: true
+
+  /is-windows/1.0.2:
+    resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-wsl/1.1.0:
+    resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /is-wsl/2.2.0:
+    resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+    engines: {node: '>=8'}
+    dependencies:
+      is-docker: 2.2.1
+    dev: true
+    optional: true
+
+  /isarray/1.0.0:
+    resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
+    dev: true
+
+  /isexe/2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+    dev: true
+
+  /isobject/2.1.0:
+    resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      isarray: 1.0.0
+    dev: true
+
+  /isobject/3.0.1:
+    resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /istanbul-lib-coverage/3.2.0:
+    resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /istanbul-lib-instrument/4.0.3:
+    resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@babel/core': 7.18.2
+      '@istanbuljs/schema': 0.1.3
+      istanbul-lib-coverage: 3.2.0
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /istanbul-lib-instrument/5.2.0:
+    resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@babel/core': 7.18.2
+      '@babel/parser': 7.18.4
+      '@istanbuljs/schema': 0.1.3
+      istanbul-lib-coverage: 3.2.0
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /istanbul-lib-report/3.0.0:
+    resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==}
+    engines: {node: '>=8'}
+    dependencies:
+      istanbul-lib-coverage: 3.2.0
+      make-dir: 3.1.0
+      supports-color: 7.2.0
+    dev: true
+
+  /istanbul-lib-source-maps/4.0.1:
+    resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
+    engines: {node: '>=10'}
+    dependencies:
+      debug: 4.3.4
+      istanbul-lib-coverage: 3.2.0
+      source-map: 0.6.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /istanbul-reports/3.1.4:
+    resolution: {integrity: sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==}
+    engines: {node: '>=8'}
+    dependencies:
+      html-escaper: 2.0.2
+      istanbul-lib-report: 3.0.0
+    dev: true
+
+  /jest-changed-files/26.6.2:
+    resolution: {integrity: sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      execa: 4.1.0
+      throat: 5.0.0
+    dev: true
+
+  /jest-cli/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==}
+    engines: {node: '>= 10.14.2'}
+    hasBin: true
+    dependencies:
+      '@jest/core': 26.6.3_canvas@2.9.1
+      '@jest/test-result': 26.6.2
+      '@jest/types': 26.6.2
+      chalk: 4.1.2
+      exit: 0.1.2
+      graceful-fs: 4.2.10
+      import-local: 3.1.0
+      is-ci: 2.0.0
+      jest-config: 26.6.3_canvas@2.9.1
+      jest-util: 26.6.2
+      jest-validate: 26.6.2
+      prompts: 2.4.2
+      yargs: 15.4.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /jest-config/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==}
+    engines: {node: '>= 10.14.2'}
+    peerDependencies:
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      ts-node:
+        optional: true
+    dependencies:
+      '@babel/core': 7.18.2
+      '@jest/test-sequencer': 26.6.3_canvas@2.9.1
+      '@jest/types': 26.6.2
+      babel-jest: 26.6.3_@babel+core@7.18.2
+      chalk: 4.1.2
+      deepmerge: 4.2.2
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      jest-environment-jsdom: 26.6.2_canvas@2.9.1
+      jest-environment-node: 26.6.2
+      jest-get-type: 26.3.0
+      jest-jasmine2: 26.6.3_canvas@2.9.1
+      jest-regex-util: 26.0.0
+      jest-resolve: 26.6.2
+      jest-util: 26.6.2
+      jest-validate: 26.6.2
+      micromatch: 4.0.5
+      pretty-format: 26.6.2
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jest-diff/26.6.2:
+    resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      chalk: 4.1.2
+      diff-sequences: 26.6.2
+      jest-get-type: 26.3.0
+      pretty-format: 26.6.2
+    dev: true
+
+  /jest-docblock/26.0.0:
+    resolution: {integrity: sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      detect-newline: 3.1.0
+    dev: true
+
+  /jest-each/26.6.2:
+    resolution: {integrity: sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      chalk: 4.1.2
+      jest-get-type: 26.3.0
+      jest-util: 26.6.2
+      pretty-format: 26.6.2
+    dev: true
+
+  /jest-environment-jsdom/26.6.2_canvas@2.9.1:
+    resolution: {integrity: sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/environment': 26.6.2
+      '@jest/fake-timers': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      jest-mock: 26.6.2
+      jest-util: 26.6.2
+      jsdom: 16.7.0_canvas@2.9.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jest-environment-node/26.6.2:
+    resolution: {integrity: sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/environment': 26.6.2
+      '@jest/fake-timers': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      jest-mock: 26.6.2
+      jest-util: 26.6.2
+    dev: true
+
+  /jest-get-type/26.3.0:
+    resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==}
+    engines: {node: '>= 10.14.2'}
+    dev: true
+
+  /jest-haste-map/26.6.2:
+    resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      '@types/graceful-fs': 4.1.5
+      '@types/node': 17.0.41
+      anymatch: 3.1.2
+      fb-watchman: 2.0.1
+      graceful-fs: 4.2.10
+      jest-regex-util: 26.0.0
+      jest-serializer: 26.6.2
+      jest-util: 26.6.2
+      jest-worker: 26.6.2
+      micromatch: 4.0.5
+      sane: 4.1.0
+      walker: 1.0.8
+    optionalDependencies:
+      fsevents: 2.3.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-jasmine2/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@babel/traverse': 7.18.2
+      '@jest/environment': 26.6.2
+      '@jest/source-map': 26.6.2
+      '@jest/test-result': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      chalk: 4.1.2
+      co: 4.6.0
+      expect: 26.6.2
+      is-generator-fn: 2.1.0
+      jest-each: 26.6.2
+      jest-matcher-utils: 26.6.2
+      jest-message-util: 26.6.2
+      jest-runtime: 26.6.3_canvas@2.9.1
+      jest-snapshot: 26.6.2
+      jest-util: 26.6.2
+      pretty-format: 26.6.2
+      throat: 5.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /jest-leak-detector/26.6.2:
+    resolution: {integrity: sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      jest-get-type: 26.3.0
+      pretty-format: 26.6.2
+    dev: true
+
+  /jest-matcher-utils/26.6.2:
+    resolution: {integrity: sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      chalk: 4.1.2
+      jest-diff: 26.6.2
+      jest-get-type: 26.3.0
+      pretty-format: 26.6.2
+    dev: true
+
+  /jest-message-util/26.6.2:
+    resolution: {integrity: sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@babel/code-frame': 7.16.7
+      '@jest/types': 26.6.2
+      '@types/stack-utils': 2.0.1
+      chalk: 4.1.2
+      graceful-fs: 4.2.10
+      micromatch: 4.0.5
+      pretty-format: 26.6.2
+      slash: 3.0.0
+      stack-utils: 2.0.5
+    dev: true
+
+  /jest-mock/26.6.2:
+    resolution: {integrity: sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+    dev: true
+
+  /jest-pnp-resolver/1.2.2_jest-resolve@26.6.2:
+    resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==}
+    engines: {node: '>=6'}
+    peerDependencies:
+      jest-resolve: '*'
+    peerDependenciesMeta:
+      jest-resolve:
+        optional: true
+    dependencies:
+      jest-resolve: 26.6.2
+    dev: true
+
+  /jest-regex-util/26.0.0:
+    resolution: {integrity: sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==}
+    engines: {node: '>= 10.14.2'}
+    dev: true
+
+  /jest-resolve-dependencies/26.6.3:
+    resolution: {integrity: sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      jest-regex-util: 26.0.0
+      jest-snapshot: 26.6.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-resolve/26.6.2:
+    resolution: {integrity: sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      chalk: 4.1.2
+      graceful-fs: 4.2.10
+      jest-pnp-resolver: 1.2.2_jest-resolve@26.6.2
+      jest-util: 26.6.2
+      read-pkg-up: 7.0.1
+      resolve: 1.22.0
+      slash: 3.0.0
+    dev: true
+
+  /jest-runner/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/console': 26.6.2
+      '@jest/environment': 26.6.2
+      '@jest/test-result': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      chalk: 4.1.2
+      emittery: 0.7.2
+      exit: 0.1.2
+      graceful-fs: 4.2.10
+      jest-config: 26.6.3_canvas@2.9.1
+      jest-docblock: 26.0.0
+      jest-haste-map: 26.6.2
+      jest-leak-detector: 26.6.2
+      jest-message-util: 26.6.2
+      jest-resolve: 26.6.2
+      jest-runtime: 26.6.3_canvas@2.9.1
+      jest-util: 26.6.2
+      jest-worker: 26.6.2
+      source-map-support: 0.5.21
+      throat: 5.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /jest-runtime/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==}
+    engines: {node: '>= 10.14.2'}
+    hasBin: true
+    dependencies:
+      '@jest/console': 26.6.2
+      '@jest/environment': 26.6.2
+      '@jest/fake-timers': 26.6.2
+      '@jest/globals': 26.6.2
+      '@jest/source-map': 26.6.2
+      '@jest/test-result': 26.6.2
+      '@jest/transform': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/yargs': 15.0.14
+      chalk: 4.1.2
+      cjs-module-lexer: 0.6.0
+      collect-v8-coverage: 1.0.1
+      exit: 0.1.2
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      jest-config: 26.6.3_canvas@2.9.1
+      jest-haste-map: 26.6.2
+      jest-message-util: 26.6.2
+      jest-mock: 26.6.2
+      jest-regex-util: 26.0.0
+      jest-resolve: 26.6.2
+      jest-snapshot: 26.6.2
+      jest-util: 26.6.2
+      jest-validate: 26.6.2
+      slash: 3.0.0
+      strip-bom: 4.0.0
+      yargs: 15.4.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /jest-serializer/26.6.2:
+    resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@types/node': 17.0.41
+      graceful-fs: 4.2.10
+    dev: true
+
+  /jest-snapshot/26.6.2:
+    resolution: {integrity: sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@babel/types': 7.18.4
+      '@jest/types': 26.6.2
+      '@types/babel__traverse': 7.17.1
+      '@types/prettier': 2.6.0
+      chalk: 4.1.2
+      expect: 26.6.2
+      graceful-fs: 4.2.10
+      jest-diff: 26.6.2
+      jest-get-type: 26.3.0
+      jest-haste-map: 26.6.2
+      jest-matcher-utils: 26.6.2
+      jest-message-util: 26.6.2
+      jest-resolve: 26.6.2
+      natural-compare: 1.4.0
+      pretty-format: 26.6.2
+      semver: 7.3.7
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-util/26.6.2:
+    resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      chalk: 4.1.2
+      graceful-fs: 4.2.10
+      is-ci: 2.0.0
+      micromatch: 4.0.5
+    dev: true
+
+  /jest-validate/26.6.2:
+    resolution: {integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/types': 26.6.2
+      camelcase: 6.3.0
+      chalk: 4.1.2
+      jest-get-type: 26.3.0
+      leven: 3.1.0
+      pretty-format: 26.6.2
+    dev: true
+
+  /jest-watcher/26.6.2:
+    resolution: {integrity: sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==}
+    engines: {node: '>= 10.14.2'}
+    dependencies:
+      '@jest/test-result': 26.6.2
+      '@jest/types': 26.6.2
+      '@types/node': 17.0.41
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      jest-util: 26.6.2
+      string-length: 4.0.2
+    dev: true
+
+  /jest-worker/26.6.2:
+    resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
+    engines: {node: '>= 10.13.0'}
+    dependencies:
+      '@types/node': 17.0.41
+      merge-stream: 2.0.0
+      supports-color: 7.2.0
+    dev: true
+
+  /jest/26.6.3_canvas@2.9.1:
+    resolution: {integrity: sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==}
+    engines: {node: '>= 10.14.2'}
+    hasBin: true
+    dependencies:
+      '@jest/core': 26.6.3_canvas@2.9.1
+      import-local: 3.1.0
+      jest-cli: 26.6.3_canvas@2.9.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /js-tokens/4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+    dev: true
+
+  /js-yaml/3.14.1:
+    resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+    hasBin: true
+    dependencies:
+      argparse: 1.0.10
+      esprima: 4.0.1
+    dev: true
+
+  /jsdom/16.7.0_canvas@2.9.1:
+    resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      canvas: ^2.5.0
+    peerDependenciesMeta:
+      canvas:
+        optional: true
+    dependencies:
+      abab: 2.0.6
+      acorn: 8.7.1
+      acorn-globals: 6.0.0
+      canvas: 2.9.1
+      cssom: 0.4.4
+      cssstyle: 2.3.0
+      data-urls: 2.0.0
+      decimal.js: 10.3.1
+      domexception: 2.0.1
+      escodegen: 2.0.0
+      form-data: 3.0.1
+      html-encoding-sniffer: 2.0.1
+      http-proxy-agent: 4.0.1
+      https-proxy-agent: 5.0.1
+      is-potential-custom-element-name: 1.0.1
+      nwsapi: 2.2.0
+      parse5: 6.0.1
+      saxes: 5.0.1
+      symbol-tree: 3.2.4
+      tough-cookie: 4.0.0
+      w3c-hr-time: 1.0.2
+      w3c-xmlserializer: 2.0.0
+      webidl-conversions: 6.1.0
+      whatwg-encoding: 1.0.5
+      whatwg-mimetype: 2.3.0
+      whatwg-url: 8.7.0
+      ws: 7.5.8
+      xml-name-validator: 3.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jsesc/2.5.2:
+    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /json-parse-better-errors/1.0.2:
+    resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
+    dev: true
+
+  /json-parse-even-better-errors/2.3.1:
+    resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+    dev: true
+
+  /json-schema-traverse/0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+    dev: true
+
+  /json-schema-traverse/1.0.0:
+    resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
+    dev: true
+
+  /json-stable-stringify-without-jsonify/1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+    dev: true
+
+  /json5/1.0.1:
+    resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==}
+    hasBin: true
+    dependencies:
+      minimist: 1.2.6
+    dev: true
+
+  /json5/2.2.1:
+    resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
+    engines: {node: '>=6'}
+    hasBin: true
+    dev: true
+
+  /kind-of/3.2.2:
+    resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-buffer: 1.1.6
+    dev: true
+
+  /kind-of/4.0.0:
+    resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-buffer: 1.1.6
+    dev: true
+
+  /kind-of/5.1.0:
+    resolution: {integrity: sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /kind-of/6.0.3:
+    resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /kleur/3.0.3:
+    resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /leven/3.1.0:
+    resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /levn/0.3.0:
+    resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.1.2
+      type-check: 0.3.2
+    dev: true
+
+  /levn/0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+    dev: true
+
+  /lines-and-columns/1.2.4:
+    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+    dev: true
+
+  /loader-runner/2.4.0:
+    resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==}
+    engines: {node: '>=4.3.0 <5.0.0 || >=5.10'}
+    dev: true
+
+  /loader-utils/1.4.0:
+    resolution: {integrity: sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==}
+    engines: {node: '>=4.0.0'}
+    dependencies:
+      big.js: 5.2.2
+      emojis-list: 3.0.0
+      json5: 1.0.1
+    dev: true
+
+  /loader-utils/2.0.2:
+    resolution: {integrity: sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==}
+    engines: {node: '>=8.9.0'}
+    dependencies:
+      big.js: 5.2.2
+      emojis-list: 3.0.0
+      json5: 2.2.1
+    dev: true
+
+  /locate-path/3.0.0:
+    resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
+    engines: {node: '>=6'}
+    dependencies:
+      p-locate: 3.0.0
+      path-exists: 3.0.0
+    dev: true
+
+  /locate-path/5.0.0:
+    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-locate: 4.1.0
+    dev: true
+
+  /lodash.merge/4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+    dev: true
+
+  /lodash.truncate/4.4.2:
+    resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
+    dev: true
+
+  /lodash/4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+    dev: true
+
+  /lru-cache/5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+    dependencies:
+      yallist: 3.1.1
+    dev: true
+
+  /lru-cache/6.0.0:
+    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+    engines: {node: '>=10'}
+    dependencies:
+      yallist: 4.0.0
+    dev: true
+
+  /make-dir/2.1.0:
+    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
+    engines: {node: '>=6'}
+    dependencies:
+      pify: 4.0.1
+      semver: 5.7.1
+    dev: true
+
+  /make-dir/3.1.0:
+    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+    engines: {node: '>=8'}
+    dependencies:
+      semver: 6.3.0
+    dev: true
+
+  /make-error/1.3.6:
+    resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
+    dev: true
+
+  /makeerror/1.0.12:
+    resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+    dependencies:
+      tmpl: 1.0.5
+    dev: true
+
+  /map-cache/0.2.2:
+    resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /map-visit/1.0.0:
+    resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      object-visit: 1.0.1
+    dev: true
+
+  /md5.js/1.3.5:
+    resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
+    dependencies:
+      hash-base: 3.1.0
+      inherits: 2.0.4
+      safe-buffer: 5.2.1
+    dev: true
+
+  /memory-fs/0.4.1:
+    resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==}
+    dependencies:
+      errno: 0.1.8
+      readable-stream: 2.3.7
+    dev: true
+
+  /memory-fs/0.5.0:
+    resolution: {integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==}
+    engines: {node: '>=4.3.0 <5.0.0 || >=5.10'}
+    dependencies:
+      errno: 0.1.8
+      readable-stream: 2.3.7
+    dev: true
+
+  /merge-stream/2.0.0:
+    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+    dev: true
+
+  /micromatch/3.1.10:
+    resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-diff: 4.0.0
+      array-unique: 0.3.2
+      braces: 2.3.2
+      define-property: 2.0.2
+      extend-shallow: 3.0.2
+      extglob: 2.0.4
+      fragment-cache: 0.2.1
+      kind-of: 6.0.3
+      nanomatch: 1.2.13
+      object.pick: 1.3.0
+      regex-not: 1.0.2
+      snapdragon: 0.8.2
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /micromatch/3.1.10_supports-color@6.1.0:
+    resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-diff: 4.0.0
+      array-unique: 0.3.2
+      braces: 2.3.2_supports-color@6.1.0
+      define-property: 2.0.2
+      extend-shallow: 3.0.2
+      extglob: 2.0.4_supports-color@6.1.0
+      fragment-cache: 0.2.1
+      kind-of: 6.0.3
+      nanomatch: 1.2.13_supports-color@6.1.0
+      object.pick: 1.3.0
+      regex-not: 1.0.2
+      snapdragon: 0.8.2_supports-color@6.1.0
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /micromatch/4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      braces: 3.0.2
+      picomatch: 2.3.1
+    dev: true
+
+  /miller-rabin/4.0.1:
+    resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==}
+    hasBin: true
+    dependencies:
+      bn.js: 4.12.0
+      brorand: 1.1.0
+    dev: true
+
+  /mime-db/1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /mime-types/2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      mime-db: 1.52.0
+    dev: true
+
+  /mimic-fn/2.1.0:
+    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /mimic-response/2.1.0:
+    resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /minimalistic-assert/1.0.1:
+    resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
+    dev: true
+
+  /minimalistic-crypto-utils/1.0.1:
+    resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
+    dev: true
+
+  /minimatch/3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+    dependencies:
+      brace-expansion: 1.1.11
+    dev: true
+
+  /minimist/1.2.6:
+    resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
+    dev: true
+
+  /minipass-collect/1.0.2:
+    resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.1.6
+    dev: true
+
+  /minipass-flush/1.0.5:
+    resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.1.6
+    dev: true
+
+  /minipass-pipeline/1.2.4:
+    resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
+    engines: {node: '>=8'}
+    dependencies:
+      minipass: 3.1.6
+    dev: true
+
+  /minipass/3.1.6:
+    resolution: {integrity: sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      yallist: 4.0.0
+    dev: true
+
+  /minizlib/2.1.2:
+    resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.1.6
+      yallist: 4.0.0
+    dev: true
+
+  /mississippi/3.0.0:
+    resolution: {integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==}
+    engines: {node: '>=4.0.0'}
+    dependencies:
+      concat-stream: 1.6.2
+      duplexify: 3.7.1
+      end-of-stream: 1.4.4
+      flush-write-stream: 1.1.1
+      from2: 2.3.0
+      parallel-transform: 1.2.0
+      pump: 3.0.0
+      pumpify: 1.5.1
+      stream-each: 1.2.3
+      through2: 2.0.5
+    dev: true
+
+  /mixin-deep/1.3.2:
+    resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      for-in: 1.0.2
+      is-extendable: 1.0.1
+    dev: true
+
+  /mkdirp/0.5.6:
+    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+    hasBin: true
+    dependencies:
+      minimist: 1.2.6
+    dev: true
+
+  /mkdirp/1.0.4:
+    resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dev: true
+
+  /move-concurrently/1.0.1:
+    resolution: {integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==}
+    dependencies:
+      aproba: 1.2.0
+      copy-concurrently: 1.0.5
+      fs-write-stream-atomic: 1.0.10
+      mkdirp: 0.5.6
+      rimraf: 2.7.1
+      run-queue: 1.0.3
+    dev: true
+
+  /ms/2.0.0:
+    resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
+    dev: true
+
+  /ms/2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+    dev: true
+
+  /nan/2.16.0:
+    resolution: {integrity: sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==}
+    dev: true
+
+  /nanomatch/1.2.13:
+    resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-diff: 4.0.0
+      array-unique: 0.3.2
+      define-property: 2.0.2
+      extend-shallow: 3.0.2
+      fragment-cache: 0.2.1
+      is-windows: 1.0.2
+      kind-of: 6.0.3
+      object.pick: 1.3.0
+      regex-not: 1.0.2
+      snapdragon: 0.8.2
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /nanomatch/1.2.13_supports-color@6.1.0:
+    resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-diff: 4.0.0
+      array-unique: 0.3.2
+      define-property: 2.0.2
+      extend-shallow: 3.0.2
+      fragment-cache: 0.2.1
+      is-windows: 1.0.2
+      kind-of: 6.0.3
+      object.pick: 1.3.0
+      regex-not: 1.0.2
+      snapdragon: 0.8.2_supports-color@6.1.0
+      to-regex: 3.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /natural-compare/1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+    dev: true
+
+  /neo-async/2.6.2:
+    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+    dev: true
+
+  /nice-try/1.0.5:
+    resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
+    dev: true
+
+  /node-fetch/2.6.7:
+    resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+    dependencies:
+      whatwg-url: 5.0.0
+    dev: true
+
+  /node-int64/0.4.0:
+    resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+    dev: true
+
+  /node-libs-browser/2.2.1:
+    resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==}
+    dependencies:
+      assert: 1.5.0
+      browserify-zlib: 0.2.0
+      buffer: 4.9.2
+      console-browserify: 1.2.0
+      constants-browserify: 1.0.0
+      crypto-browserify: 3.12.0
+      domain-browser: 1.2.0
+      events: 3.3.0
+      https-browserify: 1.0.0
+      os-browserify: 0.3.0
+      path-browserify: 0.0.1
+      process: 0.11.10
+      punycode: 1.4.1
+      querystring-es3: 0.2.1
+      readable-stream: 2.3.7
+      stream-browserify: 2.0.2
+      stream-http: 2.8.3
+      string_decoder: 1.3.0
+      timers-browserify: 2.0.12
+      tty-browserify: 0.0.0
+      url: 0.11.0
+      util: 0.11.1
+      vm-browserify: 1.1.2
+    dev: true
+
+  /node-notifier/8.0.2:
+    resolution: {integrity: sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==}
+    requiresBuild: true
+    dependencies:
+      growly: 1.3.0
+      is-wsl: 2.2.0
+      semver: 7.3.7
+      shellwords: 0.1.1
+      uuid: 8.3.2
+      which: 2.0.2
+    dev: true
+    optional: true
+
+  /node-releases/2.0.5:
+    resolution: {integrity: sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==}
+    dev: true
+
+  /nopt/5.0.0:
+    resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
+    engines: {node: '>=6'}
+    hasBin: true
+    dependencies:
+      abbrev: 1.1.1
+    dev: true
+
+  /normalize-package-data/2.5.0:
+    resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
+    dependencies:
+      hosted-git-info: 2.8.9
+      resolve: 1.22.0
+      semver: 5.7.1
+      validate-npm-package-license: 3.0.4
+    dev: true
+
+  /normalize-path/2.1.1:
+    resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      remove-trailing-separator: 1.1.0
+    dev: true
+
+  /normalize-path/3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /npm-run-path/2.0.2:
+    resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
+    engines: {node: '>=4'}
+    dependencies:
+      path-key: 2.0.1
+    dev: true
+
+  /npm-run-path/4.0.1:
+    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+    engines: {node: '>=8'}
+    dependencies:
+      path-key: 3.1.1
+    dev: true
+
+  /npmlog/5.0.1:
+    resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
+    dependencies:
+      are-we-there-yet: 2.0.0
+      console-control-strings: 1.1.0
+      gauge: 3.0.2
+      set-blocking: 2.0.0
+    dev: true
+
+  /nwsapi/2.2.0:
+    resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==}
+    dev: true
+
+  /object-assign/4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /object-copy/0.1.0:
+    resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      copy-descriptor: 0.1.1
+      define-property: 0.2.5
+      kind-of: 3.2.2
+    dev: true
+
+  /object-visit/1.0.1:
+    resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      isobject: 3.0.1
+    dev: true
+
+  /object.pick/1.3.0:
+    resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      isobject: 3.0.1
+    dev: true
+
+  /once/1.4.0:
+    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+    dependencies:
+      wrappy: 1.0.2
+    dev: true
+
+  /onetime/5.1.2:
+    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+    engines: {node: '>=6'}
+    dependencies:
+      mimic-fn: 2.1.0
+    dev: true
+
+  /optionator/0.8.3:
+    resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.3.0
+      prelude-ls: 1.1.2
+      type-check: 0.3.2
+      word-wrap: 1.2.3
+    dev: true
+
+  /optionator/0.9.1:
+    resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.3
+    dev: true
+
+  /os-browserify/0.3.0:
+    resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==}
+    dev: true
+
+  /p-each-series/2.2.0:
+    resolution: {integrity: sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /p-finally/1.0.0:
+    resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /p-limit/2.3.0:
+    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+    engines: {node: '>=6'}
+    dependencies:
+      p-try: 2.2.0
+    dev: true
+
+  /p-limit/3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      yocto-queue: 0.1.0
+    dev: true
+
+  /p-locate/3.0.0:
+    resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      p-limit: 2.3.0
+    dev: true
+
+  /p-locate/4.1.0:
+    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-limit: 2.3.0
+    dev: true
+
+  /p-map/4.0.0:
+    resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      aggregate-error: 3.1.0
+    dev: true
+
+  /p-try/2.2.0:
+    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /pako/1.0.11:
+    resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+    dev: true
+
+  /parallel-transform/1.2.0:
+    resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==}
+    dependencies:
+      cyclist: 1.0.1
+      inherits: 2.0.4
+      readable-stream: 2.3.7
+    dev: true
+
+  /parent-module/1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+    dependencies:
+      callsites: 3.1.0
+    dev: true
+
+  /parse-asn1/5.1.6:
+    resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==}
+    dependencies:
+      asn1.js: 5.4.1
+      browserify-aes: 1.2.0
+      evp_bytestokey: 1.0.3
+      pbkdf2: 3.1.2
+      safe-buffer: 5.2.1
+    dev: true
+
+  /parse-json/5.2.0:
+    resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@babel/code-frame': 7.16.7
+      error-ex: 1.3.2
+      json-parse-even-better-errors: 2.3.1
+      lines-and-columns: 1.2.4
+    dev: true
+
+  /parse-passwd/1.0.0:
+    resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /parse5/6.0.1:
+    resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+    dev: true
+
+  /pascalcase/0.1.1:
+    resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /path-browserify/0.0.1:
+    resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==}
+    dev: true
+
+  /path-dirname/1.0.2:
+    resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==}
+    dev: true
+    optional: true
+
+  /path-exists/3.0.0:
+    resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /path-exists/4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-is-absolute/1.0.1:
+    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /path-key/2.0.1:
+    resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /path-key/3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-parse/1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+    dev: true
+
+  /pbkdf2/3.1.2:
+    resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
+    engines: {node: '>=0.12'}
+    dependencies:
+      create-hash: 1.2.0
+      create-hmac: 1.1.7
+      ripemd160: 2.0.2
+      safe-buffer: 5.2.1
+      sha.js: 2.4.11
+    dev: true
+
+  /picocolors/1.0.0:
+    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+    dev: true
+
+  /picomatch/2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+    dev: true
+
+  /pify/4.0.1:
+    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /pirates/4.0.5:
+    resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /pkg-dir/3.0.0:
+    resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
+    engines: {node: '>=6'}
+    dependencies:
+      find-up: 3.0.0
+    dev: true
+
+  /pkg-dir/4.2.0:
+    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      find-up: 4.1.0
+    dev: true
+
+  /posix-character-classes/0.1.1:
+    resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /prelude-ls/1.1.2:
+    resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /prelude-ls/1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /prettier-linter-helpers/1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      fast-diff: 1.2.0
+    dev: true
+
+  /prettier/2.6.0:
+    resolution: {integrity: sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dev: true
+
+  /pretty-format/26.6.2:
+    resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==}
+    engines: {node: '>= 10'}
+    dependencies:
+      '@jest/types': 26.6.2
+      ansi-regex: 5.0.1
+      ansi-styles: 4.3.0
+      react-is: 17.0.2
+    dev: true
+
+  /process-nextick-args/2.0.1:
+    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+    dev: true
+
+  /process/0.11.10:
+    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+    engines: {node: '>= 0.6.0'}
+    dev: true
+
+  /progress/2.0.3:
+    resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
+    engines: {node: '>=0.4.0'}
+    dev: true
+
+  /promise-inflight/1.0.1_bluebird@3.7.2:
+    resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
+    peerDependencies:
+      bluebird: '*'
+    peerDependenciesMeta:
+      bluebird:
+        optional: true
+    dependencies:
+      bluebird: 3.7.2
+    dev: true
+
+  /prompts/2.4.2:
+    resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+    engines: {node: '>= 6'}
+    dependencies:
+      kleur: 3.0.3
+      sisteransi: 1.0.5
+    dev: true
+
+  /prr/1.0.1:
+    resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+    dev: true
+
+  /psl/1.8.0:
+    resolution: {integrity: sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==}
+    dev: true
+
+  /public-encrypt/4.0.3:
+    resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==}
+    dependencies:
+      bn.js: 4.12.0
+      browserify-rsa: 4.1.0
+      create-hash: 1.2.0
+      parse-asn1: 5.1.6
+      randombytes: 2.1.0
+      safe-buffer: 5.2.1
+    dev: true
+
+  /pump/2.0.1:
+    resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
+    dependencies:
+      end-of-stream: 1.4.4
+      once: 1.4.0
+    dev: true
+
+  /pump/3.0.0:
+    resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
+    dependencies:
+      end-of-stream: 1.4.4
+      once: 1.4.0
+    dev: true
+
+  /pumpify/1.5.1:
+    resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
+    dependencies:
+      duplexify: 3.7.1
+      inherits: 2.0.4
+      pump: 2.0.1
+    dev: true
+
+  /punycode/1.3.2:
+    resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==}
+    dev: true
+
+  /punycode/1.4.1:
+    resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
+    dev: true
+
+  /punycode/2.1.1:
+    resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /querystring-es3/0.2.1:
+    resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==}
+    engines: {node: '>=0.4.x'}
+    dev: true
+
+  /querystring/0.2.0:
+    resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==}
+    engines: {node: '>=0.4.x'}
+    deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
+    dev: true
+
+  /randombytes/2.1.0:
+    resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+    dependencies:
+      safe-buffer: 5.2.1
+    dev: true
+
+  /randomfill/1.0.4:
+    resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==}
+    dependencies:
+      randombytes: 2.1.0
+      safe-buffer: 5.2.1
+    dev: true
+
+  /react-is/17.0.2:
+    resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+    dev: true
+
+  /read-pkg-up/7.0.1:
+    resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
+    engines: {node: '>=8'}
+    dependencies:
+      find-up: 4.1.0
+      read-pkg: 5.2.0
+      type-fest: 0.8.1
+    dev: true
+
+  /read-pkg/5.2.0:
+    resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@types/normalize-package-data': 2.4.1
+      normalize-package-data: 2.5.0
+      parse-json: 5.2.0
+      type-fest: 0.6.0
+    dev: true
+
+  /readable-stream/2.3.7:
+    resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
+    dependencies:
+      core-util-is: 1.0.3
+      inherits: 2.0.4
+      isarray: 1.0.0
+      process-nextick-args: 2.0.1
+      safe-buffer: 5.1.2
+      string_decoder: 1.1.1
+      util-deprecate: 1.0.2
+    dev: true
+
+  /readable-stream/3.6.0:
+    resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
+    engines: {node: '>= 6'}
+    dependencies:
+      inherits: 2.0.4
+      string_decoder: 1.3.0
+      util-deprecate: 1.0.2
+    dev: true
+
+  /readdirp/2.2.1:
+    resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==}
+    engines: {node: '>=0.10'}
+    dependencies:
+      graceful-fs: 4.2.10
+      micromatch: 3.1.10
+      readable-stream: 2.3.7
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+    optional: true
+
+  /readdirp/3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+    dependencies:
+      picomatch: 2.3.1
+    dev: true
+    optional: true
+
+  /regex-not/1.0.2:
+    resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      extend-shallow: 3.0.2
+      safe-regex: 1.1.0
+    dev: true
+
+  /regexpp/3.2.0:
+    resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /remove-trailing-separator/1.1.0:
+    resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==}
+    dev: true
+
+  /repeat-element/1.1.4:
+    resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /repeat-string/1.6.1:
+    resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
+    engines: {node: '>=0.10'}
+    dev: true
+
+  /require-directory/2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /require-from-string/2.0.2:
+    resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /require-main-filename/2.0.0:
+    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+    dev: true
+
+  /resolve-cwd/2.0.0:
+    resolution: {integrity: sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==}
+    engines: {node: '>=4'}
+    dependencies:
+      resolve-from: 3.0.0
+    dev: true
+
+  /resolve-cwd/3.0.0:
+    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      resolve-from: 5.0.0
+    dev: true
+
+  /resolve-dir/1.0.1:
+    resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      expand-tilde: 2.0.2
+      global-modules: 1.0.0
+    dev: true
+
+  /resolve-from/3.0.0:
+    resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /resolve-from/4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /resolve-from/5.0.0:
+    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /resolve-url/0.2.1:
+    resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==}
+    deprecated: https://github.com/lydell/resolve-url#deprecated
+    dev: true
+
+  /resolve/1.22.0:
+    resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
+    hasBin: true
+    dependencies:
+      is-core-module: 2.9.0
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+    dev: true
+
+  /ret/0.1.15:
+    resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==}
+    engines: {node: '>=0.12'}
+    dev: true
+
+  /rimraf/2.7.1:
+    resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+    hasBin: true
+    dependencies:
+      glob: 7.2.3
+    dev: true
+
+  /rimraf/3.0.2:
+    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    hasBin: true
+    dependencies:
+      glob: 7.2.3
+    dev: true
+
+  /ripemd160/2.0.2:
+    resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
+    dependencies:
+      hash-base: 3.1.0
+      inherits: 2.0.4
+    dev: true
+
+  /rsvp/4.8.5:
+    resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==}
+    engines: {node: 6.* || >= 7.*}
+    dev: true
+
+  /run-queue/1.0.3:
+    resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==}
+    dependencies:
+      aproba: 1.2.0
+    dev: true
+
+  /safe-buffer/5.1.2:
+    resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+    dev: true
+
+  /safe-buffer/5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+    dev: true
+
+  /safe-regex/1.1.0:
+    resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==}
+    dependencies:
+      ret: 0.1.15
+    dev: true
+
+  /safer-buffer/2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+    dev: true
+
+  /sane/4.1.0:
+    resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+    deprecated: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
+    hasBin: true
+    dependencies:
+      '@cnakazawa/watch': 1.0.4
+      anymatch: 2.0.0
+      capture-exit: 2.0.0
+      exec-sh: 0.3.6
+      execa: 1.0.0
+      fb-watchman: 2.0.1
+      micromatch: 3.1.10
+      minimist: 1.2.6
+      walker: 1.0.8
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /saxes/5.0.1:
+    resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
+    engines: {node: '>=10'}
+    dependencies:
+      xmlchars: 2.2.0
+    dev: true
+
+  /schema-utils/1.0.0:
+    resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==}
+    engines: {node: '>= 4'}
+    dependencies:
+      ajv: 6.12.6
+      ajv-errors: 1.0.1_ajv@6.12.6
+      ajv-keywords: 3.5.2_ajv@6.12.6
+    dev: true
+
+  /schema-utils/3.1.1:
+    resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==}
+    engines: {node: '>= 10.13.0'}
+    dependencies:
+      '@types/json-schema': 7.0.11
+      ajv: 6.12.6
+      ajv-keywords: 3.5.2_ajv@6.12.6
+    dev: true
+
+  /semver/5.7.1:
+    resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
+    hasBin: true
+    dev: true
+
+  /semver/6.3.0:
+    resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+    hasBin: true
+    dev: true
+
+  /semver/7.3.7:
+    resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      lru-cache: 6.0.0
+    dev: true
+
+  /serialize-javascript/4.0.0:
+    resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==}
+    dependencies:
+      randombytes: 2.1.0
+    dev: true
+
+  /serialize-javascript/5.0.1:
+    resolution: {integrity: sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==}
+    dependencies:
+      randombytes: 2.1.0
+    dev: true
+
+  /set-blocking/2.0.0:
+    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+    dev: true
+
+  /set-value/2.0.1:
+    resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      extend-shallow: 2.0.1
+      is-extendable: 0.1.1
+      is-plain-object: 2.0.4
+      split-string: 3.1.0
+    dev: true
+
+  /setimmediate/1.0.5:
+    resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
+    dev: true
+
+  /sha.js/2.4.11:
+    resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
+    hasBin: true
+    dependencies:
+      inherits: 2.0.4
+      safe-buffer: 5.2.1
+    dev: true
+
+  /shebang-command/1.2.0:
+    resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      shebang-regex: 1.0.0
+    dev: true
+
+  /shebang-command/2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+    dependencies:
+      shebang-regex: 3.0.0
+    dev: true
+
+  /shebang-regex/1.0.0:
+    resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /shebang-regex/3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /shellwords/0.1.1:
+    resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==}
+    dev: true
+    optional: true
+
+  /signal-exit/3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+    dev: true
+
+  /simple-concat/1.0.1:
+    resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+    dev: true
+
+  /simple-get/3.1.1:
+    resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==}
+    dependencies:
+      decompress-response: 4.2.1
+      once: 1.4.0
+      simple-concat: 1.0.1
+    dev: true
+
+  /sisteransi/1.0.5:
+    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+    dev: true
+
+  /slash/3.0.0:
+    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /slice-ansi/4.0.0:
+    resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      astral-regex: 2.0.0
+      is-fullwidth-code-point: 3.0.0
+    dev: true
+
+  /snapdragon-node/2.1.1:
+    resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      define-property: 1.0.0
+      isobject: 3.0.1
+      snapdragon-util: 3.0.1
+    dev: true
+
+  /snapdragon-util/3.0.1:
+    resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      kind-of: 3.2.2
+    dev: true
+
+  /snapdragon/0.8.2:
+    resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      base: 0.11.2
+      debug: 2.6.9
+      define-property: 0.2.5
+      extend-shallow: 2.0.1
+      map-cache: 0.2.2
+      source-map: 0.5.7
+      source-map-resolve: 0.5.3
+      use: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /snapdragon/0.8.2_supports-color@6.1.0:
+    resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      base: 0.11.2
+      debug: 2.6.9_supports-color@6.1.0
+      define-property: 0.2.5
+      extend-shallow: 2.0.1
+      map-cache: 0.2.2
+      source-map: 0.5.7
+      source-map-resolve: 0.5.3
+      use: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /source-list-map/2.0.1:
+    resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
+    dev: true
+
+  /source-map-resolve/0.5.3:
+    resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==}
+    deprecated: See https://github.com/lydell/source-map-resolve#deprecated
+    dependencies:
+      atob: 2.1.2
+      decode-uri-component: 0.2.0
+      resolve-url: 0.2.1
+      source-map-url: 0.4.1
+      urix: 0.1.0
+    dev: true
+
+  /source-map-support/0.5.21:
+    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+    dependencies:
+      buffer-from: 1.1.2
+      source-map: 0.6.1
+    dev: true
+
+  /source-map-url/0.4.1:
+    resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==}
+    deprecated: See https://github.com/lydell/source-map-url#deprecated
+    dev: true
+
+  /source-map/0.5.7:
+    resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /source-map/0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /source-map/0.7.4:
+    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /spdx-correct/3.1.1:
+    resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
+    dependencies:
+      spdx-expression-parse: 3.0.1
+      spdx-license-ids: 3.0.11
+    dev: true
+
+  /spdx-exceptions/2.3.0:
+    resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
+    dev: true
+
+  /spdx-expression-parse/3.0.1:
+    resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
+    dependencies:
+      spdx-exceptions: 2.3.0
+      spdx-license-ids: 3.0.11
+    dev: true
+
+  /spdx-license-ids/3.0.11:
+    resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==}
+    dev: true
+
+  /split-string/3.1.0:
+    resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      extend-shallow: 3.0.2
+    dev: true
+
+  /sprintf-js/1.0.3:
+    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+    dev: true
+
+  /ssri/6.0.2:
+    resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==}
+    dependencies:
+      figgy-pudding: 3.5.2
+    dev: true
+
+  /ssri/8.0.1:
+    resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.1.6
+    dev: true
+
+  /stack-utils/2.0.5:
+    resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==}
+    engines: {node: '>=10'}
+    dependencies:
+      escape-string-regexp: 2.0.0
+    dev: true
+
+  /static-extend/0.1.2:
+    resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      define-property: 0.2.5
+      object-copy: 0.1.0
+    dev: true
+
+  /stream-browserify/2.0.2:
+    resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
+    dependencies:
+      inherits: 2.0.4
+      readable-stream: 2.3.7
+    dev: true
+
+  /stream-each/1.2.3:
+    resolution: {integrity: sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==}
+    dependencies:
+      end-of-stream: 1.4.4
+      stream-shift: 1.0.1
+    dev: true
+
+  /stream-http/2.8.3:
+    resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==}
+    dependencies:
+      builtin-status-codes: 3.0.0
+      inherits: 2.0.4
+      readable-stream: 2.3.7
+      to-arraybuffer: 1.0.1
+      xtend: 4.0.2
+    dev: true
+
+  /stream-shift/1.0.1:
+    resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
+    dev: true
+
+  /string-length/4.0.2:
+    resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      char-regex: 1.0.2
+      strip-ansi: 6.0.1
+    dev: true
+
+  /string-width/3.1.0:
+    resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==}
+    engines: {node: '>=6'}
+    dependencies:
+      emoji-regex: 7.0.3
+      is-fullwidth-code-point: 2.0.0
+      strip-ansi: 5.2.0
+    dev: true
+
+  /string-width/4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+    dev: true
+
+  /string_decoder/1.1.1:
+    resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+    dependencies:
+      safe-buffer: 5.1.2
+    dev: true
+
+  /string_decoder/1.3.0:
+    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+    dependencies:
+      safe-buffer: 5.2.1
+    dev: true
+
+  /strip-ansi/5.2.0:
+    resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==}
+    engines: {node: '>=6'}
+    dependencies:
+      ansi-regex: 4.1.1
+    dev: true
+
+  /strip-ansi/6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-regex: 5.0.1
+    dev: true
+
+  /strip-bom/4.0.0:
+    resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /strip-eof/1.0.0:
+    resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /strip-final-newline/2.0.0:
+    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /strip-json-comments/3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /supports-color/5.5.0:
+    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+    engines: {node: '>=4'}
+    dependencies:
+      has-flag: 3.0.0
+    dev: true
+
+  /supports-color/6.1.0:
+    resolution: {integrity: sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      has-flag: 3.0.0
+    dev: true
+
+  /supports-color/7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
+  /supports-hyperlinks/2.2.0:
+    resolution: {integrity: sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+      supports-color: 7.2.0
+    dev: true
+
+  /supports-preserve-symlinks-flag/1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /symbol-tree/3.2.4:
+    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+    dev: true
+
+  /table/6.8.0:
+    resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      ajv: 8.11.0
+      lodash.truncate: 4.4.2
+      slice-ansi: 4.0.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+    dev: true
+
+  /tapable/1.1.3:
+    resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /tar/6.1.11:
+    resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==}
+    engines: {node: '>= 10'}
+    dependencies:
+      chownr: 2.0.0
+      fs-minipass: 2.1.0
+      minipass: 3.1.6
+      minizlib: 2.1.2
+      mkdirp: 1.0.4
+      yallist: 4.0.0
+    dev: true
+
+  /terminal-link/2.1.1:
+    resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-escapes: 4.3.2
+      supports-hyperlinks: 2.2.0
+    dev: true
+
+  /terser-webpack-plugin/1.4.5_webpack@4.46.0:
+    resolution: {integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==}
+    engines: {node: '>= 6.9.0'}
+    peerDependencies:
+      webpack: ^4.0.0
+    dependencies:
+      cacache: 12.0.4
+      find-cache-dir: 2.1.0
+      is-wsl: 1.1.0
+      schema-utils: 1.0.0
+      serialize-javascript: 4.0.0
+      source-map: 0.6.1
+      terser: 4.8.0
+      webpack: 4.46.0_webpack-cli@3.3.12
+      webpack-sources: 1.4.3
+      worker-farm: 1.7.0
+    dev: true
+
+  /terser-webpack-plugin/4.2.3_webpack@4.46.0:
+    resolution: {integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==}
+    engines: {node: '>= 10.13.0'}
+    peerDependencies:
+      webpack: ^4.0.0 || ^5.0.0
+    dependencies:
+      cacache: 15.3.0
+      find-cache-dir: 3.3.2
+      jest-worker: 26.6.2
+      p-limit: 3.1.0
+      schema-utils: 3.1.1
+      serialize-javascript: 5.0.1
+      source-map: 0.6.1
+      terser: 5.14.0
+      webpack: 4.46.0_webpack-cli@3.3.12
+      webpack-sources: 1.4.3
+    transitivePeerDependencies:
+      - bluebird
+    dev: true
+
+  /terser/4.8.0:
+    resolution: {integrity: sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      acorn: 8.7.1
+      commander: 2.20.3
+      source-map: 0.6.1
+      source-map-support: 0.5.21
+    dev: true
+
+  /terser/5.14.0:
+    resolution: {integrity: sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      '@jridgewell/source-map': 0.3.2
+      acorn: 8.7.1
+      commander: 2.20.3
+      source-map-support: 0.5.21
+    dev: true
+
+  /test-exclude/6.0.0:
+    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@istanbuljs/schema': 0.1.3
+      glob: 7.2.3
+      minimatch: 3.1.2
+    dev: true
+
+  /text-table/0.2.0:
+    resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=}
+    dev: true
+
+  /throat/5.0.0:
+    resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==}
+    dev: true
+
+  /through2/2.0.5:
+    resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
+    dependencies:
+      readable-stream: 2.3.7
+      xtend: 4.0.2
+    dev: true
+
+  /timers-browserify/2.0.12:
+    resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==}
+    engines: {node: '>=0.6.0'}
+    dependencies:
+      setimmediate: 1.0.5
+    dev: true
+
+  /tmpl/1.0.5:
+    resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+    dev: true
+
+  /to-arraybuffer/1.0.1:
+    resolution: {integrity: sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=}
+    dev: true
+
+  /to-fast-properties/2.0.0:
+    resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=}
+    engines: {node: '>=4'}
+    dev: true
+
+  /to-object-path/0.3.0:
+    resolution: {integrity: sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      kind-of: 3.2.2
+    dev: true
+
+  /to-regex-range/2.1.1:
+    resolution: {integrity: sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-number: 3.0.0
+      repeat-string: 1.6.1
+    dev: true
+
+  /to-regex-range/5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      is-number: 7.0.0
+    dev: true
+
+  /to-regex/3.0.2:
+    resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      define-property: 2.0.2
+      extend-shallow: 3.0.2
+      regex-not: 1.0.2
+      safe-regex: 1.1.0
+    dev: true
+
+  /tough-cookie/4.0.0:
+    resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==}
+    engines: {node: '>=6'}
+    dependencies:
+      psl: 1.8.0
+      punycode: 2.1.1
+      universalify: 0.1.2
+    dev: true
+
+  /tr46/0.0.3:
+    resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
+    dev: true
+
+  /tr46/2.1.0:
+    resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
+    engines: {node: '>=8'}
+    dependencies:
+      punycode: 2.1.1
+    dev: true
+
+  /ts-jest/26.5.6_eee4nuuc45mdmx5rmbx7hzjjtq:
+    resolution: {integrity: sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA==}
+    engines: {node: '>= 10'}
+    hasBin: true
+    peerDependencies:
+      jest: '>=26 <27'
+      typescript: '>=3.8 <5.0'
+    dependencies:
+      bs-logger: 0.2.6
+      buffer-from: 1.1.2
+      fast-json-stable-stringify: 2.1.0
+      jest: 26.6.3_canvas@2.9.1
+      jest-util: 26.6.2
+      json5: 2.2.1
+      lodash: 4.17.21
+      make-error: 1.3.6
+      mkdirp: 1.0.4
+      semver: 7.3.7
+      typescript: 3.9.10
+      yargs-parser: 20.2.9
+    dev: true
+
+  /ts-loader/8.4.0_ezrl6vhczkqqw35uifasf5iyga:
+    resolution: {integrity: sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      typescript: '*'
+      webpack: '*'
+    dependencies:
+      chalk: 4.1.2
+      enhanced-resolve: 4.5.0
+      loader-utils: 2.0.2
+      micromatch: 4.0.5
+      semver: 7.3.7
+      typescript: 3.9.10
+      webpack: 4.46.0_webpack-cli@3.3.12
+    dev: true
+
+  /tslib/1.14.1:
+    resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+    dev: true
+
+  /tsutils/3.21.0_typescript@3.9.10:
+    resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
+    engines: {node: '>= 6'}
+    peerDependencies:
+      typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
+    dependencies:
+      tslib: 1.14.1
+      typescript: 3.9.10
+    dev: true
+
+  /tty-browserify/0.0.0:
+    resolution: {integrity: sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=}
+    dev: true
+
+  /type-check/0.3.2:
+    resolution: {integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.1.2
+    dev: true
+
+  /type-check/0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+    dev: true
+
+  /type-detect/4.0.8:
+    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /type-fest/0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /type-fest/0.21.3:
+    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /type-fest/0.6.0:
+    resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /type-fest/0.8.1:
+    resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /typedarray-to-buffer/3.1.5:
+    resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
+    dependencies:
+      is-typedarray: 1.0.0
+    dev: true
+
+  /typedarray/0.0.6:
+    resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=}
+    dev: true
+
+  /typescript/3.9.10:
+    resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==}
+    engines: {node: '>=4.2.0'}
+    hasBin: true
+    dev: true
+
+  /union-value/1.0.1:
+    resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      arr-union: 3.1.0
+      get-value: 2.0.6
+      is-extendable: 0.1.1
+      set-value: 2.0.1
+    dev: true
+
+  /unique-filename/1.1.1:
+    resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==}
+    dependencies:
+      unique-slug: 2.0.2
+    dev: true
+
+  /unique-slug/2.0.2:
+    resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==}
+    dependencies:
+      imurmurhash: 0.1.4
+    dev: true
+
+  /universalify/0.1.2:
+    resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+    engines: {node: '>= 4.0.0'}
+    dev: true
+
+  /unset-value/1.0.0:
+    resolution: {integrity: sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      has-value: 0.3.1
+      isobject: 3.0.1
+    dev: true
+
+  /upath/1.2.0:
+    resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
+    engines: {node: '>=4'}
+    dev: true
+    optional: true
+
+  /uri-js/4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+    dependencies:
+      punycode: 2.1.1
+    dev: true
+
+  /urix/0.1.0:
+    resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=}
+    deprecated: Please see https://github.com/lydell/urix#deprecated
+    dev: true
+
+  /url/0.11.0:
+    resolution: {integrity: sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=}
+    dependencies:
+      punycode: 1.3.2
+      querystring: 0.2.0
+    dev: true
+
+  /use/3.1.1:
+    resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /util-deprecate/1.0.2:
+    resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=}
+    dev: true
+
+  /util/0.10.3:
+    resolution: {integrity: sha1-evsa/lCAUkZInj23/g7TeTNqwPk=}
+    dependencies:
+      inherits: 2.0.1
+    dev: true
+
+  /util/0.11.1:
+    resolution: {integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==}
+    dependencies:
+      inherits: 2.0.3
+    dev: true
+
+  /uuid/8.3.2:
+    resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+    hasBin: true
+    dev: true
+    optional: true
+
+  /v8-compile-cache/2.3.0:
+    resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
+    dev: true
+
+  /v8-to-istanbul/7.1.2:
+    resolution: {integrity: sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==}
+    engines: {node: '>=10.10.0'}
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.4
+      convert-source-map: 1.8.0
+      source-map: 0.7.4
+    dev: true
+
+  /validate-npm-package-license/3.0.4:
+    resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+    dependencies:
+      spdx-correct: 3.1.1
+      spdx-expression-parse: 3.0.1
+    dev: true
+
+  /vm-browserify/1.1.2:
+    resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
+    dev: true
+
+  /w3c-hr-time/1.0.2:
+    resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
+    dependencies:
+      browser-process-hrtime: 1.0.0
+    dev: true
+
+  /w3c-xmlserializer/2.0.0:
+    resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==}
+    engines: {node: '>=10'}
+    dependencies:
+      xml-name-validator: 3.0.0
+    dev: true
+
+  /walker/1.0.8:
+    resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+    dependencies:
+      makeerror: 1.0.12
+    dev: true
+
+  /watchpack-chokidar2/2.0.1:
+    resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==}
+    requiresBuild: true
+    dependencies:
+      chokidar: 2.1.8
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+    optional: true
+
+  /watchpack/1.7.5:
+    resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==}
+    dependencies:
+      graceful-fs: 4.2.10
+      neo-async: 2.6.2
+    optionalDependencies:
+      chokidar: 3.5.3
+      watchpack-chokidar2: 2.0.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /webidl-conversions/3.0.1:
+    resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
+    dev: true
+
+  /webidl-conversions/5.0.0:
+    resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /webidl-conversions/6.1.0:
+    resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==}
+    engines: {node: '>=10.4'}
+    dev: true
+
+  /webpack-cli/3.3.12_webpack@4.46.0:
+    resolution: {integrity: sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==}
+    engines: {node: '>=6.11.5'}
+    hasBin: true
+    peerDependencies:
+      webpack: 4.x.x
+    dependencies:
+      chalk: 2.4.2
+      cross-spawn: 6.0.5
+      enhanced-resolve: 4.5.0
+      findup-sync: 3.0.0_supports-color@6.1.0
+      global-modules: 2.0.0
+      import-local: 2.0.0
+      interpret: 1.4.0
+      loader-utils: 1.4.0
+      supports-color: 6.1.0
+      v8-compile-cache: 2.3.0
+      webpack: 4.46.0_webpack-cli@3.3.12
+      yargs: 13.3.2
+    dev: true
+
+  /webpack-sources/1.4.3:
+    resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==}
+    dependencies:
+      source-list-map: 2.0.1
+      source-map: 0.6.1
+    dev: true
+
+  /webpack/4.46.0_webpack-cli@3.3.12:
+    resolution: {integrity: sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==}
+    engines: {node: '>=6.11.5'}
+    hasBin: true
+    peerDependencies:
+      webpack-cli: '*'
+      webpack-command: '*'
+    peerDependenciesMeta:
+      webpack-cli:
+        optional: true
+      webpack-command:
+        optional: true
+    dependencies:
+      '@webassemblyjs/ast': 1.9.0
+      '@webassemblyjs/helper-module-context': 1.9.0
+      '@webassemblyjs/wasm-edit': 1.9.0
+      '@webassemblyjs/wasm-parser': 1.9.0
+      acorn: 6.4.2
+      ajv: 6.12.6
+      ajv-keywords: 3.5.2_ajv@6.12.6
+      chrome-trace-event: 1.0.3
+      enhanced-resolve: 4.5.0
+      eslint-scope: 4.0.3
+      json-parse-better-errors: 1.0.2
+      loader-runner: 2.4.0
+      loader-utils: 1.4.0
+      memory-fs: 0.4.1
+      micromatch: 3.1.10
+      mkdirp: 0.5.6
+      neo-async: 2.6.2
+      node-libs-browser: 2.2.1
+      schema-utils: 1.0.0
+      tapable: 1.1.3
+      terser-webpack-plugin: 1.4.5_webpack@4.46.0
+      watchpack: 1.7.5
+      webpack-cli: 3.3.12_webpack@4.46.0
+      webpack-sources: 1.4.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /whatwg-encoding/1.0.5:
+    resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
+    dependencies:
+      iconv-lite: 0.4.24
+    dev: true
+
+  /whatwg-mimetype/2.3.0:
+    resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
+    dev: true
+
+  /whatwg-url/5.0.0:
+    resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=}
+    dependencies:
+      tr46: 0.0.3
+      webidl-conversions: 3.0.1
+    dev: true
+
+  /whatwg-url/8.7.0:
+    resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
+    engines: {node: '>=10'}
+    dependencies:
+      lodash: 4.17.21
+      tr46: 2.1.0
+      webidl-conversions: 6.1.0
+    dev: true
+
+  /which-module/2.0.0:
+    resolution: {integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=}
+    dev: true
+
+  /which/1.3.1:
+    resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /which/2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /wide-align/1.1.5:
+    resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
+    dependencies:
+      string-width: 4.2.3
+    dev: true
+
+  /word-wrap/1.2.3:
+    resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /worker-farm/1.7.0:
+    resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==}
+    dependencies:
+      errno: 0.1.8
+    dev: true
+
+  /wrap-ansi/5.1.0:
+    resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==}
+    engines: {node: '>=6'}
+    dependencies:
+      ansi-styles: 3.2.1
+      string-width: 3.1.0
+      strip-ansi: 5.2.0
+    dev: true
+
+  /wrap-ansi/6.2.0:
+    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+    dev: true
+
+  /wrappy/1.0.2:
+    resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
+    dev: true
+
+  /write-file-atomic/3.0.3:
+    resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}
+    dependencies:
+      imurmurhash: 0.1.4
+      is-typedarray: 1.0.0
+      signal-exit: 3.0.7
+      typedarray-to-buffer: 3.1.5
+    dev: true
+
+  /ws/7.5.8:
+    resolution: {integrity: sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==}
+    engines: {node: '>=8.3.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: ^5.0.2
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+    dev: true
+
+  /xml-name-validator/3.0.0:
+    resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
+    dev: true
+
+  /xmlchars/2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+    dev: true
+
+  /xtend/4.0.2:
+    resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+    engines: {node: '>=0.4'}
+    dev: true
+
+  /y18n/4.0.3:
+    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
+    dev: true
+
+  /yallist/3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+    dev: true
+
+  /yallist/4.0.0:
+    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+    dev: true
+
+  /yargs-parser/13.1.2:
+    resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==}
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+    dev: true
+
+  /yargs-parser/18.1.3:
+    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+    dev: true
+
+  /yargs-parser/20.2.9:
+    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /yargs/13.3.2:
+    resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==}
+    dependencies:
+      cliui: 5.0.0
+      find-up: 3.0.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      require-main-filename: 2.0.0
+      set-blocking: 2.0.0
+      string-width: 3.1.0
+      which-module: 2.0.0
+      y18n: 4.0.3
+      yargs-parser: 13.1.2
+    dev: true
+
+  /yargs/15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+    dependencies:
+      cliui: 6.0.0
+      decamelize: 1.2.0
+      find-up: 4.1.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      require-main-filename: 2.0.0
+      set-blocking: 2.0.0
+      string-width: 4.2.3
+      which-module: 2.0.0
+      y18n: 4.0.3
+      yargs-parser: 18.1.3
+    dev: true
+
+  /yocto-queue/0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+    dev: true
diff --git a/src/editable.ts b/src/editable.ts
index 687c799b..c78b28c2 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -24,15 +24,16 @@ let _sourceTarget: SVGElement | null;
  *
  * @param container     HTML element for rendering visualization into.
  * @param sqore         Sqore object
+ * @param callbackFn    User-provided callback function triggered when circuit is changed
  *
  */
 
-const addEditable = (container: HTMLElement, sqore: Sqore): void => {
+const addEditable = (container: HTMLElement, sqore: Sqore, callbackFn?: () => void): void => {
     const context: Context = {
         container: container,
         operations: sqore.circuit.operations,
         wires: getWireElemsY(container),
-        renderFn: () => sqore.draw(container, 0, true),
+        renderFn: getRenderFn(container, sqore, callbackFn),
     };
     addCustomStyles(container);
     addDropzones(container);
@@ -74,7 +75,7 @@ const addCustomStyles = (container: HTMLElement): void => {
 const addDropzones = (container: HTMLElement): void => {
     const gateElems = getGateElems(container);
     gateElems.forEach((gateElem) => {
-        const { x, y, width, height } = gateElem.getBBox({ stroke: true });
+        const { x, y, width, height } = gateElem.getBBox();
         const dataId = getDataId(gateElem);
         gateElem.append(createLeftDropzone(x, y, height, dataId));
         gateElem.append(createRightDropzone(x, y, width, height, dataId));
@@ -147,7 +148,6 @@ const handleDropzoneMouseUp = (ev: MouseEvent, context: Context): void | false =
 
     // Not allow Measure gate to move vertically
     if (wireY != null && newGate.gate !== 'measure') {
-        console.log(wires[wireY]);
         // wires[wireY] returns qubit name (i.e: 'q0')
         // this remove 'q' and assign an index (i.e: 0)
         const index = Number(wires[wireY].slice(1));
@@ -249,6 +249,13 @@ const getGate = (dataId: string, operations: Operation[]): Operation => {
 };
 
 // Utilities
+const getRenderFn = (container: HTMLElement, sqore: Sqore, callbackFn?: () => void): (() => void) => {
+    return () => {
+        sqore.draw(container, 0, true, callbackFn);
+        if (callbackFn) callbackFn();
+    };
+};
+
 const getDataId = (element: Element): string => {
     return element.getAttribute('data-id') || '';
 };
@@ -320,7 +327,7 @@ const cursorCopy = (container: HTMLElement, value: boolean): void => {
 const exportedForTesting = {
     // addEditable
     addCustomStyles,
-    // addDropzones
+    addDropzones,
     addDocumentEvents,
     addDropzoneEvents,
     addMouseEvents,
@@ -334,6 +341,7 @@ const exportedForTesting = {
     getParent,
     getGate,
     getDataId,
+    getRenderFn,
     splitDataId,
     getWireElemsY,
     getWireElemY,
diff --git a/src/index.ts b/src/index.ts
index 8a9093bc..49973b3f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -12,17 +12,20 @@ import { StyleConfig } from './styles';
  * @param container HTML element for rendering visualization into.
  * @param style Custom visualization style.
  * @param renderDepth Initial layer depth at which to render gates.
+ * @param isEditable Optional value enabling/disabling editable feature
+ * @param callbackFn Optional function to trigger when changing elements in circuit
  */
 export const draw = (
     circuit: Circuit,
     container: HTMLElement,
     style: StyleConfig | string = {},
     renderDepth = 0,
-    editable = false,
+    isEditable?: boolean,
+    callbackFn?: () => void,
 ): void => {
     const sqore = new Sqore(circuit, style);
 
-    sqore.draw(container, renderDepth, editable);
+    sqore.draw(container, renderDepth, isEditable, callbackFn);
 };
 
 export { STYLES } from './styles';
diff --git a/src/sqore.ts b/src/sqore.ts
index bfb63e98..990ea3dc 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -56,8 +56,10 @@ export class Sqore {
      *
      * @param container HTML element for rendering visualization into.
      * @param renderDepth Initial layer depth at which to render gates.
+     * @param isEditable Optional value enabling/disabling editable feature
+     * @param callbackFn Optional function to trigger when changing elements in circuit
      */
-    draw(container: HTMLElement, renderDepth = 0, editable = false): void {
+    draw(container: HTMLElement, renderDepth = 0, isEditable?: boolean, callbackFn?: () => void): void {
         // Inject into container
         if (container == null) throw new Error(`Container not provided.`);
 
@@ -79,7 +81,7 @@ export class Sqore {
             const id: string = circuit.operations[0].dataAttributes['id'];
             this.expandOperation(circuit.operations, id);
         }
-        this.renderCircuit(container, circuit, editable);
+        this.renderCircuit(container, circuit, isEditable, callbackFn);
     }
 
     /**
@@ -106,17 +108,24 @@ export class Sqore {
      *
      * @param container HTML element for rendering visualization into.
      * @param circuit Circuit object to be rendered.
+     * @param isEditable Optional value enabling/disabling editable feature
+     * @param callbackFn Optional function to trigger when changing elements in circuit
      */
-    private renderCircuit(container: HTMLElement, circuit: Circuit, editable: boolean): void {
+    private renderCircuit(
+        container: HTMLElement,
+        circuit: Circuit,
+        isEditable?: boolean,
+        callbackFn?: () => void,
+    ): void {
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);
         container.innerHTML = '';
         container.appendChild(svg);
-        this.addGateClickHandlers(container, circuit, editable);
+        this.addGateClickHandlers(container, circuit, isEditable, callbackFn);
 
-        if (editable) {
-            addEditable(container, this);
+        if (isEditable) {
+            addEditable(container, this, callbackFn);
         }
     }
 
@@ -233,11 +242,18 @@ export class Sqore {
      *
      * @param container HTML element containing visualized circuit.
      * @param circuit Circuit to be visualized.
+     * @param isEditable Optional value enabling/disabling editable feature
+     * @param callbackFn Optional function to trigger when changing elements in circuit
      *
      */
-    private addGateClickHandlers(container: HTMLElement, circuit: Circuit, editable: boolean): void {
+    private addGateClickHandlers(
+        container: HTMLElement,
+        circuit: Circuit,
+        isEditable?: boolean,
+        callbackFn?: () => void,
+    ): void {
         this.addClassicalControlHandlers(container);
-        this.addZoomHandlers(container, circuit, editable);
+        this.addZoomHandlers(container, circuit, isEditable, callbackFn);
     }
 
     /**
@@ -293,9 +309,16 @@ export class Sqore {
      *
      * @param container HTML element containing visualized circuit.
      * @param circuit Circuit to be visualized.
+     * @param isEditable Optional value enabling/disabling editable feature
+     * @param callbackFn Optional function to trigger when changing elements in circuit
      *
      */
-    private addZoomHandlers(container: HTMLElement, circuit: Circuit, editable: boolean): void {
+    private addZoomHandlers(
+        container: HTMLElement,
+        circuit: Circuit,
+        isEditable?: boolean,
+        callbackFn?: () => void,
+    ): void {
         container.querySelectorAll('.gate .gate-control').forEach((ctrl) => {
             // Zoom in on clicked gate
             ctrl.addEventListener('click', (ev: Event) => {
@@ -306,7 +329,7 @@ export class Sqore {
                     } else if (ctrl.classList.contains('gate-expand')) {
                         this.expandOperation(circuit.operations, gateId);
                     }
-                    this.renderCircuit(container, circuit, editable);
+                    this.renderCircuit(container, circuit, isEditable, callbackFn);
                     ev.stopPropagation();
                 }
             });

From bf12e14dc3c92e312ceb10264ef55d0bae7d9c3c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 19:33:52 -0400
Subject: [PATCH 021/108] Remove pnpm-lock

---
 pnpm-lock.yaml | 6261 ------------------------------------------------
 1 file changed, 6261 deletions(-)
 delete mode 100644 pnpm-lock.yaml

diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
deleted file mode 100644
index fda0d629..00000000
--- a/pnpm-lock.yaml
+++ /dev/null
@@ -1,6261 +0,0 @@
-lockfileVersion: 5.4
-
-specifiers:
-  '@types/jest': ^26.0.4
-  '@types/prettier': 2.6.0
-  '@typescript-eslint/eslint-plugin': ^3.9.0
-  '@typescript-eslint/parser': ^3.9.0
-  canvas: ^2.6.1
-  eslint: ^7.6.0
-  eslint-config-prettier: ^6.11.0
-  eslint-plugin-jest: ^23.20.0
-  eslint-plugin-prettier: ^3.1.4
-  jest: ^26.6.3
-  prettier: 2.6.0
-  terser-webpack-plugin: ^4.1.0
-  ts-jest: ^26.1.2
-  ts-loader: ^8.0.2
-  typescript: ^3.9.6
-  webpack: ^4.46.0
-  webpack-cli: ^3.3.12
-
-devDependencies:
-  '@types/jest': 26.0.24
-  '@types/prettier': 2.6.0
-  '@typescript-eslint/eslint-plugin': 3.10.1_kxujzhw6vbtdri44htsqryf25e
-  '@typescript-eslint/parser': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
-  canvas: 2.9.1
-  eslint: 7.32.0
-  eslint-config-prettier: 6.15.0_eslint@7.32.0
-  eslint-plugin-jest: 23.20.0_2de3j2mqba4wgeuiaqz2k7syrm
-  eslint-plugin-prettier: 3.4.1_dnozcfbeb5wiozlftdrslmvmgu
-  jest: 26.6.3_canvas@2.9.1
-  prettier: 2.6.0
-  terser-webpack-plugin: 4.2.3_webpack@4.46.0
-  ts-jest: 26.5.6_eee4nuuc45mdmx5rmbx7hzjjtq
-  ts-loader: 8.4.0_ezrl6vhczkqqw35uifasf5iyga
-  typescript: 3.9.10
-  webpack: 4.46.0_webpack-cli@3.3.12
-  webpack-cli: 3.3.12_webpack@4.46.0
-
-packages:
-
-  /@ampproject/remapping/2.2.0:
-    resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/gen-mapping': 0.1.1
-      '@jridgewell/trace-mapping': 0.3.13
-    dev: true
-
-  /@babel/code-frame/7.12.11:
-    resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==}
-    dependencies:
-      '@babel/highlight': 7.17.12
-    dev: true
-
-  /@babel/code-frame/7.16.7:
-    resolution: {integrity: sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/highlight': 7.17.12
-    dev: true
-
-  /@babel/compat-data/7.17.10:
-    resolution: {integrity: sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/core/7.18.2:
-    resolution: {integrity: sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@ampproject/remapping': 2.2.0
-      '@babel/code-frame': 7.16.7
-      '@babel/generator': 7.18.2
-      '@babel/helper-compilation-targets': 7.18.2_@babel+core@7.18.2
-      '@babel/helper-module-transforms': 7.18.0
-      '@babel/helpers': 7.18.2
-      '@babel/parser': 7.18.4
-      '@babel/template': 7.16.7
-      '@babel/traverse': 7.18.2
-      '@babel/types': 7.18.4
-      convert-source-map: 1.8.0
-      debug: 4.3.4
-      gensync: 1.0.0-beta.2
-      json5: 2.2.1
-      semver: 6.3.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/generator/7.18.2:
-    resolution: {integrity: sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.18.4
-      '@jridgewell/gen-mapping': 0.3.1
-      jsesc: 2.5.2
-    dev: true
-
-  /@babel/helper-compilation-targets/7.18.2_@babel+core@7.18.2:
-    resolution: {integrity: sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/compat-data': 7.17.10
-      '@babel/core': 7.18.2
-      '@babel/helper-validator-option': 7.16.7
-      browserslist: 4.20.4
-      semver: 6.3.0
-    dev: true
-
-  /@babel/helper-environment-visitor/7.18.2:
-    resolution: {integrity: sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/helper-function-name/7.17.9:
-    resolution: {integrity: sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/template': 7.16.7
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@babel/helper-hoist-variables/7.16.7:
-    resolution: {integrity: sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@babel/helper-module-imports/7.16.7:
-    resolution: {integrity: sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@babel/helper-module-transforms/7.18.0:
-    resolution: {integrity: sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-environment-visitor': 7.18.2
-      '@babel/helper-module-imports': 7.16.7
-      '@babel/helper-simple-access': 7.18.2
-      '@babel/helper-split-export-declaration': 7.16.7
-      '@babel/helper-validator-identifier': 7.16.7
-      '@babel/template': 7.16.7
-      '@babel/traverse': 7.18.2
-      '@babel/types': 7.18.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/helper-plugin-utils/7.17.12:
-    resolution: {integrity: sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/helper-simple-access/7.18.2:
-    resolution: {integrity: sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@babel/helper-split-export-declaration/7.16.7:
-    resolution: {integrity: sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@babel/helper-validator-identifier/7.16.7:
-    resolution: {integrity: sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/helper-validator-option/7.16.7:
-    resolution: {integrity: sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /@babel/helpers/7.18.2:
-    resolution: {integrity: sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/template': 7.16.7
-      '@babel/traverse': 7.18.2
-      '@babel/types': 7.18.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/highlight/7.17.12:
-    resolution: {integrity: sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-validator-identifier': 7.16.7
-      chalk: 2.4.2
-      js-tokens: 4.0.0
-    dev: true
-
-  /@babel/parser/7.18.4:
-    resolution: {integrity: sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-    dependencies:
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@babel/plugin-syntax-async-generators/7.8.4_@babel+core@7.18.2:
-    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-bigint/7.8.3_@babel+core@7.18.2:
-    resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-class-properties/7.12.13_@babel+core@7.18.2:
-    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-import-meta/7.10.4_@babel+core@7.18.2:
-    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-json-strings/7.8.3_@babel+core@7.18.2:
-    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-logical-assignment-operators/7.10.4_@babel+core@7.18.2:
-    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-nullish-coalescing-operator/7.8.3_@babel+core@7.18.2:
-    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-numeric-separator/7.10.4_@babel+core@7.18.2:
-    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-object-rest-spread/7.8.3_@babel+core@7.18.2:
-    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-optional-catch-binding/7.8.3_@babel+core@7.18.2:
-    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-optional-chaining/7.8.3_@babel+core@7.18.2:
-    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/plugin-syntax-top-level-await/7.14.5_@babel+core@7.18.2:
-    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
-    engines: {node: '>=6.9.0'}
-    peerDependencies:
-      '@babel/core': ^7.0.0-0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/helper-plugin-utils': 7.17.12
-    dev: true
-
-  /@babel/template/7.16.7:
-    resolution: {integrity: sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/code-frame': 7.16.7
-      '@babel/parser': 7.18.4
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@babel/traverse/7.18.2:
-    resolution: {integrity: sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/code-frame': 7.16.7
-      '@babel/generator': 7.18.2
-      '@babel/helper-environment-visitor': 7.18.2
-      '@babel/helper-function-name': 7.17.9
-      '@babel/helper-hoist-variables': 7.16.7
-      '@babel/helper-split-export-declaration': 7.16.7
-      '@babel/parser': 7.18.4
-      '@babel/types': 7.18.4
-      debug: 4.3.4
-      globals: 11.12.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@babel/types/7.18.4:
-    resolution: {integrity: sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      '@babel/helper-validator-identifier': 7.16.7
-      to-fast-properties: 2.0.0
-    dev: true
-
-  /@bcoe/v8-coverage/0.2.3:
-    resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
-    dev: true
-
-  /@cnakazawa/watch/1.0.4:
-    resolution: {integrity: sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==}
-    engines: {node: '>=0.1.95'}
-    hasBin: true
-    dependencies:
-      exec-sh: 0.3.6
-      minimist: 1.2.6
-    dev: true
-
-  /@eslint/eslintrc/0.4.3:
-    resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    dependencies:
-      ajv: 6.12.6
-      debug: 4.3.4
-      espree: 7.3.1
-      globals: 13.15.0
-      ignore: 4.0.6
-      import-fresh: 3.3.0
-      js-yaml: 3.14.1
-      minimatch: 3.1.2
-      strip-json-comments: 3.1.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@gar/promisify/1.1.3:
-    resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==}
-    dev: true
-
-  /@humanwhocodes/config-array/0.5.0:
-    resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==}
-    engines: {node: '>=10.10.0'}
-    dependencies:
-      '@humanwhocodes/object-schema': 1.2.1
-      debug: 4.3.4
-      minimatch: 3.1.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@humanwhocodes/object-schema/1.2.1:
-    resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
-    dev: true
-
-  /@istanbuljs/load-nyc-config/1.1.0:
-    resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      camelcase: 5.3.1
-      find-up: 4.1.0
-      get-package-type: 0.1.0
-      js-yaml: 3.14.1
-      resolve-from: 5.0.0
-    dev: true
-
-  /@istanbuljs/schema/0.1.3:
-    resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /@jest/console/26.6.2:
-    resolution: {integrity: sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      chalk: 4.1.2
-      jest-message-util: 26.6.2
-      jest-util: 26.6.2
-      slash: 3.0.0
-    dev: true
-
-  /@jest/core/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-xvV1kKbhfUqFVuZ8Cyo+JPpipAHHAV3kcDBftiduK8EICXmTFddryy3P7NfZt8Pv37rA9nEJBKCCkglCPt/Xjw==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/console': 26.6.2
-      '@jest/reporters': 26.6.2
-      '@jest/test-result': 26.6.2
-      '@jest/transform': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      ansi-escapes: 4.3.2
-      chalk: 4.1.2
-      exit: 0.1.2
-      graceful-fs: 4.2.10
-      jest-changed-files: 26.6.2
-      jest-config: 26.6.3_canvas@2.9.1
-      jest-haste-map: 26.6.2
-      jest-message-util: 26.6.2
-      jest-regex-util: 26.0.0
-      jest-resolve: 26.6.2
-      jest-resolve-dependencies: 26.6.3
-      jest-runner: 26.6.3_canvas@2.9.1
-      jest-runtime: 26.6.3_canvas@2.9.1
-      jest-snapshot: 26.6.2
-      jest-util: 26.6.2
-      jest-validate: 26.6.2
-      jest-watcher: 26.6.2
-      micromatch: 4.0.5
-      p-each-series: 2.2.0
-      rimraf: 3.0.2
-      slash: 3.0.0
-      strip-ansi: 6.0.1
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - ts-node
-      - utf-8-validate
-    dev: true
-
-  /@jest/environment/26.6.2:
-    resolution: {integrity: sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/fake-timers': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      jest-mock: 26.6.2
-    dev: true
-
-  /@jest/fake-timers/26.6.2:
-    resolution: {integrity: sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      '@sinonjs/fake-timers': 6.0.1
-      '@types/node': 17.0.41
-      jest-message-util: 26.6.2
-      jest-mock: 26.6.2
-      jest-util: 26.6.2
-    dev: true
-
-  /@jest/globals/26.6.2:
-    resolution: {integrity: sha512-85Ltnm7HlB/KesBUuALwQ68YTU72w9H2xW9FjZ1eL1U3lhtefjjl5c2MiUbpXt/i6LaPRvoOFJ22yCBSfQ0JIA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/environment': 26.6.2
-      '@jest/types': 26.6.2
-      expect: 26.6.2
-    dev: true
-
-  /@jest/reporters/26.6.2:
-    resolution: {integrity: sha512-h2bW53APG4HvkOnVMo8q3QXa6pcaNt1HkwVsOPMBV6LD/q9oSpxNSYZQYkAnjdMjrJ86UuYeLo+aEZClV6opnw==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@bcoe/v8-coverage': 0.2.3
-      '@jest/console': 26.6.2
-      '@jest/test-result': 26.6.2
-      '@jest/transform': 26.6.2
-      '@jest/types': 26.6.2
-      chalk: 4.1.2
-      collect-v8-coverage: 1.0.1
-      exit: 0.1.2
-      glob: 7.2.3
-      graceful-fs: 4.2.10
-      istanbul-lib-coverage: 3.2.0
-      istanbul-lib-instrument: 4.0.3
-      istanbul-lib-report: 3.0.0
-      istanbul-lib-source-maps: 4.0.1
-      istanbul-reports: 3.1.4
-      jest-haste-map: 26.6.2
-      jest-resolve: 26.6.2
-      jest-util: 26.6.2
-      jest-worker: 26.6.2
-      slash: 3.0.0
-      source-map: 0.6.1
-      string-length: 4.0.2
-      terminal-link: 2.1.1
-      v8-to-istanbul: 7.1.2
-    optionalDependencies:
-      node-notifier: 8.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@jest/source-map/26.6.2:
-    resolution: {integrity: sha512-YwYcCwAnNmOVsZ8mr3GfnzdXDAl4LaenZP5z+G0c8bzC9/dugL8zRmxZzdoTl4IaS3CryS1uWnROLPFmb6lVvA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      callsites: 3.1.0
-      graceful-fs: 4.2.10
-      source-map: 0.6.1
-    dev: true
-
-  /@jest/test-result/26.6.2:
-    resolution: {integrity: sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/console': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/istanbul-lib-coverage': 2.0.4
-      collect-v8-coverage: 1.0.1
-    dev: true
-
-  /@jest/test-sequencer/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-YHlVIjP5nfEyjlrSr8t/YdNfU/1XEt7c5b4OxcXCjyRhjzLYu/rO69/WHPuYcbCWkz8kAeZVZp2N2+IOLLEPGw==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/test-result': 26.6.2
-      graceful-fs: 4.2.10
-      jest-haste-map: 26.6.2
-      jest-runner: 26.6.3_canvas@2.9.1
-      jest-runtime: 26.6.3_canvas@2.9.1
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - ts-node
-      - utf-8-validate
-    dev: true
-
-  /@jest/transform/26.6.2:
-    resolution: {integrity: sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@babel/core': 7.18.2
-      '@jest/types': 26.6.2
-      babel-plugin-istanbul: 6.1.1
-      chalk: 4.1.2
-      convert-source-map: 1.8.0
-      fast-json-stable-stringify: 2.1.0
-      graceful-fs: 4.2.10
-      jest-haste-map: 26.6.2
-      jest-regex-util: 26.0.0
-      jest-util: 26.6.2
-      micromatch: 4.0.5
-      pirates: 4.0.5
-      slash: 3.0.0
-      source-map: 0.6.1
-      write-file-atomic: 3.0.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@jest/types/26.6.2:
-    resolution: {integrity: sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@types/istanbul-lib-coverage': 2.0.4
-      '@types/istanbul-reports': 3.0.1
-      '@types/node': 17.0.41
-      '@types/yargs': 15.0.14
-      chalk: 4.1.2
-    dev: true
-
-  /@jridgewell/gen-mapping/0.1.1:
-    resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/set-array': 1.1.1
-      '@jridgewell/sourcemap-codec': 1.4.13
-    dev: true
-
-  /@jridgewell/gen-mapping/0.3.1:
-    resolution: {integrity: sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      '@jridgewell/set-array': 1.1.1
-      '@jridgewell/sourcemap-codec': 1.4.13
-      '@jridgewell/trace-mapping': 0.3.13
-    dev: true
-
-  /@jridgewell/resolve-uri/3.0.7:
-    resolution: {integrity: sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==}
-    engines: {node: '>=6.0.0'}
-    dev: true
-
-  /@jridgewell/set-array/1.1.1:
-    resolution: {integrity: sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==}
-    engines: {node: '>=6.0.0'}
-    dev: true
-
-  /@jridgewell/source-map/0.3.2:
-    resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
-    dependencies:
-      '@jridgewell/gen-mapping': 0.3.1
-      '@jridgewell/trace-mapping': 0.3.13
-    dev: true
-
-  /@jridgewell/sourcemap-codec/1.4.13:
-    resolution: {integrity: sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==}
-    dev: true
-
-  /@jridgewell/trace-mapping/0.3.13:
-    resolution: {integrity: sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==}
-    dependencies:
-      '@jridgewell/resolve-uri': 3.0.7
-      '@jridgewell/sourcemap-codec': 1.4.13
-    dev: true
-
-  /@mapbox/node-pre-gyp/1.0.9:
-    resolution: {integrity: sha512-aDF3S3rK9Q2gey/WAttUlISduDItz5BU3306M9Eyv6/oS40aMprnopshtlKTykxRNIBEZuRMaZAnbrQ4QtKGyw==}
-    hasBin: true
-    dependencies:
-      detect-libc: 2.0.1
-      https-proxy-agent: 5.0.1
-      make-dir: 3.1.0
-      node-fetch: 2.6.7
-      nopt: 5.0.0
-      npmlog: 5.0.1
-      rimraf: 3.0.2
-      semver: 7.3.7
-      tar: 6.1.11
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
-  /@npmcli/fs/1.1.1:
-    resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==}
-    dependencies:
-      '@gar/promisify': 1.1.3
-      semver: 7.3.7
-    dev: true
-
-  /@npmcli/move-file/1.1.2:
-    resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
-    engines: {node: '>=10'}
-    dependencies:
-      mkdirp: 1.0.4
-      rimraf: 3.0.2
-    dev: true
-
-  /@sinonjs/commons/1.8.3:
-    resolution: {integrity: sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==}
-    dependencies:
-      type-detect: 4.0.8
-    dev: true
-
-  /@sinonjs/fake-timers/6.0.1:
-    resolution: {integrity: sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==}
-    dependencies:
-      '@sinonjs/commons': 1.8.3
-    dev: true
-
-  /@tootallnate/once/1.1.2:
-    resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
-    engines: {node: '>= 6'}
-    dev: true
-
-  /@types/babel__core/7.1.19:
-    resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==}
-    dependencies:
-      '@babel/parser': 7.18.4
-      '@babel/types': 7.18.4
-      '@types/babel__generator': 7.6.4
-      '@types/babel__template': 7.4.1
-      '@types/babel__traverse': 7.17.1
-    dev: true
-
-  /@types/babel__generator/7.6.4:
-    resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
-    dependencies:
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@types/babel__template/7.4.1:
-    resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
-    dependencies:
-      '@babel/parser': 7.18.4
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@types/babel__traverse/7.17.1:
-    resolution: {integrity: sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==}
-    dependencies:
-      '@babel/types': 7.18.4
-    dev: true
-
-  /@types/eslint-visitor-keys/1.0.0:
-    resolution: {integrity: sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==}
-    dev: true
-
-  /@types/graceful-fs/4.1.5:
-    resolution: {integrity: sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==}
-    dependencies:
-      '@types/node': 17.0.41
-    dev: true
-
-  /@types/istanbul-lib-coverage/2.0.4:
-    resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
-    dev: true
-
-  /@types/istanbul-lib-report/3.0.0:
-    resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==}
-    dependencies:
-      '@types/istanbul-lib-coverage': 2.0.4
-    dev: true
-
-  /@types/istanbul-reports/3.0.1:
-    resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==}
-    dependencies:
-      '@types/istanbul-lib-report': 3.0.0
-    dev: true
-
-  /@types/jest/26.0.24:
-    resolution: {integrity: sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==}
-    dependencies:
-      jest-diff: 26.6.2
-      pretty-format: 26.6.2
-    dev: true
-
-  /@types/json-schema/7.0.11:
-    resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
-    dev: true
-
-  /@types/node/17.0.41:
-    resolution: {integrity: sha512-xA6drNNeqb5YyV5fO3OAEsnXLfO7uF0whiOfPTz5AeDo8KeZFmODKnvwPymMNO8qE/an8pVY/O50tig2SQCrGw==}
-    dev: true
-
-  /@types/normalize-package-data/2.4.1:
-    resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
-    dev: true
-
-  /@types/prettier/2.6.0:
-    resolution: {integrity: sha512-G/AdOadiZhnJp0jXCaBQU449W2h716OW/EoXeYkCytxKL06X1WCXB4DZpp8TpZ8eyIJVS1cw4lrlkkSYU21cDw==}
-    dev: true
-
-  /@types/stack-utils/2.0.1:
-    resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
-    dev: true
-
-  /@types/yargs-parser/21.0.0:
-    resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
-    dev: true
-
-  /@types/yargs/15.0.14:
-    resolution: {integrity: sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ==}
-    dependencies:
-      '@types/yargs-parser': 21.0.0
-    dev: true
-
-  /@typescript-eslint/eslint-plugin/3.10.1_kxujzhw6vbtdri44htsqryf25e:
-    resolution: {integrity: sha512-PQg0emRtzZFWq6PxBcdxRH3QIQiyFO3WCVpRL3fgj5oQS3CDs3AeAKfv4DxNhzn8ITdNJGJ4D3Qw8eAJf3lXeQ==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    peerDependencies:
-      '@typescript-eslint/parser': ^3.0.0
-      eslint: ^5.0.0 || ^6.0.0 || ^7.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/experimental-utils': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
-      '@typescript-eslint/parser': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
-      debug: 4.3.4
-      eslint: 7.32.0
-      functional-red-black-tree: 1.0.1
-      regexpp: 3.2.0
-      semver: 7.3.7
-      tsutils: 3.21.0_typescript@3.9.10
-      typescript: 3.9.10
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/experimental-utils/2.34.0_2de3j2mqba4wgeuiaqz2k7syrm:
-    resolution: {integrity: sha512-eS6FTkq+wuMJ+sgtuNTtcqavWXqsflWcfBnlYhg/nS4aZ1leewkXGbvBhaapn1q6qf4M71bsR1tez5JTRMuqwA==}
-    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
-    peerDependencies:
-      eslint: '*'
-    dependencies:
-      '@types/json-schema': 7.0.11
-      '@typescript-eslint/typescript-estree': 2.34.0_typescript@3.9.10
-      eslint: 7.32.0
-      eslint-scope: 5.1.1
-      eslint-utils: 2.1.0
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-    dev: true
-
-  /@typescript-eslint/experimental-utils/3.10.1_2de3j2mqba4wgeuiaqz2k7syrm:
-    resolution: {integrity: sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    peerDependencies:
-      eslint: '*'
-    dependencies:
-      '@types/json-schema': 7.0.11
-      '@typescript-eslint/types': 3.10.1
-      '@typescript-eslint/typescript-estree': 3.10.1_typescript@3.9.10
-      eslint: 7.32.0
-      eslint-scope: 5.1.1
-      eslint-utils: 2.1.0
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-    dev: true
-
-  /@typescript-eslint/parser/3.10.1_2de3j2mqba4wgeuiaqz2k7syrm:
-    resolution: {integrity: sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    peerDependencies:
-      eslint: ^5.0.0 || ^6.0.0 || ^7.0.0
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@types/eslint-visitor-keys': 1.0.0
-      '@typescript-eslint/experimental-utils': 3.10.1_2de3j2mqba4wgeuiaqz2k7syrm
-      '@typescript-eslint/types': 3.10.1
-      '@typescript-eslint/typescript-estree': 3.10.1_typescript@3.9.10
-      eslint: 7.32.0
-      eslint-visitor-keys: 1.3.0
-      typescript: 3.9.10
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/types/3.10.1:
-    resolution: {integrity: sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ==}
-    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
-    dev: true
-
-  /@typescript-eslint/typescript-estree/2.34.0_typescript@3.9.10:
-    resolution: {integrity: sha512-OMAr+nJWKdlVM9LOqCqh3pQQPwxHAN7Du8DR6dmwCrAmxtiXQnhHJ6tBNtf+cggqfo51SG/FCwnKhXCIM7hnVg==}
-    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      debug: 4.3.4
-      eslint-visitor-keys: 1.3.0
-      glob: 7.2.3
-      is-glob: 4.0.3
-      lodash: 4.17.21
-      semver: 7.3.7
-      tsutils: 3.21.0_typescript@3.9.10
-      typescript: 3.9.10
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/typescript-estree/3.10.1_typescript@3.9.10:
-    resolution: {integrity: sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    peerDependencies:
-      typescript: '*'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-    dependencies:
-      '@typescript-eslint/types': 3.10.1
-      '@typescript-eslint/visitor-keys': 3.10.1
-      debug: 4.3.4
-      glob: 7.2.3
-      is-glob: 4.0.3
-      lodash: 4.17.21
-      semver: 7.3.7
-      tsutils: 3.21.0_typescript@3.9.10
-      typescript: 3.9.10
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /@typescript-eslint/visitor-keys/3.10.1:
-    resolution: {integrity: sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ==}
-    engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1}
-    dependencies:
-      eslint-visitor-keys: 1.3.0
-    dev: true
-
-  /@webassemblyjs/ast/1.9.0:
-    resolution: {integrity: sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==}
-    dependencies:
-      '@webassemblyjs/helper-module-context': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/wast-parser': 1.9.0
-    dev: true
-
-  /@webassemblyjs/floating-point-hex-parser/1.9.0:
-    resolution: {integrity: sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==}
-    dev: true
-
-  /@webassemblyjs/helper-api-error/1.9.0:
-    resolution: {integrity: sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==}
-    dev: true
-
-  /@webassemblyjs/helper-buffer/1.9.0:
-    resolution: {integrity: sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==}
-    dev: true
-
-  /@webassemblyjs/helper-code-frame/1.9.0:
-    resolution: {integrity: sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==}
-    dependencies:
-      '@webassemblyjs/wast-printer': 1.9.0
-    dev: true
-
-  /@webassemblyjs/helper-fsm/1.9.0:
-    resolution: {integrity: sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==}
-    dev: true
-
-  /@webassemblyjs/helper-module-context/1.9.0:
-    resolution: {integrity: sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-    dev: true
-
-  /@webassemblyjs/helper-wasm-bytecode/1.9.0:
-    resolution: {integrity: sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==}
-    dev: true
-
-  /@webassemblyjs/helper-wasm-section/1.9.0:
-    resolution: {integrity: sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-buffer': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/wasm-gen': 1.9.0
-    dev: true
-
-  /@webassemblyjs/ieee754/1.9.0:
-    resolution: {integrity: sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==}
-    dependencies:
-      '@xtuc/ieee754': 1.2.0
-    dev: true
-
-  /@webassemblyjs/leb128/1.9.0:
-    resolution: {integrity: sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==}
-    dependencies:
-      '@xtuc/long': 4.2.2
-    dev: true
-
-  /@webassemblyjs/utf8/1.9.0:
-    resolution: {integrity: sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==}
-    dev: true
-
-  /@webassemblyjs/wasm-edit/1.9.0:
-    resolution: {integrity: sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-buffer': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/helper-wasm-section': 1.9.0
-      '@webassemblyjs/wasm-gen': 1.9.0
-      '@webassemblyjs/wasm-opt': 1.9.0
-      '@webassemblyjs/wasm-parser': 1.9.0
-      '@webassemblyjs/wast-printer': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wasm-gen/1.9.0:
-    resolution: {integrity: sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/ieee754': 1.9.0
-      '@webassemblyjs/leb128': 1.9.0
-      '@webassemblyjs/utf8': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wasm-opt/1.9.0:
-    resolution: {integrity: sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-buffer': 1.9.0
-      '@webassemblyjs/wasm-gen': 1.9.0
-      '@webassemblyjs/wasm-parser': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wasm-parser/1.9.0:
-    resolution: {integrity: sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-api-error': 1.9.0
-      '@webassemblyjs/helper-wasm-bytecode': 1.9.0
-      '@webassemblyjs/ieee754': 1.9.0
-      '@webassemblyjs/leb128': 1.9.0
-      '@webassemblyjs/utf8': 1.9.0
-    dev: true
-
-  /@webassemblyjs/wast-parser/1.9.0:
-    resolution: {integrity: sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/floating-point-hex-parser': 1.9.0
-      '@webassemblyjs/helper-api-error': 1.9.0
-      '@webassemblyjs/helper-code-frame': 1.9.0
-      '@webassemblyjs/helper-fsm': 1.9.0
-      '@xtuc/long': 4.2.2
-    dev: true
-
-  /@webassemblyjs/wast-printer/1.9.0:
-    resolution: {integrity: sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==}
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/wast-parser': 1.9.0
-      '@xtuc/long': 4.2.2
-    dev: true
-
-  /@xtuc/ieee754/1.2.0:
-    resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
-    dev: true
-
-  /@xtuc/long/4.2.2:
-    resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
-    dev: true
-
-  /abab/2.0.6:
-    resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
-    dev: true
-
-  /abbrev/1.1.1:
-    resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
-    dev: true
-
-  /acorn-globals/6.0.0:
-    resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
-    dependencies:
-      acorn: 7.4.1
-      acorn-walk: 7.2.0
-    dev: true
-
-  /acorn-jsx/5.3.2_acorn@7.4.1:
-    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
-    peerDependencies:
-      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
-    dependencies:
-      acorn: 7.4.1
-    dev: true
-
-  /acorn-walk/7.2.0:
-    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /acorn/6.4.2:
-    resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-    dev: true
-
-  /acorn/7.4.1:
-    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-    dev: true
-
-  /acorn/8.7.1:
-    resolution: {integrity: sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==}
-    engines: {node: '>=0.4.0'}
-    hasBin: true
-    dev: true
-
-  /agent-base/6.0.2:
-    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
-    engines: {node: '>= 6.0.0'}
-    dependencies:
-      debug: 4.3.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /aggregate-error/3.1.0:
-    resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
-    engines: {node: '>=8'}
-    dependencies:
-      clean-stack: 2.2.0
-      indent-string: 4.0.0
-    dev: true
-
-  /ajv-errors/1.0.1_ajv@6.12.6:
-    resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==}
-    peerDependencies:
-      ajv: '>=5.0.0'
-    dependencies:
-      ajv: 6.12.6
-    dev: true
-
-  /ajv-keywords/3.5.2_ajv@6.12.6:
-    resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
-    peerDependencies:
-      ajv: ^6.9.1
-    dependencies:
-      ajv: 6.12.6
-    dev: true
-
-  /ajv/6.12.6:
-    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
-    dependencies:
-      fast-deep-equal: 3.1.3
-      fast-json-stable-stringify: 2.1.0
-      json-schema-traverse: 0.4.1
-      uri-js: 4.4.1
-    dev: true
-
-  /ajv/8.11.0:
-    resolution: {integrity: sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==}
-    dependencies:
-      fast-deep-equal: 3.1.3
-      json-schema-traverse: 1.0.0
-      require-from-string: 2.0.2
-      uri-js: 4.4.1
-    dev: true
-
-  /ansi-colors/4.1.3:
-    resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /ansi-escapes/4.3.2:
-    resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      type-fest: 0.21.3
-    dev: true
-
-  /ansi-regex/4.1.1:
-    resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /ansi-regex/5.0.1:
-    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /ansi-styles/3.2.1:
-    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
-    engines: {node: '>=4'}
-    dependencies:
-      color-convert: 1.9.3
-    dev: true
-
-  /ansi-styles/4.3.0:
-    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
-    engines: {node: '>=8'}
-    dependencies:
-      color-convert: 2.0.1
-    dev: true
-
-  /anymatch/2.0.0:
-    resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==}
-    dependencies:
-      micromatch: 3.1.10
-      normalize-path: 2.1.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /anymatch/3.1.2:
-    resolution: {integrity: sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==}
-    engines: {node: '>= 8'}
-    dependencies:
-      normalize-path: 3.0.0
-      picomatch: 2.3.1
-    dev: true
-
-  /aproba/1.2.0:
-    resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==}
-    dev: true
-
-  /aproba/2.0.0:
-    resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
-    dev: true
-
-  /are-we-there-yet/2.0.0:
-    resolution: {integrity: sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==}
-    engines: {node: '>=10'}
-    dependencies:
-      delegates: 1.0.0
-      readable-stream: 3.6.0
-    dev: true
-
-  /argparse/1.0.10:
-    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
-    dependencies:
-      sprintf-js: 1.0.3
-    dev: true
-
-  /arr-diff/4.0.0:
-    resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /arr-flatten/1.1.0:
-    resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /arr-union/3.1.0:
-    resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /array-unique/0.3.2:
-    resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /asn1.js/5.4.1:
-    resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
-    dependencies:
-      bn.js: 4.12.0
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-      safer-buffer: 2.1.2
-    dev: true
-
-  /assert/1.5.0:
-    resolution: {integrity: sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==}
-    dependencies:
-      object-assign: 4.1.1
-      util: 0.10.3
-    dev: true
-
-  /assign-symbols/1.0.0:
-    resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /astral-regex/2.0.0:
-    resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /async-each/1.0.3:
-    resolution: {integrity: sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==}
-    dev: true
-    optional: true
-
-  /asynckit/0.4.0:
-    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
-    dev: true
-
-  /atob/2.1.2:
-    resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
-    engines: {node: '>= 4.5.0'}
-    hasBin: true
-    dev: true
-
-  /babel-jest/26.6.3_@babel+core@7.18.2:
-    resolution: {integrity: sha512-pl4Q+GAVOHwvjrck6jKjvmGhnO3jHX/xuB9d27f+EJZ/6k+6nMuPjorrYp7s++bKKdANwzElBWnLWaObvTnaZA==}
-    engines: {node: '>= 10.14.2'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@jest/transform': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/babel__core': 7.1.19
-      babel-plugin-istanbul: 6.1.1
-      babel-preset-jest: 26.6.2_@babel+core@7.18.2
-      chalk: 4.1.2
-      graceful-fs: 4.2.10
-      slash: 3.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /babel-plugin-istanbul/6.1.1:
-    resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@babel/helper-plugin-utils': 7.17.12
-      '@istanbuljs/load-nyc-config': 1.1.0
-      '@istanbuljs/schema': 0.1.3
-      istanbul-lib-instrument: 5.2.0
-      test-exclude: 6.0.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /babel-plugin-jest-hoist/26.6.2:
-    resolution: {integrity: sha512-PO9t0697lNTmcEHH69mdtYiOIkkOlj9fySqfO3K1eCcdISevLAE0xY59VLLUj0SoiPiTX/JU2CYFpILydUa5Lw==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@babel/template': 7.16.7
-      '@babel/types': 7.18.4
-      '@types/babel__core': 7.1.19
-      '@types/babel__traverse': 7.17.1
-    dev: true
-
-  /babel-preset-current-node-syntax/1.0.1_@babel+core@7.18.2:
-    resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/plugin-syntax-async-generators': 7.8.4_@babel+core@7.18.2
-      '@babel/plugin-syntax-bigint': 7.8.3_@babel+core@7.18.2
-      '@babel/plugin-syntax-class-properties': 7.12.13_@babel+core@7.18.2
-      '@babel/plugin-syntax-import-meta': 7.10.4_@babel+core@7.18.2
-      '@babel/plugin-syntax-json-strings': 7.8.3_@babel+core@7.18.2
-      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4_@babel+core@7.18.2
-      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3_@babel+core@7.18.2
-      '@babel/plugin-syntax-numeric-separator': 7.10.4_@babel+core@7.18.2
-      '@babel/plugin-syntax-object-rest-spread': 7.8.3_@babel+core@7.18.2
-      '@babel/plugin-syntax-optional-catch-binding': 7.8.3_@babel+core@7.18.2
-      '@babel/plugin-syntax-optional-chaining': 7.8.3_@babel+core@7.18.2
-      '@babel/plugin-syntax-top-level-await': 7.14.5_@babel+core@7.18.2
-    dev: true
-
-  /babel-preset-jest/26.6.2_@babel+core@7.18.2:
-    resolution: {integrity: sha512-YvdtlVm9t3k777c5NPQIv6cxFFFapys25HiUmuSgHwIZhfifweR5c5Sf5nwE3MAbfu327CYSvps8Yx6ANLyleQ==}
-    engines: {node: '>= 10.14.2'}
-    peerDependencies:
-      '@babel/core': ^7.0.0
-    dependencies:
-      '@babel/core': 7.18.2
-      babel-plugin-jest-hoist: 26.6.2
-      babel-preset-current-node-syntax: 1.0.1_@babel+core@7.18.2
-    dev: true
-
-  /balanced-match/1.0.2:
-    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
-    dev: true
-
-  /base/0.11.2:
-    resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      cache-base: 1.0.1
-      class-utils: 0.3.6
-      component-emitter: 1.3.0
-      define-property: 1.0.0
-      isobject: 3.0.1
-      mixin-deep: 1.3.2
-      pascalcase: 0.1.1
-    dev: true
-
-  /base64-js/1.5.1:
-    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
-    dev: true
-
-  /big.js/5.2.2:
-    resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
-    dev: true
-
-  /binary-extensions/1.13.1:
-    resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-    optional: true
-
-  /binary-extensions/2.2.0:
-    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
-    engines: {node: '>=8'}
-    dev: true
-    optional: true
-
-  /bindings/1.5.0:
-    resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
-    dependencies:
-      file-uri-to-path: 1.0.0
-    dev: true
-    optional: true
-
-  /bluebird/3.7.2:
-    resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
-    dev: true
-
-  /bn.js/4.12.0:
-    resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
-    dev: true
-
-  /bn.js/5.2.1:
-    resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==}
-    dev: true
-
-  /brace-expansion/1.1.11:
-    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
-    dependencies:
-      balanced-match: 1.0.2
-      concat-map: 0.0.1
-    dev: true
-
-  /braces/2.3.2:
-    resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-flatten: 1.1.0
-      array-unique: 0.3.2
-      extend-shallow: 2.0.1
-      fill-range: 4.0.0
-      isobject: 3.0.1
-      repeat-element: 1.1.4
-      snapdragon: 0.8.2
-      snapdragon-node: 2.1.1
-      split-string: 3.1.0
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /braces/2.3.2_supports-color@6.1.0:
-    resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-flatten: 1.1.0
-      array-unique: 0.3.2
-      extend-shallow: 2.0.1
-      fill-range: 4.0.0
-      isobject: 3.0.1
-      repeat-element: 1.1.4
-      snapdragon: 0.8.2_supports-color@6.1.0
-      snapdragon-node: 2.1.1
-      split-string: 3.1.0
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /braces/3.0.2:
-    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
-    engines: {node: '>=8'}
-    dependencies:
-      fill-range: 7.0.1
-    dev: true
-
-  /brorand/1.1.0:
-    resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==}
-    dev: true
-
-  /browser-process-hrtime/1.0.0:
-    resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
-    dev: true
-
-  /browserify-aes/1.2.0:
-    resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==}
-    dependencies:
-      buffer-xor: 1.0.3
-      cipher-base: 1.0.4
-      create-hash: 1.2.0
-      evp_bytestokey: 1.0.3
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /browserify-cipher/1.0.1:
-    resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==}
-    dependencies:
-      browserify-aes: 1.2.0
-      browserify-des: 1.0.2
-      evp_bytestokey: 1.0.3
-    dev: true
-
-  /browserify-des/1.0.2:
-    resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==}
-    dependencies:
-      cipher-base: 1.0.4
-      des.js: 1.0.1
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /browserify-rsa/4.1.0:
-    resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==}
-    dependencies:
-      bn.js: 5.2.1
-      randombytes: 2.1.0
-    dev: true
-
-  /browserify-sign/4.2.1:
-    resolution: {integrity: sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==}
-    dependencies:
-      bn.js: 5.2.1
-      browserify-rsa: 4.1.0
-      create-hash: 1.2.0
-      create-hmac: 1.1.7
-      elliptic: 6.5.4
-      inherits: 2.0.4
-      parse-asn1: 5.1.6
-      readable-stream: 3.6.0
-      safe-buffer: 5.2.1
-    dev: true
-
-  /browserify-zlib/0.2.0:
-    resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==}
-    dependencies:
-      pako: 1.0.11
-    dev: true
-
-  /browserslist/4.20.4:
-    resolution: {integrity: sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==}
-    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
-    hasBin: true
-    dependencies:
-      caniuse-lite: 1.0.30001352
-      electron-to-chromium: 1.4.150
-      escalade: 3.1.1
-      node-releases: 2.0.5
-      picocolors: 1.0.0
-    dev: true
-
-  /bs-logger/0.2.6:
-    resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==}
-    engines: {node: '>= 6'}
-    dependencies:
-      fast-json-stable-stringify: 2.1.0
-    dev: true
-
-  /bser/2.1.1:
-    resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
-    dependencies:
-      node-int64: 0.4.0
-    dev: true
-
-  /buffer-from/1.1.2:
-    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
-    dev: true
-
-  /buffer-xor/1.0.3:
-    resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==}
-    dev: true
-
-  /buffer/4.9.2:
-    resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==}
-    dependencies:
-      base64-js: 1.5.1
-      ieee754: 1.2.1
-      isarray: 1.0.0
-    dev: true
-
-  /builtin-status-codes/3.0.0:
-    resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
-    dev: true
-
-  /cacache/12.0.4:
-    resolution: {integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==}
-    dependencies:
-      bluebird: 3.7.2
-      chownr: 1.1.4
-      figgy-pudding: 3.5.2
-      glob: 7.2.3
-      graceful-fs: 4.2.10
-      infer-owner: 1.0.4
-      lru-cache: 5.1.1
-      mississippi: 3.0.0
-      mkdirp: 0.5.6
-      move-concurrently: 1.0.1
-      promise-inflight: 1.0.1_bluebird@3.7.2
-      rimraf: 2.7.1
-      ssri: 6.0.2
-      unique-filename: 1.1.1
-      y18n: 4.0.3
-    dev: true
-
-  /cacache/15.3.0:
-    resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==}
-    engines: {node: '>= 10'}
-    dependencies:
-      '@npmcli/fs': 1.1.1
-      '@npmcli/move-file': 1.1.2
-      chownr: 2.0.0
-      fs-minipass: 2.1.0
-      glob: 7.2.3
-      infer-owner: 1.0.4
-      lru-cache: 6.0.0
-      minipass: 3.1.6
-      minipass-collect: 1.0.2
-      minipass-flush: 1.0.5
-      minipass-pipeline: 1.2.4
-      mkdirp: 1.0.4
-      p-map: 4.0.0
-      promise-inflight: 1.0.1_bluebird@3.7.2
-      rimraf: 3.0.2
-      ssri: 8.0.1
-      tar: 6.1.11
-      unique-filename: 1.1.1
-    transitivePeerDependencies:
-      - bluebird
-    dev: true
-
-  /cache-base/1.0.1:
-    resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      collection-visit: 1.0.0
-      component-emitter: 1.3.0
-      get-value: 2.0.6
-      has-value: 1.0.0
-      isobject: 3.0.1
-      set-value: 2.0.1
-      to-object-path: 0.3.0
-      union-value: 1.0.1
-      unset-value: 1.0.0
-    dev: true
-
-  /callsites/3.1.0:
-    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /camelcase/5.3.1:
-    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /camelcase/6.3.0:
-    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /caniuse-lite/1.0.30001352:
-    resolution: {integrity: sha512-GUgH8w6YergqPQDGWhJGt8GDRnY0L/iJVQcU3eJ46GYf52R8tk0Wxp0PymuFVZboJYXGiCqwozAYZNRjVj6IcA==}
-    dev: true
-
-  /canvas/2.9.1:
-    resolution: {integrity: sha512-vSQti1uG/2gjv3x6QLOZw7TctfufaerTWbVe+NSduHxxLGB+qf3kFgQ6n66DSnuoINtVUjrLLIK2R+lxrBG07A==}
-    engines: {node: '>=6'}
-    requiresBuild: true
-    dependencies:
-      '@mapbox/node-pre-gyp': 1.0.9
-      nan: 2.16.0
-      simple-get: 3.1.1
-    transitivePeerDependencies:
-      - encoding
-      - supports-color
-    dev: true
-
-  /capture-exit/2.0.0:
-    resolution: {integrity: sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==}
-    engines: {node: 6.* || 8.* || >= 10.*}
-    dependencies:
-      rsvp: 4.8.5
-    dev: true
-
-  /chalk/2.4.2:
-    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
-    engines: {node: '>=4'}
-    dependencies:
-      ansi-styles: 3.2.1
-      escape-string-regexp: 1.0.5
-      supports-color: 5.5.0
-    dev: true
-
-  /chalk/4.1.2:
-    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-styles: 4.3.0
-      supports-color: 7.2.0
-    dev: true
-
-  /char-regex/1.0.2:
-    resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /chokidar/2.1.8:
-    resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==}
-    deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies
-    dependencies:
-      anymatch: 2.0.0
-      async-each: 1.0.3
-      braces: 2.3.2
-      glob-parent: 3.1.0
-      inherits: 2.0.4
-      is-binary-path: 1.0.1
-      is-glob: 4.0.3
-      normalize-path: 3.0.0
-      path-is-absolute: 1.0.1
-      readdirp: 2.2.1
-      upath: 1.2.0
-    optionalDependencies:
-      fsevents: 1.2.13
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /chokidar/3.5.3:
-    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
-    engines: {node: '>= 8.10.0'}
-    requiresBuild: true
-    dependencies:
-      anymatch: 3.1.2
-      braces: 3.0.2
-      glob-parent: 5.1.2
-      is-binary-path: 2.1.0
-      is-glob: 4.0.3
-      normalize-path: 3.0.0
-      readdirp: 3.6.0
-    optionalDependencies:
-      fsevents: 2.3.2
-    dev: true
-    optional: true
-
-  /chownr/1.1.4:
-    resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
-    dev: true
-
-  /chownr/2.0.0:
-    resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /chrome-trace-event/1.0.3:
-    resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
-    engines: {node: '>=6.0'}
-    dev: true
-
-  /ci-info/2.0.0:
-    resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
-    dev: true
-
-  /cipher-base/1.0.4:
-    resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==}
-    dependencies:
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /cjs-module-lexer/0.6.0:
-    resolution: {integrity: sha512-uc2Vix1frTfnuzxxu1Hp4ktSvM3QaI4oXl4ZUqL1wjTu/BGki9TrCWoqLTg/drR1KwAEarXuRFCG2Svr1GxPFw==}
-    dev: true
-
-  /class-utils/0.3.6:
-    resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-union: 3.1.0
-      define-property: 0.2.5
-      isobject: 3.0.1
-      static-extend: 0.1.2
-    dev: true
-
-  /clean-stack/2.2.0:
-    resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /cliui/5.0.0:
-    resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==}
-    dependencies:
-      string-width: 3.1.0
-      strip-ansi: 5.2.0
-      wrap-ansi: 5.1.0
-    dev: true
-
-  /cliui/6.0.0:
-    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
-    dependencies:
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-      wrap-ansi: 6.2.0
-    dev: true
-
-  /co/4.6.0:
-    resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
-    engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
-    dev: true
-
-  /collect-v8-coverage/1.0.1:
-    resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
-    dev: true
-
-  /collection-visit/1.0.0:
-    resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      map-visit: 1.0.0
-      object-visit: 1.0.1
-    dev: true
-
-  /color-convert/1.9.3:
-    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
-    dependencies:
-      color-name: 1.1.3
-    dev: true
-
-  /color-convert/2.0.1:
-    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
-    engines: {node: '>=7.0.0'}
-    dependencies:
-      color-name: 1.1.4
-    dev: true
-
-  /color-name/1.1.3:
-    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
-    dev: true
-
-  /color-name/1.1.4:
-    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
-    dev: true
-
-  /color-support/1.1.3:
-    resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
-    hasBin: true
-    dev: true
-
-  /combined-stream/1.0.8:
-    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
-    engines: {node: '>= 0.8'}
-    dependencies:
-      delayed-stream: 1.0.0
-    dev: true
-
-  /commander/2.20.3:
-    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
-    dev: true
-
-  /commondir/1.0.1:
-    resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
-    dev: true
-
-  /component-emitter/1.3.0:
-    resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
-    dev: true
-
-  /concat-map/0.0.1:
-    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
-    dev: true
-
-  /concat-stream/1.6.2:
-    resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==}
-    engines: {'0': node >= 0.8}
-    dependencies:
-      buffer-from: 1.1.2
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-      typedarray: 0.0.6
-    dev: true
-
-  /console-browserify/1.2.0:
-    resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==}
-    dev: true
-
-  /console-control-strings/1.1.0:
-    resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==}
-    dev: true
-
-  /constants-browserify/1.0.0:
-    resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==}
-    dev: true
-
-  /convert-source-map/1.8.0:
-    resolution: {integrity: sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==}
-    dependencies:
-      safe-buffer: 5.1.2
-    dev: true
-
-  /copy-concurrently/1.0.5:
-    resolution: {integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==}
-    dependencies:
-      aproba: 1.2.0
-      fs-write-stream-atomic: 1.0.10
-      iferr: 0.1.5
-      mkdirp: 0.5.6
-      rimraf: 2.7.1
-      run-queue: 1.0.3
-    dev: true
-
-  /copy-descriptor/0.1.1:
-    resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /core-util-is/1.0.3:
-    resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
-    dev: true
-
-  /create-ecdh/4.0.4:
-    resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
-    dependencies:
-      bn.js: 4.12.0
-      elliptic: 6.5.4
-    dev: true
-
-  /create-hash/1.2.0:
-    resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
-    dependencies:
-      cipher-base: 1.0.4
-      inherits: 2.0.4
-      md5.js: 1.3.5
-      ripemd160: 2.0.2
-      sha.js: 2.4.11
-    dev: true
-
-  /create-hmac/1.1.7:
-    resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==}
-    dependencies:
-      cipher-base: 1.0.4
-      create-hash: 1.2.0
-      inherits: 2.0.4
-      ripemd160: 2.0.2
-      safe-buffer: 5.2.1
-      sha.js: 2.4.11
-    dev: true
-
-  /cross-spawn/6.0.5:
-    resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
-    engines: {node: '>=4.8'}
-    dependencies:
-      nice-try: 1.0.5
-      path-key: 2.0.1
-      semver: 5.7.1
-      shebang-command: 1.2.0
-      which: 1.3.1
-    dev: true
-
-  /cross-spawn/7.0.3:
-    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
-    engines: {node: '>= 8'}
-    dependencies:
-      path-key: 3.1.1
-      shebang-command: 2.0.0
-      which: 2.0.2
-    dev: true
-
-  /crypto-browserify/3.12.0:
-    resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==}
-    dependencies:
-      browserify-cipher: 1.0.1
-      browserify-sign: 4.2.1
-      create-ecdh: 4.0.4
-      create-hash: 1.2.0
-      create-hmac: 1.1.7
-      diffie-hellman: 5.0.3
-      inherits: 2.0.4
-      pbkdf2: 3.1.2
-      public-encrypt: 4.0.3
-      randombytes: 2.1.0
-      randomfill: 1.0.4
-    dev: true
-
-  /cssom/0.3.8:
-    resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
-    dev: true
-
-  /cssom/0.4.4:
-    resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==}
-    dev: true
-
-  /cssstyle/2.3.0:
-    resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
-    engines: {node: '>=8'}
-    dependencies:
-      cssom: 0.3.8
-    dev: true
-
-  /cyclist/1.0.1:
-    resolution: {integrity: sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==}
-    dev: true
-
-  /data-urls/2.0.0:
-    resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      abab: 2.0.6
-      whatwg-mimetype: 2.3.0
-      whatwg-url: 8.7.0
-    dev: true
-
-  /debug/2.6.9:
-    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.0.0
-    dev: true
-
-  /debug/2.6.9_supports-color@6.1.0:
-    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.0.0
-      supports-color: 6.1.0
-    dev: true
-
-  /debug/4.3.4:
-    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
-    engines: {node: '>=6.0'}
-    peerDependencies:
-      supports-color: '*'
-    peerDependenciesMeta:
-      supports-color:
-        optional: true
-    dependencies:
-      ms: 2.1.2
-    dev: true
-
-  /decamelize/1.2.0:
-    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /decimal.js/10.3.1:
-    resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==}
-    dev: true
-
-  /decode-uri-component/0.2.0:
-    resolution: {integrity: sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==}
-    engines: {node: '>=0.10'}
-    dev: true
-
-  /decompress-response/4.2.1:
-    resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==}
-    engines: {node: '>=8'}
-    dependencies:
-      mimic-response: 2.1.0
-    dev: true
-
-  /deep-is/0.1.4:
-    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
-    dev: true
-
-  /deepmerge/4.2.2:
-    resolution: {integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /define-property/0.2.5:
-    resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-descriptor: 0.1.6
-    dev: true
-
-  /define-property/1.0.0:
-    resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-descriptor: 1.0.2
-    dev: true
-
-  /define-property/2.0.2:
-    resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-descriptor: 1.0.2
-      isobject: 3.0.1
-    dev: true
-
-  /delayed-stream/1.0.0:
-    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /delegates/1.0.0:
-    resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
-    dev: true
-
-  /des.js/1.0.1:
-    resolution: {integrity: sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==}
-    dependencies:
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-    dev: true
-
-  /detect-file/1.0.0:
-    resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /detect-libc/2.0.1:
-    resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /detect-newline/3.1.0:
-    resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /diff-sequences/26.6.2:
-    resolution: {integrity: sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==}
-    engines: {node: '>= 10.14.2'}
-    dev: true
-
-  /diffie-hellman/5.0.3:
-    resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
-    dependencies:
-      bn.js: 4.12.0
-      miller-rabin: 4.0.1
-      randombytes: 2.1.0
-    dev: true
-
-  /doctrine/3.0.0:
-    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      esutils: 2.0.3
-    dev: true
-
-  /domain-browser/1.2.0:
-    resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==}
-    engines: {node: '>=0.4', npm: '>=1.2'}
-    dev: true
-
-  /domexception/2.0.1:
-    resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==}
-    engines: {node: '>=8'}
-    dependencies:
-      webidl-conversions: 5.0.0
-    dev: true
-
-  /duplexify/3.7.1:
-    resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==}
-    dependencies:
-      end-of-stream: 1.4.4
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-      stream-shift: 1.0.1
-    dev: true
-
-  /electron-to-chromium/1.4.150:
-    resolution: {integrity: sha512-MP3oBer0X7ZeS9GJ0H6lmkn561UxiwOIY9TTkdxVY7lI9G6GVCKfgJaHaDcakwdKxBXA4T3ybeswH/WBIN/KTA==}
-    dev: true
-
-  /elliptic/6.5.4:
-    resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==}
-    dependencies:
-      bn.js: 4.12.0
-      brorand: 1.1.0
-      hash.js: 1.1.7
-      hmac-drbg: 1.0.1
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-      minimalistic-crypto-utils: 1.0.1
-    dev: true
-
-  /emittery/0.7.2:
-    resolution: {integrity: sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /emoji-regex/7.0.3:
-    resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==}
-    dev: true
-
-  /emoji-regex/8.0.0:
-    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
-    dev: true
-
-  /emojis-list/3.0.0:
-    resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
-    engines: {node: '>= 4'}
-    dev: true
-
-  /end-of-stream/1.4.4:
-    resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
-    dependencies:
-      once: 1.4.0
-    dev: true
-
-  /enhanced-resolve/4.5.0:
-    resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==}
-    engines: {node: '>=6.9.0'}
-    dependencies:
-      graceful-fs: 4.2.10
-      memory-fs: 0.5.0
-      tapable: 1.1.3
-    dev: true
-
-  /enquirer/2.3.6:
-    resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==}
-    engines: {node: '>=8.6'}
-    dependencies:
-      ansi-colors: 4.1.3
-    dev: true
-
-  /errno/0.1.8:
-    resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
-    hasBin: true
-    dependencies:
-      prr: 1.0.1
-    dev: true
-
-  /error-ex/1.3.2:
-    resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
-    dependencies:
-      is-arrayish: 0.2.1
-    dev: true
-
-  /escalade/3.1.1:
-    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /escape-string-regexp/1.0.5:
-    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
-    engines: {node: '>=0.8.0'}
-    dev: true
-
-  /escape-string-regexp/2.0.0:
-    resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /escape-string-regexp/4.0.0:
-    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /escodegen/2.0.0:
-    resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==}
-    engines: {node: '>=6.0'}
-    hasBin: true
-    dependencies:
-      esprima: 4.0.1
-      estraverse: 5.3.0
-      esutils: 2.0.3
-      optionator: 0.8.3
-    optionalDependencies:
-      source-map: 0.6.1
-    dev: true
-
-  /eslint-config-prettier/6.15.0_eslint@7.32.0:
-    resolution: {integrity: sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw==}
-    hasBin: true
-    peerDependencies:
-      eslint: '>=3.14.1'
-    dependencies:
-      eslint: 7.32.0
-      get-stdin: 6.0.0
-    dev: true
-
-  /eslint-plugin-jest/23.20.0_2de3j2mqba4wgeuiaqz2k7syrm:
-    resolution: {integrity: sha512-+6BGQt85OREevBDWCvhqj1yYA4+BFK4XnRZSGJionuEYmcglMZYLNNBBemwzbqUAckURaHdJSBcjHPyrtypZOw==}
-    engines: {node: '>=8'}
-    peerDependencies:
-      eslint: '>=5'
-    dependencies:
-      '@typescript-eslint/experimental-utils': 2.34.0_2de3j2mqba4wgeuiaqz2k7syrm
-      eslint: 7.32.0
-    transitivePeerDependencies:
-      - supports-color
-      - typescript
-    dev: true
-
-  /eslint-plugin-prettier/3.4.1_dnozcfbeb5wiozlftdrslmvmgu:
-    resolution: {integrity: sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==}
-    engines: {node: '>=6.0.0'}
-    peerDependencies:
-      eslint: '>=5.0.0'
-      eslint-config-prettier: '*'
-      prettier: '>=1.13.0'
-    peerDependenciesMeta:
-      eslint-config-prettier:
-        optional: true
-    dependencies:
-      eslint: 7.32.0
-      eslint-config-prettier: 6.15.0_eslint@7.32.0
-      prettier: 2.6.0
-      prettier-linter-helpers: 1.0.0
-    dev: true
-
-  /eslint-scope/4.0.3:
-    resolution: {integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==}
-    engines: {node: '>=4.0.0'}
-    dependencies:
-      esrecurse: 4.3.0
-      estraverse: 4.3.0
-    dev: true
-
-  /eslint-scope/5.1.1:
-    resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
-    engines: {node: '>=8.0.0'}
-    dependencies:
-      esrecurse: 4.3.0
-      estraverse: 4.3.0
-    dev: true
-
-  /eslint-utils/2.1.0:
-    resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==}
-    engines: {node: '>=6'}
-    dependencies:
-      eslint-visitor-keys: 1.3.0
-    dev: true
-
-  /eslint-visitor-keys/1.3.0:
-    resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /eslint-visitor-keys/2.1.0:
-    resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /eslint/7.32.0:
-    resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    hasBin: true
-    dependencies:
-      '@babel/code-frame': 7.12.11
-      '@eslint/eslintrc': 0.4.3
-      '@humanwhocodes/config-array': 0.5.0
-      ajv: 6.12.6
-      chalk: 4.1.2
-      cross-spawn: 7.0.3
-      debug: 4.3.4
-      doctrine: 3.0.0
-      enquirer: 2.3.6
-      escape-string-regexp: 4.0.0
-      eslint-scope: 5.1.1
-      eslint-utils: 2.1.0
-      eslint-visitor-keys: 2.1.0
-      espree: 7.3.1
-      esquery: 1.4.0
-      esutils: 2.0.3
-      fast-deep-equal: 3.1.3
-      file-entry-cache: 6.0.1
-      functional-red-black-tree: 1.0.1
-      glob-parent: 5.1.2
-      globals: 13.15.0
-      ignore: 4.0.6
-      import-fresh: 3.3.0
-      imurmurhash: 0.1.4
-      is-glob: 4.0.3
-      js-yaml: 3.14.1
-      json-stable-stringify-without-jsonify: 1.0.1
-      levn: 0.4.1
-      lodash.merge: 4.6.2
-      minimatch: 3.1.2
-      natural-compare: 1.4.0
-      optionator: 0.9.1
-      progress: 2.0.3
-      regexpp: 3.2.0
-      semver: 7.3.7
-      strip-ansi: 6.0.1
-      strip-json-comments: 3.1.1
-      table: 6.8.0
-      text-table: 0.2.0
-      v8-compile-cache: 2.3.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /espree/7.3.1:
-    resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    dependencies:
-      acorn: 7.4.1
-      acorn-jsx: 5.3.2_acorn@7.4.1
-      eslint-visitor-keys: 1.3.0
-    dev: true
-
-  /esprima/4.0.1:
-    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
-
-  /esquery/1.4.0:
-    resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
-    engines: {node: '>=0.10'}
-    dependencies:
-      estraverse: 5.3.0
-    dev: true
-
-  /esrecurse/4.3.0:
-    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
-    engines: {node: '>=4.0'}
-    dependencies:
-      estraverse: 5.3.0
-    dev: true
-
-  /estraverse/4.3.0:
-    resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
-    engines: {node: '>=4.0'}
-    dev: true
-
-  /estraverse/5.3.0:
-    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
-    engines: {node: '>=4.0'}
-    dev: true
-
-  /esutils/2.0.3:
-    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /events/3.3.0:
-    resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
-    engines: {node: '>=0.8.x'}
-    dev: true
-
-  /evp_bytestokey/1.0.3:
-    resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==}
-    dependencies:
-      md5.js: 1.3.5
-      safe-buffer: 5.2.1
-    dev: true
-
-  /exec-sh/0.3.6:
-    resolution: {integrity: sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w==}
-    dev: true
-
-  /execa/1.0.0:
-    resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
-    engines: {node: '>=6'}
-    dependencies:
-      cross-spawn: 6.0.5
-      get-stream: 4.1.0
-      is-stream: 1.1.0
-      npm-run-path: 2.0.2
-      p-finally: 1.0.0
-      signal-exit: 3.0.7
-      strip-eof: 1.0.0
-    dev: true
-
-  /execa/4.1.0:
-    resolution: {integrity: sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==}
-    engines: {node: '>=10'}
-    dependencies:
-      cross-spawn: 7.0.3
-      get-stream: 5.2.0
-      human-signals: 1.1.1
-      is-stream: 2.0.1
-      merge-stream: 2.0.0
-      npm-run-path: 4.0.1
-      onetime: 5.1.2
-      signal-exit: 3.0.7
-      strip-final-newline: 2.0.0
-    dev: true
-
-  /exit/0.1.2:
-    resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
-    engines: {node: '>= 0.8.0'}
-    dev: true
-
-  /expand-brackets/2.1.4:
-    resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      debug: 2.6.9
-      define-property: 0.2.5
-      extend-shallow: 2.0.1
-      posix-character-classes: 0.1.1
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /expand-brackets/2.1.4_supports-color@6.1.0:
-    resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      debug: 2.6.9_supports-color@6.1.0
-      define-property: 0.2.5
-      extend-shallow: 2.0.1
-      posix-character-classes: 0.1.1
-      regex-not: 1.0.2
-      snapdragon: 0.8.2_supports-color@6.1.0
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /expand-tilde/2.0.2:
-    resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      homedir-polyfill: 1.0.3
-    dev: true
-
-  /expect/26.6.2:
-    resolution: {integrity: sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      ansi-styles: 4.3.0
-      jest-get-type: 26.3.0
-      jest-matcher-utils: 26.6.2
-      jest-message-util: 26.6.2
-      jest-regex-util: 26.0.0
-    dev: true
-
-  /extend-shallow/2.0.1:
-    resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-extendable: 0.1.1
-    dev: true
-
-  /extend-shallow/3.0.2:
-    resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      assign-symbols: 1.0.0
-      is-extendable: 1.0.1
-    dev: true
-
-  /extglob/2.0.4:
-    resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      array-unique: 0.3.2
-      define-property: 1.0.0
-      expand-brackets: 2.1.4
-      extend-shallow: 2.0.1
-      fragment-cache: 0.2.1
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /extglob/2.0.4_supports-color@6.1.0:
-    resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      array-unique: 0.3.2
-      define-property: 1.0.0
-      expand-brackets: 2.1.4_supports-color@6.1.0
-      extend-shallow: 2.0.1
-      fragment-cache: 0.2.1
-      regex-not: 1.0.2
-      snapdragon: 0.8.2_supports-color@6.1.0
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /fast-deep-equal/3.1.3:
-    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
-    dev: true
-
-  /fast-diff/1.2.0:
-    resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==}
-    dev: true
-
-  /fast-json-stable-stringify/2.1.0:
-    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
-    dev: true
-
-  /fast-levenshtein/2.0.6:
-    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
-    dev: true
-
-  /fb-watchman/2.0.1:
-    resolution: {integrity: sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==}
-    dependencies:
-      bser: 2.1.1
-    dev: true
-
-  /figgy-pudding/3.5.2:
-    resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==}
-    dev: true
-
-  /file-entry-cache/6.0.1:
-    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    dependencies:
-      flat-cache: 3.0.4
-    dev: true
-
-  /file-uri-to-path/1.0.0:
-    resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==}
-    dev: true
-    optional: true
-
-  /fill-range/4.0.0:
-    resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 2.0.1
-      is-number: 3.0.0
-      repeat-string: 1.6.1
-      to-regex-range: 2.1.1
-    dev: true
-
-  /fill-range/7.0.1:
-    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      to-regex-range: 5.0.1
-    dev: true
-
-  /find-cache-dir/2.1.0:
-    resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      commondir: 1.0.1
-      make-dir: 2.1.0
-      pkg-dir: 3.0.0
-    dev: true
-
-  /find-cache-dir/3.3.2:
-    resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
-    engines: {node: '>=8'}
-    dependencies:
-      commondir: 1.0.1
-      make-dir: 3.1.0
-      pkg-dir: 4.2.0
-    dev: true
-
-  /find-up/3.0.0:
-    resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
-    engines: {node: '>=6'}
-    dependencies:
-      locate-path: 3.0.0
-    dev: true
-
-  /find-up/4.1.0:
-    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
-    engines: {node: '>=8'}
-    dependencies:
-      locate-path: 5.0.0
-      path-exists: 4.0.0
-    dev: true
-
-  /findup-sync/3.0.0_supports-color@6.1.0:
-    resolution: {integrity: sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==}
-    engines: {node: '>= 0.10'}
-    dependencies:
-      detect-file: 1.0.0
-      is-glob: 4.0.3
-      micromatch: 3.1.10_supports-color@6.1.0
-      resolve-dir: 1.0.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /flat-cache/3.0.4:
-    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
-    engines: {node: ^10.12.0 || >=12.0.0}
-    dependencies:
-      flatted: 3.2.5
-      rimraf: 3.0.2
-    dev: true
-
-  /flatted/3.2.5:
-    resolution: {integrity: sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==}
-    dev: true
-
-  /flush-write-stream/1.1.1:
-    resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-    dev: true
-
-  /for-in/1.0.2:
-    resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /form-data/3.0.1:
-    resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
-    engines: {node: '>= 6'}
-    dependencies:
-      asynckit: 0.4.0
-      combined-stream: 1.0.8
-      mime-types: 2.1.35
-    dev: true
-
-  /fragment-cache/0.2.1:
-    resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      map-cache: 0.2.2
-    dev: true
-
-  /from2/2.3.0:
-    resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-    dev: true
-
-  /fs-minipass/2.1.0:
-    resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
-    engines: {node: '>= 8'}
-    dependencies:
-      minipass: 3.1.6
-    dev: true
-
-  /fs-write-stream-atomic/1.0.10:
-    resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==}
-    dependencies:
-      graceful-fs: 4.2.10
-      iferr: 0.1.5
-      imurmurhash: 0.1.4
-      readable-stream: 2.3.7
-    dev: true
-
-  /fs.realpath/1.0.0:
-    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
-    dev: true
-
-  /fsevents/1.2.13:
-    resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==}
-    engines: {node: '>= 4.0'}
-    os: [darwin]
-    deprecated: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.
-    requiresBuild: true
-    dependencies:
-      bindings: 1.5.0
-      nan: 2.16.0
-    dev: true
-    optional: true
-
-  /fsevents/2.3.2:
-    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
-    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
-    os: [darwin]
-    requiresBuild: true
-    dev: true
-    optional: true
-
-  /function-bind/1.1.1:
-    resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
-    dev: true
-
-  /functional-red-black-tree/1.0.1:
-    resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==}
-    dev: true
-
-  /gauge/3.0.2:
-    resolution: {integrity: sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==}
-    engines: {node: '>=10'}
-    dependencies:
-      aproba: 2.0.0
-      color-support: 1.1.3
-      console-control-strings: 1.1.0
-      has-unicode: 2.0.1
-      object-assign: 4.1.1
-      signal-exit: 3.0.7
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-      wide-align: 1.1.5
-    dev: true
-
-  /gensync/1.0.0-beta.2:
-    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
-    engines: {node: '>=6.9.0'}
-    dev: true
-
-  /get-caller-file/2.0.5:
-    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
-    engines: {node: 6.* || 8.* || >= 10.*}
-    dev: true
-
-  /get-package-type/0.1.0:
-    resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
-    engines: {node: '>=8.0.0'}
-    dev: true
-
-  /get-stdin/6.0.0:
-    resolution: {integrity: sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /get-stream/4.1.0:
-    resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
-    engines: {node: '>=6'}
-    dependencies:
-      pump: 3.0.0
-    dev: true
-
-  /get-stream/5.2.0:
-    resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
-    engines: {node: '>=8'}
-    dependencies:
-      pump: 3.0.0
-    dev: true
-
-  /get-value/2.0.6:
-    resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /glob-parent/3.1.0:
-    resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==}
-    dependencies:
-      is-glob: 3.1.0
-      path-dirname: 1.0.2
-    dev: true
-    optional: true
-
-  /glob-parent/5.1.2:
-    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
-    engines: {node: '>= 6'}
-    dependencies:
-      is-glob: 4.0.3
-    dev: true
-
-  /glob/7.2.3:
-    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
-    dependencies:
-      fs.realpath: 1.0.0
-      inflight: 1.0.6
-      inherits: 2.0.4
-      minimatch: 3.1.2
-      once: 1.4.0
-      path-is-absolute: 1.0.1
-    dev: true
-
-  /global-modules/1.0.0:
-    resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      global-prefix: 1.0.2
-      is-windows: 1.0.2
-      resolve-dir: 1.0.1
-    dev: true
-
-  /global-modules/2.0.0:
-    resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==}
-    engines: {node: '>=6'}
-    dependencies:
-      global-prefix: 3.0.0
-    dev: true
-
-  /global-prefix/1.0.2:
-    resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      expand-tilde: 2.0.2
-      homedir-polyfill: 1.0.3
-      ini: 1.3.8
-      is-windows: 1.0.2
-      which: 1.3.1
-    dev: true
-
-  /global-prefix/3.0.0:
-    resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
-    engines: {node: '>=6'}
-    dependencies:
-      ini: 1.3.8
-      kind-of: 6.0.3
-      which: 1.3.1
-    dev: true
-
-  /globals/11.12.0:
-    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /globals/13.15.0:
-    resolution: {integrity: sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==}
-    engines: {node: '>=8'}
-    dependencies:
-      type-fest: 0.20.2
-    dev: true
-
-  /graceful-fs/4.2.10:
-    resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
-    dev: true
-
-  /growly/1.3.0:
-    resolution: {integrity: sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==}
-    dev: true
-    optional: true
-
-  /has-flag/3.0.0:
-    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /has-flag/4.0.0:
-    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /has-unicode/2.0.1:
-    resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
-    dev: true
-
-  /has-value/0.3.1:
-    resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      get-value: 2.0.6
-      has-values: 0.1.4
-      isobject: 2.1.0
-    dev: true
-
-  /has-value/1.0.0:
-    resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      get-value: 2.0.6
-      has-values: 1.0.0
-      isobject: 3.0.1
-    dev: true
-
-  /has-values/0.1.4:
-    resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /has-values/1.0.0:
-    resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-number: 3.0.0
-      kind-of: 4.0.0
-    dev: true
-
-  /has/1.0.3:
-    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
-    engines: {node: '>= 0.4.0'}
-    dependencies:
-      function-bind: 1.1.1
-    dev: true
-
-  /hash-base/3.1.0:
-    resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==}
-    engines: {node: '>=4'}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 3.6.0
-      safe-buffer: 5.2.1
-    dev: true
-
-  /hash.js/1.1.7:
-    resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
-    dependencies:
-      inherits: 2.0.4
-      minimalistic-assert: 1.0.1
-    dev: true
-
-  /hmac-drbg/1.0.1:
-    resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==}
-    dependencies:
-      hash.js: 1.1.7
-      minimalistic-assert: 1.0.1
-      minimalistic-crypto-utils: 1.0.1
-    dev: true
-
-  /homedir-polyfill/1.0.3:
-    resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      parse-passwd: 1.0.0
-    dev: true
-
-  /hosted-git-info/2.8.9:
-    resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
-    dev: true
-
-  /html-encoding-sniffer/2.0.1:
-    resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      whatwg-encoding: 1.0.5
-    dev: true
-
-  /html-escaper/2.0.2:
-    resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
-    dev: true
-
-  /http-proxy-agent/4.0.1:
-    resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
-    engines: {node: '>= 6'}
-    dependencies:
-      '@tootallnate/once': 1.1.2
-      agent-base: 6.0.2
-      debug: 4.3.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /https-browserify/1.0.0:
-    resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==}
-    dev: true
-
-  /https-proxy-agent/5.0.1:
-    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
-    engines: {node: '>= 6'}
-    dependencies:
-      agent-base: 6.0.2
-      debug: 4.3.4
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /human-signals/1.1.1:
-    resolution: {integrity: sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==}
-    engines: {node: '>=8.12.0'}
-    dev: true
-
-  /iconv-lite/0.4.24:
-    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      safer-buffer: 2.1.2
-    dev: true
-
-  /ieee754/1.2.1:
-    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
-    dev: true
-
-  /iferr/0.1.5:
-    resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==}
-    dev: true
-
-  /ignore/4.0.6:
-    resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==}
-    engines: {node: '>= 4'}
-    dev: true
-
-  /import-fresh/3.3.0:
-    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
-    engines: {node: '>=6'}
-    dependencies:
-      parent-module: 1.0.1
-      resolve-from: 4.0.0
-    dev: true
-
-  /import-local/2.0.0:
-    resolution: {integrity: sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==}
-    engines: {node: '>=6'}
-    hasBin: true
-    dependencies:
-      pkg-dir: 3.0.0
-      resolve-cwd: 2.0.0
-    dev: true
-
-  /import-local/3.1.0:
-    resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
-    engines: {node: '>=8'}
-    hasBin: true
-    dependencies:
-      pkg-dir: 4.2.0
-      resolve-cwd: 3.0.0
-    dev: true
-
-  /imurmurhash/0.1.4:
-    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
-    engines: {node: '>=0.8.19'}
-    dev: true
-
-  /indent-string/4.0.0:
-    resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /infer-owner/1.0.4:
-    resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
-    dev: true
-
-  /inflight/1.0.6:
-    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
-    dependencies:
-      once: 1.4.0
-      wrappy: 1.0.2
-    dev: true
-
-  /inherits/2.0.1:
-    resolution: {integrity: sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==}
-    dev: true
-
-  /inherits/2.0.3:
-    resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==}
-    dev: true
-
-  /inherits/2.0.4:
-    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
-    dev: true
-
-  /ini/1.3.8:
-    resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
-    dev: true
-
-  /interpret/1.4.0:
-    resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
-    engines: {node: '>= 0.10'}
-    dev: true
-
-  /is-accessor-descriptor/0.1.6:
-    resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /is-accessor-descriptor/1.0.0:
-    resolution: {integrity: sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 6.0.3
-    dev: true
-
-  /is-arrayish/0.2.1:
-    resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
-    dev: true
-
-  /is-binary-path/1.0.1:
-    resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      binary-extensions: 1.13.1
-    dev: true
-    optional: true
-
-  /is-binary-path/2.1.0:
-    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
-    engines: {node: '>=8'}
-    dependencies:
-      binary-extensions: 2.2.0
-    dev: true
-    optional: true
-
-  /is-buffer/1.1.6:
-    resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==}
-    dev: true
-
-  /is-ci/2.0.0:
-    resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==}
-    hasBin: true
-    dependencies:
-      ci-info: 2.0.0
-    dev: true
-
-  /is-core-module/2.9.0:
-    resolution: {integrity: sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==}
-    dependencies:
-      has: 1.0.3
-    dev: true
-
-  /is-data-descriptor/0.1.4:
-    resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /is-data-descriptor/1.0.0:
-    resolution: {integrity: sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 6.0.3
-    dev: true
-
-  /is-descriptor/0.1.6:
-    resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-accessor-descriptor: 0.1.6
-      is-data-descriptor: 0.1.4
-      kind-of: 5.1.0
-    dev: true
-
-  /is-descriptor/1.0.2:
-    resolution: {integrity: sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-accessor-descriptor: 1.0.0
-      is-data-descriptor: 1.0.0
-      kind-of: 6.0.3
-    dev: true
-
-  /is-docker/2.2.1:
-    resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
-    engines: {node: '>=8'}
-    hasBin: true
-    dev: true
-    optional: true
-
-  /is-extendable/0.1.1:
-    resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-extendable/1.0.1:
-    resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-plain-object: 2.0.4
-    dev: true
-
-  /is-extglob/2.1.1:
-    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-fullwidth-code-point/2.0.0:
-    resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /is-fullwidth-code-point/3.0.0:
-    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /is-generator-fn/2.1.0:
-    resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /is-glob/3.1.0:
-    resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-extglob: 2.1.1
-    dev: true
-    optional: true
-
-  /is-glob/4.0.3:
-    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-extglob: 2.1.1
-    dev: true
-
-  /is-number/3.0.0:
-    resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /is-number/7.0.0:
-    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
-    engines: {node: '>=0.12.0'}
-    dev: true
-
-  /is-plain-object/2.0.4:
-    resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isobject: 3.0.1
-    dev: true
-
-  /is-potential-custom-element-name/1.0.1:
-    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
-    dev: true
-
-  /is-stream/1.1.0:
-    resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-stream/2.0.1:
-    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /is-typedarray/1.0.0:
-    resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
-    dev: true
-
-  /is-windows/1.0.2:
-    resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /is-wsl/1.1.0:
-    resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /is-wsl/2.2.0:
-    resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
-    engines: {node: '>=8'}
-    dependencies:
-      is-docker: 2.2.1
-    dev: true
-    optional: true
-
-  /isarray/1.0.0:
-    resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==}
-    dev: true
-
-  /isexe/2.0.0:
-    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
-    dev: true
-
-  /isobject/2.1.0:
-    resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isarray: 1.0.0
-    dev: true
-
-  /isobject/3.0.1:
-    resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /istanbul-lib-coverage/3.2.0:
-    resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /istanbul-lib-instrument/4.0.3:
-    resolution: {integrity: sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@babel/core': 7.18.2
-      '@istanbuljs/schema': 0.1.3
-      istanbul-lib-coverage: 3.2.0
-      semver: 6.3.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /istanbul-lib-instrument/5.2.0:
-    resolution: {integrity: sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@babel/core': 7.18.2
-      '@babel/parser': 7.18.4
-      '@istanbuljs/schema': 0.1.3
-      istanbul-lib-coverage: 3.2.0
-      semver: 6.3.0
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /istanbul-lib-report/3.0.0:
-    resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==}
-    engines: {node: '>=8'}
-    dependencies:
-      istanbul-lib-coverage: 3.2.0
-      make-dir: 3.1.0
-      supports-color: 7.2.0
-    dev: true
-
-  /istanbul-lib-source-maps/4.0.1:
-    resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
-    engines: {node: '>=10'}
-    dependencies:
-      debug: 4.3.4
-      istanbul-lib-coverage: 3.2.0
-      source-map: 0.6.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /istanbul-reports/3.1.4:
-    resolution: {integrity: sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==}
-    engines: {node: '>=8'}
-    dependencies:
-      html-escaper: 2.0.2
-      istanbul-lib-report: 3.0.0
-    dev: true
-
-  /jest-changed-files/26.6.2:
-    resolution: {integrity: sha512-fDS7szLcY9sCtIip8Fjry9oGf3I2ht/QT21bAHm5Dmf0mD4X3ReNUf17y+bO6fR8WgbIZTlbyG1ak/53cbRzKQ==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      execa: 4.1.0
-      throat: 5.0.0
-    dev: true
-
-  /jest-cli/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-GF9noBSa9t08pSyl3CY4frMrqp+aQXFGFkf5hEPbh/pIUFYWMK6ZLTfbmadxJVcJrdRoChlWQsA2VkJcDFK8hg==}
-    engines: {node: '>= 10.14.2'}
-    hasBin: true
-    dependencies:
-      '@jest/core': 26.6.3_canvas@2.9.1
-      '@jest/test-result': 26.6.2
-      '@jest/types': 26.6.2
-      chalk: 4.1.2
-      exit: 0.1.2
-      graceful-fs: 4.2.10
-      import-local: 3.1.0
-      is-ci: 2.0.0
-      jest-config: 26.6.3_canvas@2.9.1
-      jest-util: 26.6.2
-      jest-validate: 26.6.2
-      prompts: 2.4.2
-      yargs: 15.4.1
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - ts-node
-      - utf-8-validate
-    dev: true
-
-  /jest-config/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-t5qdIj/bCj2j7NFVHb2nFB4aUdfucDn3JRKgrZnplb8nieAirAzRSHP8uDEd+qV6ygzg9Pz4YG7UTJf94LPSyg==}
-    engines: {node: '>= 10.14.2'}
-    peerDependencies:
-      ts-node: '>=9.0.0'
-    peerDependenciesMeta:
-      ts-node:
-        optional: true
-    dependencies:
-      '@babel/core': 7.18.2
-      '@jest/test-sequencer': 26.6.3_canvas@2.9.1
-      '@jest/types': 26.6.2
-      babel-jest: 26.6.3_@babel+core@7.18.2
-      chalk: 4.1.2
-      deepmerge: 4.2.2
-      glob: 7.2.3
-      graceful-fs: 4.2.10
-      jest-environment-jsdom: 26.6.2_canvas@2.9.1
-      jest-environment-node: 26.6.2
-      jest-get-type: 26.3.0
-      jest-jasmine2: 26.6.3_canvas@2.9.1
-      jest-regex-util: 26.0.0
-      jest-resolve: 26.6.2
-      jest-util: 26.6.2
-      jest-validate: 26.6.2
-      micromatch: 4.0.5
-      pretty-format: 26.6.2
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /jest-diff/26.6.2:
-    resolution: {integrity: sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      chalk: 4.1.2
-      diff-sequences: 26.6.2
-      jest-get-type: 26.3.0
-      pretty-format: 26.6.2
-    dev: true
-
-  /jest-docblock/26.0.0:
-    resolution: {integrity: sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      detect-newline: 3.1.0
-    dev: true
-
-  /jest-each/26.6.2:
-    resolution: {integrity: sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      chalk: 4.1.2
-      jest-get-type: 26.3.0
-      jest-util: 26.6.2
-      pretty-format: 26.6.2
-    dev: true
-
-  /jest-environment-jsdom/26.6.2_canvas@2.9.1:
-    resolution: {integrity: sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/environment': 26.6.2
-      '@jest/fake-timers': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      jest-mock: 26.6.2
-      jest-util: 26.6.2
-      jsdom: 16.7.0_canvas@2.9.1
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /jest-environment-node/26.6.2:
-    resolution: {integrity: sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/environment': 26.6.2
-      '@jest/fake-timers': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      jest-mock: 26.6.2
-      jest-util: 26.6.2
-    dev: true
-
-  /jest-get-type/26.3.0:
-    resolution: {integrity: sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==}
-    engines: {node: '>= 10.14.2'}
-    dev: true
-
-  /jest-haste-map/26.6.2:
-    resolution: {integrity: sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      '@types/graceful-fs': 4.1.5
-      '@types/node': 17.0.41
-      anymatch: 3.1.2
-      fb-watchman: 2.0.1
-      graceful-fs: 4.2.10
-      jest-regex-util: 26.0.0
-      jest-serializer: 26.6.2
-      jest-util: 26.6.2
-      jest-worker: 26.6.2
-      micromatch: 4.0.5
-      sane: 4.1.0
-      walker: 1.0.8
-    optionalDependencies:
-      fsevents: 2.3.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /jest-jasmine2/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@babel/traverse': 7.18.2
-      '@jest/environment': 26.6.2
-      '@jest/source-map': 26.6.2
-      '@jest/test-result': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      chalk: 4.1.2
-      co: 4.6.0
-      expect: 26.6.2
-      is-generator-fn: 2.1.0
-      jest-each: 26.6.2
-      jest-matcher-utils: 26.6.2
-      jest-message-util: 26.6.2
-      jest-runtime: 26.6.3_canvas@2.9.1
-      jest-snapshot: 26.6.2
-      jest-util: 26.6.2
-      pretty-format: 26.6.2
-      throat: 5.0.0
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - ts-node
-      - utf-8-validate
-    dev: true
-
-  /jest-leak-detector/26.6.2:
-    resolution: {integrity: sha512-i4xlXpsVSMeKvg2cEKdfhh0H39qlJlP5Ex1yQxwF9ubahboQYMgTtz5oML35AVA3B4Eu+YsmwaiKVev9KCvLxg==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      jest-get-type: 26.3.0
-      pretty-format: 26.6.2
-    dev: true
-
-  /jest-matcher-utils/26.6.2:
-    resolution: {integrity: sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      chalk: 4.1.2
-      jest-diff: 26.6.2
-      jest-get-type: 26.3.0
-      pretty-format: 26.6.2
-    dev: true
-
-  /jest-message-util/26.6.2:
-    resolution: {integrity: sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@babel/code-frame': 7.16.7
-      '@jest/types': 26.6.2
-      '@types/stack-utils': 2.0.1
-      chalk: 4.1.2
-      graceful-fs: 4.2.10
-      micromatch: 4.0.5
-      pretty-format: 26.6.2
-      slash: 3.0.0
-      stack-utils: 2.0.5
-    dev: true
-
-  /jest-mock/26.6.2:
-    resolution: {integrity: sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-    dev: true
-
-  /jest-pnp-resolver/1.2.2_jest-resolve@26.6.2:
-    resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==}
-    engines: {node: '>=6'}
-    peerDependencies:
-      jest-resolve: '*'
-    peerDependenciesMeta:
-      jest-resolve:
-        optional: true
-    dependencies:
-      jest-resolve: 26.6.2
-    dev: true
-
-  /jest-regex-util/26.0.0:
-    resolution: {integrity: sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==}
-    engines: {node: '>= 10.14.2'}
-    dev: true
-
-  /jest-resolve-dependencies/26.6.3:
-    resolution: {integrity: sha512-pVwUjJkxbhe4RY8QEWzN3vns2kqyuldKpxlxJlzEYfKSvY6/bMvxoFrYYzUO1Gx28yKWN37qyV7rIoIp2h8fTg==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      jest-regex-util: 26.0.0
-      jest-snapshot: 26.6.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /jest-resolve/26.6.2:
-    resolution: {integrity: sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      chalk: 4.1.2
-      graceful-fs: 4.2.10
-      jest-pnp-resolver: 1.2.2_jest-resolve@26.6.2
-      jest-util: 26.6.2
-      read-pkg-up: 7.0.1
-      resolve: 1.22.0
-      slash: 3.0.0
-    dev: true
-
-  /jest-runner/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-atgKpRHnaA2OvByG/HpGA4g6CSPS/1LK0jK3gATJAoptC1ojltpmVlYC3TYgdmGp+GLuhzpH30Gvs36szSL2JQ==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/console': 26.6.2
-      '@jest/environment': 26.6.2
-      '@jest/test-result': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      chalk: 4.1.2
-      emittery: 0.7.2
-      exit: 0.1.2
-      graceful-fs: 4.2.10
-      jest-config: 26.6.3_canvas@2.9.1
-      jest-docblock: 26.0.0
-      jest-haste-map: 26.6.2
-      jest-leak-detector: 26.6.2
-      jest-message-util: 26.6.2
-      jest-resolve: 26.6.2
-      jest-runtime: 26.6.3_canvas@2.9.1
-      jest-util: 26.6.2
-      jest-worker: 26.6.2
-      source-map-support: 0.5.21
-      throat: 5.0.0
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - ts-node
-      - utf-8-validate
-    dev: true
-
-  /jest-runtime/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-lrzyR3N8sacTAMeonbqpnSka1dHNux2uk0qqDXVkMv2c/A3wYnvQ4EXuI013Y6+gSKSCxdaczvf4HF0mVXHRdw==}
-    engines: {node: '>= 10.14.2'}
-    hasBin: true
-    dependencies:
-      '@jest/console': 26.6.2
-      '@jest/environment': 26.6.2
-      '@jest/fake-timers': 26.6.2
-      '@jest/globals': 26.6.2
-      '@jest/source-map': 26.6.2
-      '@jest/test-result': 26.6.2
-      '@jest/transform': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/yargs': 15.0.14
-      chalk: 4.1.2
-      cjs-module-lexer: 0.6.0
-      collect-v8-coverage: 1.0.1
-      exit: 0.1.2
-      glob: 7.2.3
-      graceful-fs: 4.2.10
-      jest-config: 26.6.3_canvas@2.9.1
-      jest-haste-map: 26.6.2
-      jest-message-util: 26.6.2
-      jest-mock: 26.6.2
-      jest-regex-util: 26.0.0
-      jest-resolve: 26.6.2
-      jest-snapshot: 26.6.2
-      jest-util: 26.6.2
-      jest-validate: 26.6.2
-      slash: 3.0.0
-      strip-bom: 4.0.0
-      yargs: 15.4.1
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - ts-node
-      - utf-8-validate
-    dev: true
-
-  /jest-serializer/26.6.2:
-    resolution: {integrity: sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@types/node': 17.0.41
-      graceful-fs: 4.2.10
-    dev: true
-
-  /jest-snapshot/26.6.2:
-    resolution: {integrity: sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@babel/types': 7.18.4
-      '@jest/types': 26.6.2
-      '@types/babel__traverse': 7.17.1
-      '@types/prettier': 2.6.0
-      chalk: 4.1.2
-      expect: 26.6.2
-      graceful-fs: 4.2.10
-      jest-diff: 26.6.2
-      jest-get-type: 26.3.0
-      jest-haste-map: 26.6.2
-      jest-matcher-utils: 26.6.2
-      jest-message-util: 26.6.2
-      jest-resolve: 26.6.2
-      natural-compare: 1.4.0
-      pretty-format: 26.6.2
-      semver: 7.3.7
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /jest-util/26.6.2:
-    resolution: {integrity: sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      chalk: 4.1.2
-      graceful-fs: 4.2.10
-      is-ci: 2.0.0
-      micromatch: 4.0.5
-    dev: true
-
-  /jest-validate/26.6.2:
-    resolution: {integrity: sha512-NEYZ9Aeyj0i5rQqbq+tpIOom0YS1u2MVu6+euBsvpgIme+FOfRmoC4R5p0JiAUpaFvFy24xgrpMknarR/93XjQ==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/types': 26.6.2
-      camelcase: 6.3.0
-      chalk: 4.1.2
-      jest-get-type: 26.3.0
-      leven: 3.1.0
-      pretty-format: 26.6.2
-    dev: true
-
-  /jest-watcher/26.6.2:
-    resolution: {integrity: sha512-WKJob0P/Em2csiVthsI68p6aGKTIcsfjH9Gsx1f0A3Italz43e3ho0geSAVsmj09RWOELP1AZ/DXyJgOgDKxXQ==}
-    engines: {node: '>= 10.14.2'}
-    dependencies:
-      '@jest/test-result': 26.6.2
-      '@jest/types': 26.6.2
-      '@types/node': 17.0.41
-      ansi-escapes: 4.3.2
-      chalk: 4.1.2
-      jest-util: 26.6.2
-      string-length: 4.0.2
-    dev: true
-
-  /jest-worker/26.6.2:
-    resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
-    engines: {node: '>= 10.13.0'}
-    dependencies:
-      '@types/node': 17.0.41
-      merge-stream: 2.0.0
-      supports-color: 7.2.0
-    dev: true
-
-  /jest/26.6.3_canvas@2.9.1:
-    resolution: {integrity: sha512-lGS5PXGAzR4RF7V5+XObhqz2KZIDUA1yD0DG6pBVmy10eh0ZIXQImRuzocsI/N2XZ1GrLFwTS27In2i2jlpq1Q==}
-    engines: {node: '>= 10.14.2'}
-    hasBin: true
-    dependencies:
-      '@jest/core': 26.6.3_canvas@2.9.1
-      import-local: 3.1.0
-      jest-cli: 26.6.3_canvas@2.9.1
-    transitivePeerDependencies:
-      - bufferutil
-      - canvas
-      - supports-color
-      - ts-node
-      - utf-8-validate
-    dev: true
-
-  /js-tokens/4.0.0:
-    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
-    dev: true
-
-  /js-yaml/3.14.1:
-    resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
-    hasBin: true
-    dependencies:
-      argparse: 1.0.10
-      esprima: 4.0.1
-    dev: true
-
-  /jsdom/16.7.0_canvas@2.9.1:
-    resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==}
-    engines: {node: '>=10'}
-    peerDependencies:
-      canvas: ^2.5.0
-    peerDependenciesMeta:
-      canvas:
-        optional: true
-    dependencies:
-      abab: 2.0.6
-      acorn: 8.7.1
-      acorn-globals: 6.0.0
-      canvas: 2.9.1
-      cssom: 0.4.4
-      cssstyle: 2.3.0
-      data-urls: 2.0.0
-      decimal.js: 10.3.1
-      domexception: 2.0.1
-      escodegen: 2.0.0
-      form-data: 3.0.1
-      html-encoding-sniffer: 2.0.1
-      http-proxy-agent: 4.0.1
-      https-proxy-agent: 5.0.1
-      is-potential-custom-element-name: 1.0.1
-      nwsapi: 2.2.0
-      parse5: 6.0.1
-      saxes: 5.0.1
-      symbol-tree: 3.2.4
-      tough-cookie: 4.0.0
-      w3c-hr-time: 1.0.2
-      w3c-xmlserializer: 2.0.0
-      webidl-conversions: 6.1.0
-      whatwg-encoding: 1.0.5
-      whatwg-mimetype: 2.3.0
-      whatwg-url: 8.7.0
-      ws: 7.5.8
-      xml-name-validator: 3.0.0
-    transitivePeerDependencies:
-      - bufferutil
-      - supports-color
-      - utf-8-validate
-    dev: true
-
-  /jsesc/2.5.2:
-    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
-    engines: {node: '>=4'}
-    hasBin: true
-    dev: true
-
-  /json-parse-better-errors/1.0.2:
-    resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
-    dev: true
-
-  /json-parse-even-better-errors/2.3.1:
-    resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
-    dev: true
-
-  /json-schema-traverse/0.4.1:
-    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
-    dev: true
-
-  /json-schema-traverse/1.0.0:
-    resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
-    dev: true
-
-  /json-stable-stringify-without-jsonify/1.0.1:
-    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
-    dev: true
-
-  /json5/1.0.1:
-    resolution: {integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==}
-    hasBin: true
-    dependencies:
-      minimist: 1.2.6
-    dev: true
-
-  /json5/2.2.1:
-    resolution: {integrity: sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==}
-    engines: {node: '>=6'}
-    hasBin: true
-    dev: true
-
-  /kind-of/3.2.2:
-    resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-buffer: 1.1.6
-    dev: true
-
-  /kind-of/4.0.0:
-    resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-buffer: 1.1.6
-    dev: true
-
-  /kind-of/5.1.0:
-    resolution: {integrity: sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /kind-of/6.0.3:
-    resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /kleur/3.0.3:
-    resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /leven/3.1.0:
-    resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /levn/0.3.0:
-    resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      prelude-ls: 1.1.2
-      type-check: 0.3.2
-    dev: true
-
-  /levn/0.4.1:
-    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      prelude-ls: 1.2.1
-      type-check: 0.4.0
-    dev: true
-
-  /lines-and-columns/1.2.4:
-    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
-    dev: true
-
-  /loader-runner/2.4.0:
-    resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==}
-    engines: {node: '>=4.3.0 <5.0.0 || >=5.10'}
-    dev: true
-
-  /loader-utils/1.4.0:
-    resolution: {integrity: sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==}
-    engines: {node: '>=4.0.0'}
-    dependencies:
-      big.js: 5.2.2
-      emojis-list: 3.0.0
-      json5: 1.0.1
-    dev: true
-
-  /loader-utils/2.0.2:
-    resolution: {integrity: sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==}
-    engines: {node: '>=8.9.0'}
-    dependencies:
-      big.js: 5.2.2
-      emojis-list: 3.0.0
-      json5: 2.2.1
-    dev: true
-
-  /locate-path/3.0.0:
-    resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
-    engines: {node: '>=6'}
-    dependencies:
-      p-locate: 3.0.0
-      path-exists: 3.0.0
-    dev: true
-
-  /locate-path/5.0.0:
-    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
-    engines: {node: '>=8'}
-    dependencies:
-      p-locate: 4.1.0
-    dev: true
-
-  /lodash.merge/4.6.2:
-    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
-    dev: true
-
-  /lodash.truncate/4.4.2:
-    resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==}
-    dev: true
-
-  /lodash/4.17.21:
-    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
-    dev: true
-
-  /lru-cache/5.1.1:
-    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
-    dependencies:
-      yallist: 3.1.1
-    dev: true
-
-  /lru-cache/6.0.0:
-    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
-    engines: {node: '>=10'}
-    dependencies:
-      yallist: 4.0.0
-    dev: true
-
-  /make-dir/2.1.0:
-    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
-    engines: {node: '>=6'}
-    dependencies:
-      pify: 4.0.1
-      semver: 5.7.1
-    dev: true
-
-  /make-dir/3.1.0:
-    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
-    engines: {node: '>=8'}
-    dependencies:
-      semver: 6.3.0
-    dev: true
-
-  /make-error/1.3.6:
-    resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==}
-    dev: true
-
-  /makeerror/1.0.12:
-    resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
-    dependencies:
-      tmpl: 1.0.5
-    dev: true
-
-  /map-cache/0.2.2:
-    resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /map-visit/1.0.0:
-    resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      object-visit: 1.0.1
-    dev: true
-
-  /md5.js/1.3.5:
-    resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==}
-    dependencies:
-      hash-base: 3.1.0
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /memory-fs/0.4.1:
-    resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==}
-    dependencies:
-      errno: 0.1.8
-      readable-stream: 2.3.7
-    dev: true
-
-  /memory-fs/0.5.0:
-    resolution: {integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==}
-    engines: {node: '>=4.3.0 <5.0.0 || >=5.10'}
-    dependencies:
-      errno: 0.1.8
-      readable-stream: 2.3.7
-    dev: true
-
-  /merge-stream/2.0.0:
-    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
-    dev: true
-
-  /micromatch/3.1.10:
-    resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-diff: 4.0.0
-      array-unique: 0.3.2
-      braces: 2.3.2
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      extglob: 2.0.4
-      fragment-cache: 0.2.1
-      kind-of: 6.0.3
-      nanomatch: 1.2.13
-      object.pick: 1.3.0
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /micromatch/3.1.10_supports-color@6.1.0:
-    resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-diff: 4.0.0
-      array-unique: 0.3.2
-      braces: 2.3.2_supports-color@6.1.0
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      extglob: 2.0.4_supports-color@6.1.0
-      fragment-cache: 0.2.1
-      kind-of: 6.0.3
-      nanomatch: 1.2.13_supports-color@6.1.0
-      object.pick: 1.3.0
-      regex-not: 1.0.2
-      snapdragon: 0.8.2_supports-color@6.1.0
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /micromatch/4.0.5:
-    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
-    engines: {node: '>=8.6'}
-    dependencies:
-      braces: 3.0.2
-      picomatch: 2.3.1
-    dev: true
-
-  /miller-rabin/4.0.1:
-    resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==}
-    hasBin: true
-    dependencies:
-      bn.js: 4.12.0
-      brorand: 1.1.0
-    dev: true
-
-  /mime-db/1.52.0:
-    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
-    engines: {node: '>= 0.6'}
-    dev: true
-
-  /mime-types/2.1.35:
-    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
-    engines: {node: '>= 0.6'}
-    dependencies:
-      mime-db: 1.52.0
-    dev: true
-
-  /mimic-fn/2.1.0:
-    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /mimic-response/2.1.0:
-    resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /minimalistic-assert/1.0.1:
-    resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
-    dev: true
-
-  /minimalistic-crypto-utils/1.0.1:
-    resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==}
-    dev: true
-
-  /minimatch/3.1.2:
-    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
-    dependencies:
-      brace-expansion: 1.1.11
-    dev: true
-
-  /minimist/1.2.6:
-    resolution: {integrity: sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==}
-    dev: true
-
-  /minipass-collect/1.0.2:
-    resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
-    engines: {node: '>= 8'}
-    dependencies:
-      minipass: 3.1.6
-    dev: true
-
-  /minipass-flush/1.0.5:
-    resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==}
-    engines: {node: '>= 8'}
-    dependencies:
-      minipass: 3.1.6
-    dev: true
-
-  /minipass-pipeline/1.2.4:
-    resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==}
-    engines: {node: '>=8'}
-    dependencies:
-      minipass: 3.1.6
-    dev: true
-
-  /minipass/3.1.6:
-    resolution: {integrity: sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      yallist: 4.0.0
-    dev: true
-
-  /minizlib/2.1.2:
-    resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
-    engines: {node: '>= 8'}
-    dependencies:
-      minipass: 3.1.6
-      yallist: 4.0.0
-    dev: true
-
-  /mississippi/3.0.0:
-    resolution: {integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==}
-    engines: {node: '>=4.0.0'}
-    dependencies:
-      concat-stream: 1.6.2
-      duplexify: 3.7.1
-      end-of-stream: 1.4.4
-      flush-write-stream: 1.1.1
-      from2: 2.3.0
-      parallel-transform: 1.2.0
-      pump: 3.0.0
-      pumpify: 1.5.1
-      stream-each: 1.2.3
-      through2: 2.0.5
-    dev: true
-
-  /mixin-deep/1.3.2:
-    resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      for-in: 1.0.2
-      is-extendable: 1.0.1
-    dev: true
-
-  /mkdirp/0.5.6:
-    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
-    hasBin: true
-    dependencies:
-      minimist: 1.2.6
-    dev: true
-
-  /mkdirp/1.0.4:
-    resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dev: true
-
-  /move-concurrently/1.0.1:
-    resolution: {integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==}
-    dependencies:
-      aproba: 1.2.0
-      copy-concurrently: 1.0.5
-      fs-write-stream-atomic: 1.0.10
-      mkdirp: 0.5.6
-      rimraf: 2.7.1
-      run-queue: 1.0.3
-    dev: true
-
-  /ms/2.0.0:
-    resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=}
-    dev: true
-
-  /ms/2.1.2:
-    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
-    dev: true
-
-  /nan/2.16.0:
-    resolution: {integrity: sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA==}
-    dev: true
-
-  /nanomatch/1.2.13:
-    resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-diff: 4.0.0
-      array-unique: 0.3.2
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      fragment-cache: 0.2.1
-      is-windows: 1.0.2
-      kind-of: 6.0.3
-      object.pick: 1.3.0
-      regex-not: 1.0.2
-      snapdragon: 0.8.2
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /nanomatch/1.2.13_supports-color@6.1.0:
-    resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-diff: 4.0.0
-      array-unique: 0.3.2
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      fragment-cache: 0.2.1
-      is-windows: 1.0.2
-      kind-of: 6.0.3
-      object.pick: 1.3.0
-      regex-not: 1.0.2
-      snapdragon: 0.8.2_supports-color@6.1.0
-      to-regex: 3.0.2
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /natural-compare/1.4.0:
-    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
-    dev: true
-
-  /neo-async/2.6.2:
-    resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
-    dev: true
-
-  /nice-try/1.0.5:
-    resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
-    dev: true
-
-  /node-fetch/2.6.7:
-    resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
-    engines: {node: 4.x || >=6.0.0}
-    peerDependencies:
-      encoding: ^0.1.0
-    peerDependenciesMeta:
-      encoding:
-        optional: true
-    dependencies:
-      whatwg-url: 5.0.0
-    dev: true
-
-  /node-int64/0.4.0:
-    resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
-    dev: true
-
-  /node-libs-browser/2.2.1:
-    resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==}
-    dependencies:
-      assert: 1.5.0
-      browserify-zlib: 0.2.0
-      buffer: 4.9.2
-      console-browserify: 1.2.0
-      constants-browserify: 1.0.0
-      crypto-browserify: 3.12.0
-      domain-browser: 1.2.0
-      events: 3.3.0
-      https-browserify: 1.0.0
-      os-browserify: 0.3.0
-      path-browserify: 0.0.1
-      process: 0.11.10
-      punycode: 1.4.1
-      querystring-es3: 0.2.1
-      readable-stream: 2.3.7
-      stream-browserify: 2.0.2
-      stream-http: 2.8.3
-      string_decoder: 1.3.0
-      timers-browserify: 2.0.12
-      tty-browserify: 0.0.0
-      url: 0.11.0
-      util: 0.11.1
-      vm-browserify: 1.1.2
-    dev: true
-
-  /node-notifier/8.0.2:
-    resolution: {integrity: sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==}
-    requiresBuild: true
-    dependencies:
-      growly: 1.3.0
-      is-wsl: 2.2.0
-      semver: 7.3.7
-      shellwords: 0.1.1
-      uuid: 8.3.2
-      which: 2.0.2
-    dev: true
-    optional: true
-
-  /node-releases/2.0.5:
-    resolution: {integrity: sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==}
-    dev: true
-
-  /nopt/5.0.0:
-    resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==}
-    engines: {node: '>=6'}
-    hasBin: true
-    dependencies:
-      abbrev: 1.1.1
-    dev: true
-
-  /normalize-package-data/2.5.0:
-    resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
-    dependencies:
-      hosted-git-info: 2.8.9
-      resolve: 1.22.0
-      semver: 5.7.1
-      validate-npm-package-license: 3.0.4
-    dev: true
-
-  /normalize-path/2.1.1:
-    resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      remove-trailing-separator: 1.1.0
-    dev: true
-
-  /normalize-path/3.0.0:
-    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /npm-run-path/2.0.2:
-    resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
-    engines: {node: '>=4'}
-    dependencies:
-      path-key: 2.0.1
-    dev: true
-
-  /npm-run-path/4.0.1:
-    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
-    engines: {node: '>=8'}
-    dependencies:
-      path-key: 3.1.1
-    dev: true
-
-  /npmlog/5.0.1:
-    resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
-    dependencies:
-      are-we-there-yet: 2.0.0
-      console-control-strings: 1.1.0
-      gauge: 3.0.2
-      set-blocking: 2.0.0
-    dev: true
-
-  /nwsapi/2.2.0:
-    resolution: {integrity: sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==}
-    dev: true
-
-  /object-assign/4.1.1:
-    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /object-copy/0.1.0:
-    resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      copy-descriptor: 0.1.1
-      define-property: 0.2.5
-      kind-of: 3.2.2
-    dev: true
-
-  /object-visit/1.0.1:
-    resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isobject: 3.0.1
-    dev: true
-
-  /object.pick/1.3.0:
-    resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      isobject: 3.0.1
-    dev: true
-
-  /once/1.4.0:
-    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
-    dependencies:
-      wrappy: 1.0.2
-    dev: true
-
-  /onetime/5.1.2:
-    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
-    engines: {node: '>=6'}
-    dependencies:
-      mimic-fn: 2.1.0
-    dev: true
-
-  /optionator/0.8.3:
-    resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      deep-is: 0.1.4
-      fast-levenshtein: 2.0.6
-      levn: 0.3.0
-      prelude-ls: 1.1.2
-      type-check: 0.3.2
-      word-wrap: 1.2.3
-    dev: true
-
-  /optionator/0.9.1:
-    resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      deep-is: 0.1.4
-      fast-levenshtein: 2.0.6
-      levn: 0.4.1
-      prelude-ls: 1.2.1
-      type-check: 0.4.0
-      word-wrap: 1.2.3
-    dev: true
-
-  /os-browserify/0.3.0:
-    resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==}
-    dev: true
-
-  /p-each-series/2.2.0:
-    resolution: {integrity: sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /p-finally/1.0.0:
-    resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /p-limit/2.3.0:
-    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
-    engines: {node: '>=6'}
-    dependencies:
-      p-try: 2.2.0
-    dev: true
-
-  /p-limit/3.1.0:
-    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      yocto-queue: 0.1.0
-    dev: true
-
-  /p-locate/3.0.0:
-    resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      p-limit: 2.3.0
-    dev: true
-
-  /p-locate/4.1.0:
-    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
-    engines: {node: '>=8'}
-    dependencies:
-      p-limit: 2.3.0
-    dev: true
-
-  /p-map/4.0.0:
-    resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      aggregate-error: 3.1.0
-    dev: true
-
-  /p-try/2.2.0:
-    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /pako/1.0.11:
-    resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
-    dev: true
-
-  /parallel-transform/1.2.0:
-    resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==}
-    dependencies:
-      cyclist: 1.0.1
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-    dev: true
-
-  /parent-module/1.0.1:
-    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
-    engines: {node: '>=6'}
-    dependencies:
-      callsites: 3.1.0
-    dev: true
-
-  /parse-asn1/5.1.6:
-    resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==}
-    dependencies:
-      asn1.js: 5.4.1
-      browserify-aes: 1.2.0
-      evp_bytestokey: 1.0.3
-      pbkdf2: 3.1.2
-      safe-buffer: 5.2.1
-    dev: true
-
-  /parse-json/5.2.0:
-    resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@babel/code-frame': 7.16.7
-      error-ex: 1.3.2
-      json-parse-even-better-errors: 2.3.1
-      lines-and-columns: 1.2.4
-    dev: true
-
-  /parse-passwd/1.0.0:
-    resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /parse5/6.0.1:
-    resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
-    dev: true
-
-  /pascalcase/0.1.1:
-    resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /path-browserify/0.0.1:
-    resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==}
-    dev: true
-
-  /path-dirname/1.0.2:
-    resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==}
-    dev: true
-    optional: true
-
-  /path-exists/3.0.0:
-    resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /path-exists/4.0.0:
-    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /path-is-absolute/1.0.1:
-    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /path-key/2.0.1:
-    resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /path-key/3.1.1:
-    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /path-parse/1.0.7:
-    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
-    dev: true
-
-  /pbkdf2/3.1.2:
-    resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
-    engines: {node: '>=0.12'}
-    dependencies:
-      create-hash: 1.2.0
-      create-hmac: 1.1.7
-      ripemd160: 2.0.2
-      safe-buffer: 5.2.1
-      sha.js: 2.4.11
-    dev: true
-
-  /picocolors/1.0.0:
-    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
-    dev: true
-
-  /picomatch/2.3.1:
-    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
-    engines: {node: '>=8.6'}
-    dev: true
-
-  /pify/4.0.1:
-    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /pirates/4.0.5:
-    resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
-    engines: {node: '>= 6'}
-    dev: true
-
-  /pkg-dir/3.0.0:
-    resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==}
-    engines: {node: '>=6'}
-    dependencies:
-      find-up: 3.0.0
-    dev: true
-
-  /pkg-dir/4.2.0:
-    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      find-up: 4.1.0
-    dev: true
-
-  /posix-character-classes/0.1.1:
-    resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /prelude-ls/1.1.2:
-    resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
-    engines: {node: '>= 0.8.0'}
-    dev: true
-
-  /prelude-ls/1.2.1:
-    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
-    engines: {node: '>= 0.8.0'}
-    dev: true
-
-  /prettier-linter-helpers/1.0.0:
-    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
-    engines: {node: '>=6.0.0'}
-    dependencies:
-      fast-diff: 1.2.0
-    dev: true
-
-  /prettier/2.6.0:
-    resolution: {integrity: sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==}
-    engines: {node: '>=10.13.0'}
-    hasBin: true
-    dev: true
-
-  /pretty-format/26.6.2:
-    resolution: {integrity: sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==}
-    engines: {node: '>= 10'}
-    dependencies:
-      '@jest/types': 26.6.2
-      ansi-regex: 5.0.1
-      ansi-styles: 4.3.0
-      react-is: 17.0.2
-    dev: true
-
-  /process-nextick-args/2.0.1:
-    resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
-    dev: true
-
-  /process/0.11.10:
-    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
-    engines: {node: '>= 0.6.0'}
-    dev: true
-
-  /progress/2.0.3:
-    resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
-    engines: {node: '>=0.4.0'}
-    dev: true
-
-  /promise-inflight/1.0.1_bluebird@3.7.2:
-    resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
-    peerDependencies:
-      bluebird: '*'
-    peerDependenciesMeta:
-      bluebird:
-        optional: true
-    dependencies:
-      bluebird: 3.7.2
-    dev: true
-
-  /prompts/2.4.2:
-    resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
-    engines: {node: '>= 6'}
-    dependencies:
-      kleur: 3.0.3
-      sisteransi: 1.0.5
-    dev: true
-
-  /prr/1.0.1:
-    resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
-    dev: true
-
-  /psl/1.8.0:
-    resolution: {integrity: sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==}
-    dev: true
-
-  /public-encrypt/4.0.3:
-    resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==}
-    dependencies:
-      bn.js: 4.12.0
-      browserify-rsa: 4.1.0
-      create-hash: 1.2.0
-      parse-asn1: 5.1.6
-      randombytes: 2.1.0
-      safe-buffer: 5.2.1
-    dev: true
-
-  /pump/2.0.1:
-    resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==}
-    dependencies:
-      end-of-stream: 1.4.4
-      once: 1.4.0
-    dev: true
-
-  /pump/3.0.0:
-    resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
-    dependencies:
-      end-of-stream: 1.4.4
-      once: 1.4.0
-    dev: true
-
-  /pumpify/1.5.1:
-    resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==}
-    dependencies:
-      duplexify: 3.7.1
-      inherits: 2.0.4
-      pump: 2.0.1
-    dev: true
-
-  /punycode/1.3.2:
-    resolution: {integrity: sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==}
-    dev: true
-
-  /punycode/1.4.1:
-    resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
-    dev: true
-
-  /punycode/2.1.1:
-    resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /querystring-es3/0.2.1:
-    resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==}
-    engines: {node: '>=0.4.x'}
-    dev: true
-
-  /querystring/0.2.0:
-    resolution: {integrity: sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==}
-    engines: {node: '>=0.4.x'}
-    deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
-    dev: true
-
-  /randombytes/2.1.0:
-    resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
-    dependencies:
-      safe-buffer: 5.2.1
-    dev: true
-
-  /randomfill/1.0.4:
-    resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==}
-    dependencies:
-      randombytes: 2.1.0
-      safe-buffer: 5.2.1
-    dev: true
-
-  /react-is/17.0.2:
-    resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
-    dev: true
-
-  /read-pkg-up/7.0.1:
-    resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
-    engines: {node: '>=8'}
-    dependencies:
-      find-up: 4.1.0
-      read-pkg: 5.2.0
-      type-fest: 0.8.1
-    dev: true
-
-  /read-pkg/5.2.0:
-    resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@types/normalize-package-data': 2.4.1
-      normalize-package-data: 2.5.0
-      parse-json: 5.2.0
-      type-fest: 0.6.0
-    dev: true
-
-  /readable-stream/2.3.7:
-    resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
-    dependencies:
-      core-util-is: 1.0.3
-      inherits: 2.0.4
-      isarray: 1.0.0
-      process-nextick-args: 2.0.1
-      safe-buffer: 5.1.2
-      string_decoder: 1.1.1
-      util-deprecate: 1.0.2
-    dev: true
-
-  /readable-stream/3.6.0:
-    resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
-    engines: {node: '>= 6'}
-    dependencies:
-      inherits: 2.0.4
-      string_decoder: 1.3.0
-      util-deprecate: 1.0.2
-    dev: true
-
-  /readdirp/2.2.1:
-    resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==}
-    engines: {node: '>=0.10'}
-    dependencies:
-      graceful-fs: 4.2.10
-      micromatch: 3.1.10
-      readable-stream: 2.3.7
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /readdirp/3.6.0:
-    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
-    engines: {node: '>=8.10.0'}
-    dependencies:
-      picomatch: 2.3.1
-    dev: true
-    optional: true
-
-  /regex-not/1.0.2:
-    resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 3.0.2
-      safe-regex: 1.1.0
-    dev: true
-
-  /regexpp/3.2.0:
-    resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /remove-trailing-separator/1.1.0:
-    resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==}
-    dev: true
-
-  /repeat-element/1.1.4:
-    resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /repeat-string/1.6.1:
-    resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==}
-    engines: {node: '>=0.10'}
-    dev: true
-
-  /require-directory/2.1.1:
-    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /require-from-string/2.0.2:
-    resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /require-main-filename/2.0.0:
-    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
-    dev: true
-
-  /resolve-cwd/2.0.0:
-    resolution: {integrity: sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==}
-    engines: {node: '>=4'}
-    dependencies:
-      resolve-from: 3.0.0
-    dev: true
-
-  /resolve-cwd/3.0.0:
-    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
-    engines: {node: '>=8'}
-    dependencies:
-      resolve-from: 5.0.0
-    dev: true
-
-  /resolve-dir/1.0.1:
-    resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      expand-tilde: 2.0.2
-      global-modules: 1.0.0
-    dev: true
-
-  /resolve-from/3.0.0:
-    resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /resolve-from/4.0.0:
-    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /resolve-from/5.0.0:
-    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /resolve-url/0.2.1:
-    resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==}
-    deprecated: https://github.com/lydell/resolve-url#deprecated
-    dev: true
-
-  /resolve/1.22.0:
-    resolution: {integrity: sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==}
-    hasBin: true
-    dependencies:
-      is-core-module: 2.9.0
-      path-parse: 1.0.7
-      supports-preserve-symlinks-flag: 1.0.0
-    dev: true
-
-  /ret/0.1.15:
-    resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==}
-    engines: {node: '>=0.12'}
-    dev: true
-
-  /rimraf/2.7.1:
-    resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
-    hasBin: true
-    dependencies:
-      glob: 7.2.3
-    dev: true
-
-  /rimraf/3.0.2:
-    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
-    hasBin: true
-    dependencies:
-      glob: 7.2.3
-    dev: true
-
-  /ripemd160/2.0.2:
-    resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
-    dependencies:
-      hash-base: 3.1.0
-      inherits: 2.0.4
-    dev: true
-
-  /rsvp/4.8.5:
-    resolution: {integrity: sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==}
-    engines: {node: 6.* || >= 7.*}
-    dev: true
-
-  /run-queue/1.0.3:
-    resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==}
-    dependencies:
-      aproba: 1.2.0
-    dev: true
-
-  /safe-buffer/5.1.2:
-    resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
-    dev: true
-
-  /safe-buffer/5.2.1:
-    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
-    dev: true
-
-  /safe-regex/1.1.0:
-    resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==}
-    dependencies:
-      ret: 0.1.15
-    dev: true
-
-  /safer-buffer/2.1.2:
-    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
-    dev: true
-
-  /sane/4.1.0:
-    resolution: {integrity: sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==}
-    engines: {node: 6.* || 8.* || >= 10.*}
-    deprecated: some dependency vulnerabilities fixed, support for node < 10 dropped, and newer ECMAScript syntax/features added
-    hasBin: true
-    dependencies:
-      '@cnakazawa/watch': 1.0.4
-      anymatch: 2.0.0
-      capture-exit: 2.0.0
-      exec-sh: 0.3.6
-      execa: 1.0.0
-      fb-watchman: 2.0.1
-      micromatch: 3.1.10
-      minimist: 1.2.6
-      walker: 1.0.8
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /saxes/5.0.1:
-    resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
-    engines: {node: '>=10'}
-    dependencies:
-      xmlchars: 2.2.0
-    dev: true
-
-  /schema-utils/1.0.0:
-    resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==}
-    engines: {node: '>= 4'}
-    dependencies:
-      ajv: 6.12.6
-      ajv-errors: 1.0.1_ajv@6.12.6
-      ajv-keywords: 3.5.2_ajv@6.12.6
-    dev: true
-
-  /schema-utils/3.1.1:
-    resolution: {integrity: sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==}
-    engines: {node: '>= 10.13.0'}
-    dependencies:
-      '@types/json-schema': 7.0.11
-      ajv: 6.12.6
-      ajv-keywords: 3.5.2_ajv@6.12.6
-    dev: true
-
-  /semver/5.7.1:
-    resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
-    hasBin: true
-    dev: true
-
-  /semver/6.3.0:
-    resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
-    hasBin: true
-    dev: true
-
-  /semver/7.3.7:
-    resolution: {integrity: sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      lru-cache: 6.0.0
-    dev: true
-
-  /serialize-javascript/4.0.0:
-    resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==}
-    dependencies:
-      randombytes: 2.1.0
-    dev: true
-
-  /serialize-javascript/5.0.1:
-    resolution: {integrity: sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==}
-    dependencies:
-      randombytes: 2.1.0
-    dev: true
-
-  /set-blocking/2.0.0:
-    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
-    dev: true
-
-  /set-value/2.0.1:
-    resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 2.0.1
-      is-extendable: 0.1.1
-      is-plain-object: 2.0.4
-      split-string: 3.1.0
-    dev: true
-
-  /setimmediate/1.0.5:
-    resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==}
-    dev: true
-
-  /sha.js/2.4.11:
-    resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
-    hasBin: true
-    dependencies:
-      inherits: 2.0.4
-      safe-buffer: 5.2.1
-    dev: true
-
-  /shebang-command/1.2.0:
-    resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      shebang-regex: 1.0.0
-    dev: true
-
-  /shebang-command/2.0.0:
-    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
-    engines: {node: '>=8'}
-    dependencies:
-      shebang-regex: 3.0.0
-    dev: true
-
-  /shebang-regex/1.0.0:
-    resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /shebang-regex/3.0.0:
-    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /shellwords/0.1.1:
-    resolution: {integrity: sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==}
-    dev: true
-    optional: true
-
-  /signal-exit/3.0.7:
-    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
-    dev: true
-
-  /simple-concat/1.0.1:
-    resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
-    dev: true
-
-  /simple-get/3.1.1:
-    resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==}
-    dependencies:
-      decompress-response: 4.2.1
-      once: 1.4.0
-      simple-concat: 1.0.1
-    dev: true
-
-  /sisteransi/1.0.5:
-    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
-    dev: true
-
-  /slash/3.0.0:
-    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /slice-ansi/4.0.0:
-    resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      ansi-styles: 4.3.0
-      astral-regex: 2.0.0
-      is-fullwidth-code-point: 3.0.0
-    dev: true
-
-  /snapdragon-node/2.1.1:
-    resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      define-property: 1.0.0
-      isobject: 3.0.1
-      snapdragon-util: 3.0.1
-    dev: true
-
-  /snapdragon-util/3.0.1:
-    resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /snapdragon/0.8.2:
-    resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      base: 0.11.2
-      debug: 2.6.9
-      define-property: 0.2.5
-      extend-shallow: 2.0.1
-      map-cache: 0.2.2
-      source-map: 0.5.7
-      source-map-resolve: 0.5.3
-      use: 3.1.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /snapdragon/0.8.2_supports-color@6.1.0:
-    resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      base: 0.11.2
-      debug: 2.6.9_supports-color@6.1.0
-      define-property: 0.2.5
-      extend-shallow: 2.0.1
-      map-cache: 0.2.2
-      source-map: 0.5.7
-      source-map-resolve: 0.5.3
-      use: 3.1.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /source-list-map/2.0.1:
-    resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==}
-    dev: true
-
-  /source-map-resolve/0.5.3:
-    resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==}
-    deprecated: See https://github.com/lydell/source-map-resolve#deprecated
-    dependencies:
-      atob: 2.1.2
-      decode-uri-component: 0.2.0
-      resolve-url: 0.2.1
-      source-map-url: 0.4.1
-      urix: 0.1.0
-    dev: true
-
-  /source-map-support/0.5.21:
-    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
-    dependencies:
-      buffer-from: 1.1.2
-      source-map: 0.6.1
-    dev: true
-
-  /source-map-url/0.4.1:
-    resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==}
-    deprecated: See https://github.com/lydell/source-map-url#deprecated
-    dev: true
-
-  /source-map/0.5.7:
-    resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /source-map/0.6.1:
-    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /source-map/0.7.4:
-    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
-    engines: {node: '>= 8'}
-    dev: true
-
-  /spdx-correct/3.1.1:
-    resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
-    dependencies:
-      spdx-expression-parse: 3.0.1
-      spdx-license-ids: 3.0.11
-    dev: true
-
-  /spdx-exceptions/2.3.0:
-    resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
-    dev: true
-
-  /spdx-expression-parse/3.0.1:
-    resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
-    dependencies:
-      spdx-exceptions: 2.3.0
-      spdx-license-ids: 3.0.11
-    dev: true
-
-  /spdx-license-ids/3.0.11:
-    resolution: {integrity: sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==}
-    dev: true
-
-  /split-string/3.1.0:
-    resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      extend-shallow: 3.0.2
-    dev: true
-
-  /sprintf-js/1.0.3:
-    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
-    dev: true
-
-  /ssri/6.0.2:
-    resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==}
-    dependencies:
-      figgy-pudding: 3.5.2
-    dev: true
-
-  /ssri/8.0.1:
-    resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==}
-    engines: {node: '>= 8'}
-    dependencies:
-      minipass: 3.1.6
-    dev: true
-
-  /stack-utils/2.0.5:
-    resolution: {integrity: sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==}
-    engines: {node: '>=10'}
-    dependencies:
-      escape-string-regexp: 2.0.0
-    dev: true
-
-  /static-extend/0.1.2:
-    resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      define-property: 0.2.5
-      object-copy: 0.1.0
-    dev: true
-
-  /stream-browserify/2.0.2:
-    resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==}
-    dependencies:
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-    dev: true
-
-  /stream-each/1.2.3:
-    resolution: {integrity: sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==}
-    dependencies:
-      end-of-stream: 1.4.4
-      stream-shift: 1.0.1
-    dev: true
-
-  /stream-http/2.8.3:
-    resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==}
-    dependencies:
-      builtin-status-codes: 3.0.0
-      inherits: 2.0.4
-      readable-stream: 2.3.7
-      to-arraybuffer: 1.0.1
-      xtend: 4.0.2
-    dev: true
-
-  /stream-shift/1.0.1:
-    resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==}
-    dev: true
-
-  /string-length/4.0.2:
-    resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
-    engines: {node: '>=10'}
-    dependencies:
-      char-regex: 1.0.2
-      strip-ansi: 6.0.1
-    dev: true
-
-  /string-width/3.1.0:
-    resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==}
-    engines: {node: '>=6'}
-    dependencies:
-      emoji-regex: 7.0.3
-      is-fullwidth-code-point: 2.0.0
-      strip-ansi: 5.2.0
-    dev: true
-
-  /string-width/4.2.3:
-    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
-    engines: {node: '>=8'}
-    dependencies:
-      emoji-regex: 8.0.0
-      is-fullwidth-code-point: 3.0.0
-      strip-ansi: 6.0.1
-    dev: true
-
-  /string_decoder/1.1.1:
-    resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
-    dependencies:
-      safe-buffer: 5.1.2
-    dev: true
-
-  /string_decoder/1.3.0:
-    resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
-    dependencies:
-      safe-buffer: 5.2.1
-    dev: true
-
-  /strip-ansi/5.2.0:
-    resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==}
-    engines: {node: '>=6'}
-    dependencies:
-      ansi-regex: 4.1.1
-    dev: true
-
-  /strip-ansi/6.0.1:
-    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
-    engines: {node: '>=8'}
-    dependencies:
-      ansi-regex: 5.0.1
-    dev: true
-
-  /strip-bom/4.0.0:
-    resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /strip-eof/1.0.0:
-    resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /strip-final-newline/2.0.0:
-    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /strip-json-comments/3.1.1:
-    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /supports-color/5.5.0:
-    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
-    engines: {node: '>=4'}
-    dependencies:
-      has-flag: 3.0.0
-    dev: true
-
-  /supports-color/6.1.0:
-    resolution: {integrity: sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      has-flag: 3.0.0
-    dev: true
-
-  /supports-color/7.2.0:
-    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
-    engines: {node: '>=8'}
-    dependencies:
-      has-flag: 4.0.0
-    dev: true
-
-  /supports-hyperlinks/2.2.0:
-    resolution: {integrity: sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      has-flag: 4.0.0
-      supports-color: 7.2.0
-    dev: true
-
-  /supports-preserve-symlinks-flag/1.0.0:
-    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
-    engines: {node: '>= 0.4'}
-    dev: true
-
-  /symbol-tree/3.2.4:
-    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
-    dev: true
-
-  /table/6.8.0:
-    resolution: {integrity: sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==}
-    engines: {node: '>=10.0.0'}
-    dependencies:
-      ajv: 8.11.0
-      lodash.truncate: 4.4.2
-      slice-ansi: 4.0.0
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-    dev: true
-
-  /tapable/1.1.3:
-    resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==}
-    engines: {node: '>=6'}
-    dev: true
-
-  /tar/6.1.11:
-    resolution: {integrity: sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==}
-    engines: {node: '>= 10'}
-    dependencies:
-      chownr: 2.0.0
-      fs-minipass: 2.1.0
-      minipass: 3.1.6
-      minizlib: 2.1.2
-      mkdirp: 1.0.4
-      yallist: 4.0.0
-    dev: true
-
-  /terminal-link/2.1.1:
-    resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
-    engines: {node: '>=8'}
-    dependencies:
-      ansi-escapes: 4.3.2
-      supports-hyperlinks: 2.2.0
-    dev: true
-
-  /terser-webpack-plugin/1.4.5_webpack@4.46.0:
-    resolution: {integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==}
-    engines: {node: '>= 6.9.0'}
-    peerDependencies:
-      webpack: ^4.0.0
-    dependencies:
-      cacache: 12.0.4
-      find-cache-dir: 2.1.0
-      is-wsl: 1.1.0
-      schema-utils: 1.0.0
-      serialize-javascript: 4.0.0
-      source-map: 0.6.1
-      terser: 4.8.0
-      webpack: 4.46.0_webpack-cli@3.3.12
-      webpack-sources: 1.4.3
-      worker-farm: 1.7.0
-    dev: true
-
-  /terser-webpack-plugin/4.2.3_webpack@4.46.0:
-    resolution: {integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==}
-    engines: {node: '>= 10.13.0'}
-    peerDependencies:
-      webpack: ^4.0.0 || ^5.0.0
-    dependencies:
-      cacache: 15.3.0
-      find-cache-dir: 3.3.2
-      jest-worker: 26.6.2
-      p-limit: 3.1.0
-      schema-utils: 3.1.1
-      serialize-javascript: 5.0.1
-      source-map: 0.6.1
-      terser: 5.14.0
-      webpack: 4.46.0_webpack-cli@3.3.12
-      webpack-sources: 1.4.3
-    transitivePeerDependencies:
-      - bluebird
-    dev: true
-
-  /terser/4.8.0:
-    resolution: {integrity: sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==}
-    engines: {node: '>=6.0.0'}
-    hasBin: true
-    dependencies:
-      acorn: 8.7.1
-      commander: 2.20.3
-      source-map: 0.6.1
-      source-map-support: 0.5.21
-    dev: true
-
-  /terser/5.14.0:
-    resolution: {integrity: sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==}
-    engines: {node: '>=10'}
-    hasBin: true
-    dependencies:
-      '@jridgewell/source-map': 0.3.2
-      acorn: 8.7.1
-      commander: 2.20.3
-      source-map-support: 0.5.21
-    dev: true
-
-  /test-exclude/6.0.0:
-    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
-    engines: {node: '>=8'}
-    dependencies:
-      '@istanbuljs/schema': 0.1.3
-      glob: 7.2.3
-      minimatch: 3.1.2
-    dev: true
-
-  /text-table/0.2.0:
-    resolution: {integrity: sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=}
-    dev: true
-
-  /throat/5.0.0:
-    resolution: {integrity: sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==}
-    dev: true
-
-  /through2/2.0.5:
-    resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
-    dependencies:
-      readable-stream: 2.3.7
-      xtend: 4.0.2
-    dev: true
-
-  /timers-browserify/2.0.12:
-    resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==}
-    engines: {node: '>=0.6.0'}
-    dependencies:
-      setimmediate: 1.0.5
-    dev: true
-
-  /tmpl/1.0.5:
-    resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
-    dev: true
-
-  /to-arraybuffer/1.0.1:
-    resolution: {integrity: sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=}
-    dev: true
-
-  /to-fast-properties/2.0.0:
-    resolution: {integrity: sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=}
-    engines: {node: '>=4'}
-    dev: true
-
-  /to-object-path/0.3.0:
-    resolution: {integrity: sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      kind-of: 3.2.2
-    dev: true
-
-  /to-regex-range/2.1.1:
-    resolution: {integrity: sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      is-number: 3.0.0
-      repeat-string: 1.6.1
-    dev: true
-
-  /to-regex-range/5.0.1:
-    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
-    engines: {node: '>=8.0'}
-    dependencies:
-      is-number: 7.0.0
-    dev: true
-
-  /to-regex/3.0.2:
-    resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      define-property: 2.0.2
-      extend-shallow: 3.0.2
-      regex-not: 1.0.2
-      safe-regex: 1.1.0
-    dev: true
-
-  /tough-cookie/4.0.0:
-    resolution: {integrity: sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==}
-    engines: {node: '>=6'}
-    dependencies:
-      psl: 1.8.0
-      punycode: 2.1.1
-      universalify: 0.1.2
-    dev: true
-
-  /tr46/0.0.3:
-    resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
-    dev: true
-
-  /tr46/2.1.0:
-    resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
-    engines: {node: '>=8'}
-    dependencies:
-      punycode: 2.1.1
-    dev: true
-
-  /ts-jest/26.5.6_eee4nuuc45mdmx5rmbx7hzjjtq:
-    resolution: {integrity: sha512-rua+rCP8DxpA8b4DQD/6X2HQS8Zy/xzViVYfEs2OQu68tkCuKLV0Md8pmX55+W24uRIyAsf/BajRfxOs+R2MKA==}
-    engines: {node: '>= 10'}
-    hasBin: true
-    peerDependencies:
-      jest: '>=26 <27'
-      typescript: '>=3.8 <5.0'
-    dependencies:
-      bs-logger: 0.2.6
-      buffer-from: 1.1.2
-      fast-json-stable-stringify: 2.1.0
-      jest: 26.6.3_canvas@2.9.1
-      jest-util: 26.6.2
-      json5: 2.2.1
-      lodash: 4.17.21
-      make-error: 1.3.6
-      mkdirp: 1.0.4
-      semver: 7.3.7
-      typescript: 3.9.10
-      yargs-parser: 20.2.9
-    dev: true
-
-  /ts-loader/8.4.0_ezrl6vhczkqqw35uifasf5iyga:
-    resolution: {integrity: sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw==}
-    engines: {node: '>=10.0.0'}
-    peerDependencies:
-      typescript: '*'
-      webpack: '*'
-    dependencies:
-      chalk: 4.1.2
-      enhanced-resolve: 4.5.0
-      loader-utils: 2.0.2
-      micromatch: 4.0.5
-      semver: 7.3.7
-      typescript: 3.9.10
-      webpack: 4.46.0_webpack-cli@3.3.12
-    dev: true
-
-  /tslib/1.14.1:
-    resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
-    dev: true
-
-  /tsutils/3.21.0_typescript@3.9.10:
-    resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
-    engines: {node: '>= 6'}
-    peerDependencies:
-      typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
-    dependencies:
-      tslib: 1.14.1
-      typescript: 3.9.10
-    dev: true
-
-  /tty-browserify/0.0.0:
-    resolution: {integrity: sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=}
-    dev: true
-
-  /type-check/0.3.2:
-    resolution: {integrity: sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      prelude-ls: 1.1.2
-    dev: true
-
-  /type-check/0.4.0:
-    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
-    engines: {node: '>= 0.8.0'}
-    dependencies:
-      prelude-ls: 1.2.1
-    dev: true
-
-  /type-detect/4.0.8:
-    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
-    engines: {node: '>=4'}
-    dev: true
-
-  /type-fest/0.20.2:
-    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /type-fest/0.21.3:
-    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /type-fest/0.6.0:
-    resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /type-fest/0.8.1:
-    resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /typedarray-to-buffer/3.1.5:
-    resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
-    dependencies:
-      is-typedarray: 1.0.0
-    dev: true
-
-  /typedarray/0.0.6:
-    resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=}
-    dev: true
-
-  /typescript/3.9.10:
-    resolution: {integrity: sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==}
-    engines: {node: '>=4.2.0'}
-    hasBin: true
-    dev: true
-
-  /union-value/1.0.1:
-    resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      arr-union: 3.1.0
-      get-value: 2.0.6
-      is-extendable: 0.1.1
-      set-value: 2.0.1
-    dev: true
-
-  /unique-filename/1.1.1:
-    resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==}
-    dependencies:
-      unique-slug: 2.0.2
-    dev: true
-
-  /unique-slug/2.0.2:
-    resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==}
-    dependencies:
-      imurmurhash: 0.1.4
-    dev: true
-
-  /universalify/0.1.2:
-    resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
-    engines: {node: '>= 4.0.0'}
-    dev: true
-
-  /unset-value/1.0.0:
-    resolution: {integrity: sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=}
-    engines: {node: '>=0.10.0'}
-    dependencies:
-      has-value: 0.3.1
-      isobject: 3.0.1
-    dev: true
-
-  /upath/1.2.0:
-    resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
-    engines: {node: '>=4'}
-    dev: true
-    optional: true
-
-  /uri-js/4.4.1:
-    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
-    dependencies:
-      punycode: 2.1.1
-    dev: true
-
-  /urix/0.1.0:
-    resolution: {integrity: sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=}
-    deprecated: Please see https://github.com/lydell/urix#deprecated
-    dev: true
-
-  /url/0.11.0:
-    resolution: {integrity: sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=}
-    dependencies:
-      punycode: 1.3.2
-      querystring: 0.2.0
-    dev: true
-
-  /use/3.1.1:
-    resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /util-deprecate/1.0.2:
-    resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=}
-    dev: true
-
-  /util/0.10.3:
-    resolution: {integrity: sha1-evsa/lCAUkZInj23/g7TeTNqwPk=}
-    dependencies:
-      inherits: 2.0.1
-    dev: true
-
-  /util/0.11.1:
-    resolution: {integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==}
-    dependencies:
-      inherits: 2.0.3
-    dev: true
-
-  /uuid/8.3.2:
-    resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
-    hasBin: true
-    dev: true
-    optional: true
-
-  /v8-compile-cache/2.3.0:
-    resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
-    dev: true
-
-  /v8-to-istanbul/7.1.2:
-    resolution: {integrity: sha512-TxNb7YEUwkLXCQYeudi6lgQ/SZrzNO4kMdlqVxaZPUIUjCv6iSSypUQX70kNBSERpQ8fk48+d61FXk+tgqcWow==}
-    engines: {node: '>=10.10.0'}
-    dependencies:
-      '@types/istanbul-lib-coverage': 2.0.4
-      convert-source-map: 1.8.0
-      source-map: 0.7.4
-    dev: true
-
-  /validate-npm-package-license/3.0.4:
-    resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
-    dependencies:
-      spdx-correct: 3.1.1
-      spdx-expression-parse: 3.0.1
-    dev: true
-
-  /vm-browserify/1.1.2:
-    resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
-    dev: true
-
-  /w3c-hr-time/1.0.2:
-    resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
-    dependencies:
-      browser-process-hrtime: 1.0.0
-    dev: true
-
-  /w3c-xmlserializer/2.0.0:
-    resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==}
-    engines: {node: '>=10'}
-    dependencies:
-      xml-name-validator: 3.0.0
-    dev: true
-
-  /walker/1.0.8:
-    resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
-    dependencies:
-      makeerror: 1.0.12
-    dev: true
-
-  /watchpack-chokidar2/2.0.1:
-    resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==}
-    requiresBuild: true
-    dependencies:
-      chokidar: 2.1.8
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-    optional: true
-
-  /watchpack/1.7.5:
-    resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==}
-    dependencies:
-      graceful-fs: 4.2.10
-      neo-async: 2.6.2
-    optionalDependencies:
-      chokidar: 3.5.3
-      watchpack-chokidar2: 2.0.1
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /webidl-conversions/3.0.1:
-    resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
-    dev: true
-
-  /webidl-conversions/5.0.0:
-    resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
-    engines: {node: '>=8'}
-    dev: true
-
-  /webidl-conversions/6.1.0:
-    resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==}
-    engines: {node: '>=10.4'}
-    dev: true
-
-  /webpack-cli/3.3.12_webpack@4.46.0:
-    resolution: {integrity: sha512-NVWBaz9k839ZH/sinurM+HcDvJOTXwSjYp1ku+5XKeOC03z8v5QitnK/x+lAxGXFyhdayoIf/GOpv85z3/xPag==}
-    engines: {node: '>=6.11.5'}
-    hasBin: true
-    peerDependencies:
-      webpack: 4.x.x
-    dependencies:
-      chalk: 2.4.2
-      cross-spawn: 6.0.5
-      enhanced-resolve: 4.5.0
-      findup-sync: 3.0.0_supports-color@6.1.0
-      global-modules: 2.0.0
-      import-local: 2.0.0
-      interpret: 1.4.0
-      loader-utils: 1.4.0
-      supports-color: 6.1.0
-      v8-compile-cache: 2.3.0
-      webpack: 4.46.0_webpack-cli@3.3.12
-      yargs: 13.3.2
-    dev: true
-
-  /webpack-sources/1.4.3:
-    resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==}
-    dependencies:
-      source-list-map: 2.0.1
-      source-map: 0.6.1
-    dev: true
-
-  /webpack/4.46.0_webpack-cli@3.3.12:
-    resolution: {integrity: sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==}
-    engines: {node: '>=6.11.5'}
-    hasBin: true
-    peerDependencies:
-      webpack-cli: '*'
-      webpack-command: '*'
-    peerDependenciesMeta:
-      webpack-cli:
-        optional: true
-      webpack-command:
-        optional: true
-    dependencies:
-      '@webassemblyjs/ast': 1.9.0
-      '@webassemblyjs/helper-module-context': 1.9.0
-      '@webassemblyjs/wasm-edit': 1.9.0
-      '@webassemblyjs/wasm-parser': 1.9.0
-      acorn: 6.4.2
-      ajv: 6.12.6
-      ajv-keywords: 3.5.2_ajv@6.12.6
-      chrome-trace-event: 1.0.3
-      enhanced-resolve: 4.5.0
-      eslint-scope: 4.0.3
-      json-parse-better-errors: 1.0.2
-      loader-runner: 2.4.0
-      loader-utils: 1.4.0
-      memory-fs: 0.4.1
-      micromatch: 3.1.10
-      mkdirp: 0.5.6
-      neo-async: 2.6.2
-      node-libs-browser: 2.2.1
-      schema-utils: 1.0.0
-      tapable: 1.1.3
-      terser-webpack-plugin: 1.4.5_webpack@4.46.0
-      watchpack: 1.7.5
-      webpack-cli: 3.3.12_webpack@4.46.0
-      webpack-sources: 1.4.3
-    transitivePeerDependencies:
-      - supports-color
-    dev: true
-
-  /whatwg-encoding/1.0.5:
-    resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
-    dependencies:
-      iconv-lite: 0.4.24
-    dev: true
-
-  /whatwg-mimetype/2.3.0:
-    resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
-    dev: true
-
-  /whatwg-url/5.0.0:
-    resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=}
-    dependencies:
-      tr46: 0.0.3
-      webidl-conversions: 3.0.1
-    dev: true
-
-  /whatwg-url/8.7.0:
-    resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
-    engines: {node: '>=10'}
-    dependencies:
-      lodash: 4.17.21
-      tr46: 2.1.0
-      webidl-conversions: 6.1.0
-    dev: true
-
-  /which-module/2.0.0:
-    resolution: {integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=}
-    dev: true
-
-  /which/1.3.1:
-    resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
-    hasBin: true
-    dependencies:
-      isexe: 2.0.0
-    dev: true
-
-  /which/2.0.2:
-    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
-    engines: {node: '>= 8'}
-    hasBin: true
-    dependencies:
-      isexe: 2.0.0
-    dev: true
-
-  /wide-align/1.1.5:
-    resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==}
-    dependencies:
-      string-width: 4.2.3
-    dev: true
-
-  /word-wrap/1.2.3:
-    resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
-    engines: {node: '>=0.10.0'}
-    dev: true
-
-  /worker-farm/1.7.0:
-    resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==}
-    dependencies:
-      errno: 0.1.8
-    dev: true
-
-  /wrap-ansi/5.1.0:
-    resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==}
-    engines: {node: '>=6'}
-    dependencies:
-      ansi-styles: 3.2.1
-      string-width: 3.1.0
-      strip-ansi: 5.2.0
-    dev: true
-
-  /wrap-ansi/6.2.0:
-    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
-    engines: {node: '>=8'}
-    dependencies:
-      ansi-styles: 4.3.0
-      string-width: 4.2.3
-      strip-ansi: 6.0.1
-    dev: true
-
-  /wrappy/1.0.2:
-    resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
-    dev: true
-
-  /write-file-atomic/3.0.3:
-    resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}
-    dependencies:
-      imurmurhash: 0.1.4
-      is-typedarray: 1.0.0
-      signal-exit: 3.0.7
-      typedarray-to-buffer: 3.1.5
-    dev: true
-
-  /ws/7.5.8:
-    resolution: {integrity: sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==}
-    engines: {node: '>=8.3.0'}
-    peerDependencies:
-      bufferutil: ^4.0.1
-      utf-8-validate: ^5.0.2
-    peerDependenciesMeta:
-      bufferutil:
-        optional: true
-      utf-8-validate:
-        optional: true
-    dev: true
-
-  /xml-name-validator/3.0.0:
-    resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
-    dev: true
-
-  /xmlchars/2.2.0:
-    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
-    dev: true
-
-  /xtend/4.0.2:
-    resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
-    engines: {node: '>=0.4'}
-    dev: true
-
-  /y18n/4.0.3:
-    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
-    dev: true
-
-  /yallist/3.1.1:
-    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
-    dev: true
-
-  /yallist/4.0.0:
-    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
-    dev: true
-
-  /yargs-parser/13.1.2:
-    resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==}
-    dependencies:
-      camelcase: 5.3.1
-      decamelize: 1.2.0
-    dev: true
-
-  /yargs-parser/18.1.3:
-    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
-    engines: {node: '>=6'}
-    dependencies:
-      camelcase: 5.3.1
-      decamelize: 1.2.0
-    dev: true
-
-  /yargs-parser/20.2.9:
-    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
-    engines: {node: '>=10'}
-    dev: true
-
-  /yargs/13.3.2:
-    resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==}
-    dependencies:
-      cliui: 5.0.0
-      find-up: 3.0.0
-      get-caller-file: 2.0.5
-      require-directory: 2.1.1
-      require-main-filename: 2.0.0
-      set-blocking: 2.0.0
-      string-width: 3.1.0
-      which-module: 2.0.0
-      y18n: 4.0.3
-      yargs-parser: 13.1.2
-    dev: true
-
-  /yargs/15.4.1:
-    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
-    engines: {node: '>=8'}
-    dependencies:
-      cliui: 6.0.0
-      decamelize: 1.2.0
-      find-up: 4.1.0
-      get-caller-file: 2.0.5
-      require-directory: 2.1.1
-      require-main-filename: 2.0.0
-      set-blocking: 2.0.0
-      string-width: 4.2.3
-      which-module: 2.0.0
-      y18n: 4.0.3
-      yargs-parser: 18.1.3
-    dev: true
-
-  /yocto-queue/0.1.0:
-    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
-    engines: {node: '>=10'}
-    dev: true

From 40043806cd8e6042b1261b596d743289bae38ce5 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 9 Jun 2022 19:44:24 -0400
Subject: [PATCH 022/108] Add tests for addDropzones

---
 __tests__/__snapshots__/editable.test.ts.snap | 449 ++++++++++++++++++
 __tests__/editable.test.ts                    |  43 +-
 2 files changed, 491 insertions(+), 1 deletion(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index b6ae0cf6..634d04b7 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -56,6 +56,455 @@ exports[`Testing addDropzoneEvents add 2 events 1`] = `
 </div>
 `;
 
+exports[`Testing addDropzones verify dropzones 1`] = `
+<div>
+  <svg
+    class="qviz"
+    height="180"
+    style="max-width: fit-content;"
+    width="260"
+  >
+    <style>
+      
+    line,
+    circle,
+    rect {
+        stroke: #000000;
+        stroke-width: 1;
+    }
+    text {
+        fill: #000000;
+        dominant-baseline: middle;
+        text-anchor: middle;
+        font-family: Arial;
+    }
+    .control-dot {
+        fill: #000000;
+    }
+    .oplus line, .oplus circle {
+        fill: #FFFFFF;
+        stroke-width: 2;
+    }
+    .gate-unitary {
+        fill: #D9F1FA;
+    }
+    .gate-measure {
+        fill: #FFDE86;
+    }
+    rect.gate-swap {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .arc-measure {
+        stroke: #000000;
+        fill: none;
+        stroke-width: 1;
+    }
+    .register-classical {
+        stroke-width: 0.5;
+    }
+    
+    .hidden {
+        display: none;
+    }
+    .classically-controlled-unknown {
+        opacity: 0.25;
+    }
+
+    
+    .classically-controlled-one .classical-container,
+    .classically-controlled-one .classical-line {
+        stroke: #4059BD;
+        stroke-width: 1.3;
+        fill: #4059BD;
+        fill-opacity: 0.1;
+    }
+    .classically-controlled-zero .classical-container,
+    .classically-controlled-zero .classical-line {
+        stroke: #C40000;
+        stroke-width: 1.3;
+        fill: #C40000;
+        fill-opacity: 0.1;
+    }
+    
+    .classically-controlled-btn {
+        cursor: pointer;
+    }
+    .classically-controlled-unknown .classically-controlled-btn {
+        fill: #E5E5E5;
+    }
+    .classically-controlled-one .classically-controlled-btn {
+        fill: #4059BD;
+    }
+    .classically-controlled-zero .classically-controlled-btn {
+        fill: #C40000;
+    }
+    
+    .classically-controlled-btn text {
+        dominant-baseline: middle;
+        text-anchor: middle;
+        stroke: none;
+        font-family: Arial;
+    }
+    .classically-controlled-unknown .classically-controlled-btn text {
+        fill: #000000;
+    }
+    .classically-controlled-one .classically-controlled-btn text {
+        fill: #FFFFFF;
+    }
+    .classically-controlled-zero .classically-controlled-btn text {
+        fill: #FFFFFF;
+    }
+    
+    .qviz .gate-collapse,
+    .qviz .gate-expand {
+        opacity: 0;
+        transition: opacity 1s;
+    }
+
+    .qviz:hover .gate-collapse,
+    .qviz:hover .gate-expand {
+        visibility: visible;
+        opacity: 0.2;
+        transition: visibility 1s;
+        transition: opacity 1s;
+    }
+
+    .gate-expand, .gate-collapse {
+        cursor: pointer;
+    }
+
+    .gate-collapse circle,
+    .gate-expand circle {
+        fill: white;
+        stroke-width: 2px;
+        stroke: black;
+    }
+    .gate-collapse path,
+    .gate-expand path {
+        stroke-width: 4px;
+        stroke: black;
+    }
+
+    .gate:hover &gt; .gate-collapse,
+    .gate:hover &gt; .gate-expand {
+        visibility: visible;
+        opacity: 1;
+        transition: opacity 1s;
+    }.dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
+    }
+    </style>
+    <g>
+      <text
+        dominant-baseline="middle"
+        font-size="16"
+        text-anchor="start"
+        x="20"
+        y="40"
+      >
+        |0⟩
+      </text>
+      <text
+        dominant-baseline="middle"
+        font-size="16"
+        text-anchor="start"
+        x="20"
+        y="100"
+      >
+        |0⟩
+      </text>
+    </g>
+    <g>
+      <g>
+        <line
+          x1="40"
+          x2="260"
+          y1="40"
+          y2="40"
+        />
+        <text
+          dominant-baseline="hanging"
+          font-size="75%"
+          text-anchor="start"
+          x="40"
+          y="24"
+        >
+          q0
+        </text>
+      </g>
+      <g>
+        <line
+          x1="40"
+          x2="260"
+          y1="100"
+          y2="100"
+        />
+        <text
+          dominant-baseline="hanging"
+          font-size="75%"
+          text-anchor="start"
+          x="40"
+          y="84"
+        >
+          q1
+        </text>
+      </g>
+      <g>
+        <line
+          class="register-classical"
+          x1="221"
+          x2="221"
+          y1="100"
+          y2="139"
+        />
+        <line
+          class="register-classical"
+          x1="219"
+          x2="219"
+          y1="100"
+          y2="141"
+        />
+        <line
+          class="register-classical"
+          x1="221"
+          x2="260"
+          y1="139"
+          y2="139"
+        />
+        <line
+          class="register-classical"
+          x1="219"
+          x2="260"
+          y1="141"
+          y2="141"
+        />
+      </g>
+    </g>
+    <g>
+      <g
+        class="gate"
+        data-id="0"
+        data-zoom-in="false"
+        data-zoom-out="false"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="80"
+              y="20"
+            />
+            <text
+              font-size="14"
+              x="100"
+              y="40"
+            >
+              H
+            </text>
+          </g>
+        </g>
+        <rect
+          class="dropzone"
+          data-dropzone-position="left"
+          data-id="0"
+          height="0"
+          width="10"
+          x="-10"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="right"
+          data-id="0"
+          height="0"
+          width="10"
+          x="0"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="left"
+          data-id="0"
+          height="0"
+          width="10"
+          x="-10"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="right"
+          data-id="0"
+          height="0"
+          width="10"
+          x="0"
+          y="0"
+        />
+      </g>
+      <g
+        class="gate"
+        data-id="1"
+        data-zoom-in="false"
+        data-zoom-out="false"
+      >
+        <line
+          x1="160"
+          x2="160"
+          y1="40"
+          y2="100"
+        />
+        <circle
+          class="control-dot"
+          cx="160"
+          cy="40"
+          r="5"
+        />
+        <g
+          class="oplus"
+        >
+          <circle
+            cx="160"
+            cy="100"
+            r="15"
+          />
+          <line
+            x1="160"
+            x2="160"
+            y1="85"
+            y2="115"
+          />
+          <line
+            x1="145"
+            x2="175"
+            y1="100"
+            y2="100"
+          />
+        </g>
+        <rect
+          class="dropzone"
+          data-dropzone-position="left"
+          data-id="1"
+          height="0"
+          width="10"
+          x="-10"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="right"
+          data-id="1"
+          height="0"
+          width="10"
+          x="0"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="left"
+          data-id="1"
+          height="0"
+          width="10"
+          x="-10"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="right"
+          data-id="1"
+          height="0"
+          width="10"
+          x="0"
+          y="0"
+        />
+      </g>
+      <g
+        class="gate"
+        data-id="2"
+        data-zoom-in="false"
+        data-zoom-out="false"
+      >
+        <g>
+          <rect
+            class="gate-measure"
+            height="40"
+            width="40"
+            x="200"
+            y="80"
+          />
+          <path
+            class="arc-measure"
+            d="M 235 102 A 15 12 0 0 0 205 102"
+          />
+          <line
+            x1="220"
+            x2="232"
+            y1="108"
+            y2="88"
+          />
+        </g>
+        <rect
+          class="dropzone"
+          data-dropzone-position="left"
+          data-id="2"
+          height="0"
+          width="10"
+          x="-10"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="right"
+          data-id="2"
+          height="0"
+          width="10"
+          x="0"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="left"
+          data-id="2"
+          height="0"
+          width="10"
+          x="-10"
+          y="0"
+        />
+        <rect
+          class="dropzone"
+          data-dropzone-position="right"
+          data-id="2"
+          height="0"
+          width="10"
+          x="0"
+          y="0"
+        />
+      </g>
+    </g>
+  </svg>
+</div>
+`;
+
 exports[`Testing addMouseEvents verify mouse events 1`] = `
 <div>
   <svg
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 156439c7..091177d3 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -7,7 +7,7 @@ import { Sqore } from '../src/sqore';
 const {
     // addEditable
     addCustomStyles,
-    // addDropzones
+    addDropzones,
     addDocumentEvents,
     addDropzoneEvents,
     addMouseEvents,
@@ -1294,3 +1294,44 @@ describe('Testing getRenderFn', () => {
         expect(console.log).toHaveBeenCalledWith('callbackFn is triggered');
     });
 });
+
+describe('Testing addDropzones', () => {
+    test('verify dropzones', () => {
+        Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
+            writable: true,
+            value: () => ({
+                x: 0,
+                y: 0,
+                width: 0,
+                height: 0,
+            }),
+        });
+        const container = document.createElement('div');
+        const circuit: Circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default'], 0, true);
+        const svgElem = container.querySelector('svg');
+        if (svgElem != null) svgElem.removeAttribute('id');
+        addDropzones(container);
+        expect(container).toMatchSnapshot();
+    });
+});

From 5620b601696210b185b1bcfe1b5cf699a35eff21 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 13 Jun 2022 11:58:23 -0400
Subject: [PATCH 023/108] Integrate editable styles in styles.ts

---
 __tests__/__snapshots__/editable.test.ts.snap | 102 ++++++++++++------
 __tests__/editable.test.ts                    |  13 ---
 src/editable.ts                               |  30 ------
 src/styles.ts                                 |  48 ++++++++-
 4 files changed, 114 insertions(+), 79 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 634d04b7..1d834e89 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -1,40 +1,5 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`Testing addCustomStyles verify css 1`] = `
-<div>
-  <style />
-</div>
-`;
-
-exports[`Testing addCustomStyles verify css 2`] = `
-<div>
-  <style>
-    .dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
-    text {
-        user-select: none;
-    }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
-    }
-  </style>
-</div>
-`;
-
 exports[`Testing addDocumentEvents verify document events 1`] = `<div />`;
 
 exports[`Testing addDocumentEvents verify document events 2`] = `<div />`;
@@ -191,6 +156,29 @@ exports[`Testing addDropzones verify dropzones 1`] = `
         visibility: visible;
         opacity: 1;
         transition: opacity 1s;
+    }
+    
+    .dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
     }.dropzone {
         fill: transparent;
         stroke: transparent;
@@ -641,6 +629,50 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
         opacity: 1;
         transition: opacity 1s;
     }
+    
+    .dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
+    }.dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
+    }
     </style>
     <g>
       <text
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 091177d3..3729db60 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -6,7 +6,6 @@ import { Sqore } from '../src/sqore';
 
 const {
     // addEditable
-    addCustomStyles,
     addDropzones,
     addDocumentEvents,
     addDropzoneEvents,
@@ -768,18 +767,6 @@ describe('Testing getGate', () => {
     });
 });
 
-describe('Testing addCustomStyles', () => {
-    test('verify css', () => {
-        const container = document.createElement('div');
-        const style = document.createElement('style');
-        container.append(style);
-        expect(container).toMatchSnapshot();
-        addCustomStyles(container);
-        expect(container).toMatchSnapshot();
-    });
-});
-
-// Untestable
 describe('Testing addDocumentEvents', () => {
     test('verify document events', () => {
         const container = document.createElement('div');
diff --git a/src/editable.ts b/src/editable.ts
index c78b28c2..55fd9fe8 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -35,7 +35,6 @@ const addEditable = (container: HTMLElement, sqore: Sqore, callbackFn?: () => vo
         wires: getWireElemsY(container),
         renderFn: getRenderFn(container, sqore, callbackFn),
     };
-    addCustomStyles(container);
     addDropzones(container);
     addDocumentEvents(container);
     addDropzoneEvents(context);
@@ -44,34 +43,6 @@ const addEditable = (container: HTMLElement, sqore: Sqore, callbackFn?: () => vo
 
 // Commands
 
-const addCustomStyles = (container: HTMLElement): void => {
-    const style = container.querySelector<HTMLStyleElement>('style');
-    if (style) {
-        style.innerHTML += `.dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
-    text {
-        user-select: none;
-    }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
-    }`;
-    }
-};
-
 const addDropzones = (container: HTMLElement): void => {
     const gateElems = getGateElems(container);
     gateElems.forEach((gateElem) => {
@@ -326,7 +297,6 @@ const cursorCopy = (container: HTMLElement, value: boolean): void => {
 
 const exportedForTesting = {
     // addEditable
-    addCustomStyles,
     addDropzones,
     addDocumentEvents,
     addDropzoneEvents,
diff --git a/src/styles.ts b/src/styles.ts
index 491d563e..d14434c9 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -95,7 +95,8 @@ export const style = (customStyle: StyleConfig = {}): string => {
 
     return `${_defaultGates(styleConfig)}
     ${_classicallyControlledGates(styleConfig)}
-    ${_expandCollapse}`;
+    ${_expandCollapse}
+    ${_editable}`;
 };
 
 const _defaultGates = (styleConfig: StyleConfig): string => `
@@ -234,3 +235,48 @@ const _expandCollapse = `
         opacity: 1;
         transition: opacity 1s;
     }`;
+
+const _editable = `
+    .dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
+    }.dropzone {
+        fill: transparent;
+        stroke: transparent;
+    }
+    .dropzone:hover{
+        fill: red;
+        opacity: 25%;
+    }
+    text {
+        user-select: none;
+    }
+    .copying {
+        cursor: copy;
+    }
+    .moving {
+        cursor: move;
+    }
+    .detail-panel {
+        display: flex;
+        align-content: center;
+        gap: 12px;
+    }`;

From 2c6be0656069dec9f644fc8d2233ef0b7ae2c126 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 13 Jun 2022 12:17:32 -0400
Subject: [PATCH 024/108] Display grab cursor style when hover over gates

---
 __tests__/__snapshots__/editable.test.ts.snap | 14 ++++++++++++++
 src/styles.ts                                 |  9 ++++++++-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 1d834e89..d8e2ae43 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -201,6 +201,13 @@ exports[`Testing addDropzones verify dropzones 1`] = `
         align-content: center;
         gap: 12px;
     }
+    g.gate:not([data-expanded=true]) rect {
+        cursor: grab;
+    }
+    g.gate:not([data-expanded=true]) circle {
+        cursor: grab;
+    }
+    
     </style>
     <g>
       <text
@@ -673,6 +680,13 @@ exports[`Testing addMouseEvents verify mouse events 1`] = `
         align-content: center;
         gap: 12px;
     }
+    g.gate:not([data-expanded=true]) rect {
+        cursor: grab;
+    }
+    g.gate:not([data-expanded=true]) circle {
+        cursor: grab;
+    }
+    
     </style>
     <g>
       <text
diff --git a/src/styles.ts b/src/styles.ts
index d14434c9..10327841 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -279,4 +279,11 @@ const _editable = `
         display: flex;
         align-content: center;
         gap: 12px;
-    }`;
+    }
+    g.gate:not([data-expanded=true]) rect {
+        cursor: grab;
+    }
+    g.gate:not([data-expanded=true]) circle {
+        cursor: grab;
+    }
+    `;

From 009a1d772caad6cec06abe52e11dfe11da501c90 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 13 Jun 2022 12:33:07 -0400
Subject: [PATCH 025/108] Rename callbackFn to onCircuitChange

---
 __tests__/editable.test.ts |  8 ++++----
 example/script.js          |  2 +-
 src/editable.ts            | 16 ++++++++--------
 src/index.ts               |  6 +++---
 src/sqore.ts               | 26 +++++++++++++-------------
 5 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 3729db60..dc5651cf 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -1240,7 +1240,7 @@ describe('Testing addMouseEvents', () => {
 });
 
 describe('Testing getRenderFn', () => {
-    test('check console.log displaying "callbackFn is triggered"', () => {
+    test('check console.log displaying "onCircuitChange is triggered"', () => {
         Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
             writable: true,
             value: () => ({
@@ -1273,12 +1273,12 @@ describe('Testing getRenderFn', () => {
             ],
         };
         const sqore = new Sqore(circuit, STYLES['default']);
-        const callbackFn = () => console.log('callbackFn is triggered');
-        const renderFn = getRenderFn(container, sqore, callbackFn);
+        const onCircuitChange = () => console.log('onCircuitChange is triggered');
+        const renderFn = getRenderFn(container, sqore, onCircuitChange);
 
         jest.spyOn(console, 'log');
         renderFn();
-        expect(console.log).toHaveBeenCalledWith('callbackFn is triggered');
+        expect(console.log).toHaveBeenCalledWith('onCircuitChange is triggered');
     });
 });
 
diff --git a/example/script.js b/example/script.js
index ef3ef492..5c1aa464 100644
--- a/example/script.js
+++ b/example/script.js
@@ -12,7 +12,7 @@ if (typeof qviz != 'undefined') {
 
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
-        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, true, () => console.log('callbackFn'));
+        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, true, () => console.log('onCircuitChange'));
     }
 
     const teleportDiv = document.getElementById('teleport');
diff --git a/src/editable.ts b/src/editable.ts
index 55fd9fe8..e0407fb6 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -22,18 +22,18 @@ let _sourceTarget: SVGElement | null;
 /**
  * Add editable elements and events.
  *
- * @param container     HTML element for rendering visualization into.
- * @param sqore         Sqore object
- * @param callbackFn    User-provided callback function triggered when circuit is changed
+ * @param container         HTML element for rendering visualization into.
+ * @param sqore             Sqore object
+ * @param onCircuitChange   User-provided callback function triggered when circuit is changed
  *
  */
 
-const addEditable = (container: HTMLElement, sqore: Sqore, callbackFn?: () => void): void => {
+const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: () => void): void => {
     const context: Context = {
         container: container,
         operations: sqore.circuit.operations,
         wires: getWireElemsY(container),
-        renderFn: getRenderFn(container, sqore, callbackFn),
+        renderFn: getRenderFn(container, sqore, onCircuitChange),
     };
     addDropzones(container);
     addDocumentEvents(container);
@@ -220,10 +220,10 @@ const getGate = (dataId: string, operations: Operation[]): Operation => {
 };
 
 // Utilities
-const getRenderFn = (container: HTMLElement, sqore: Sqore, callbackFn?: () => void): (() => void) => {
+const getRenderFn = (container: HTMLElement, sqore: Sqore, onCircuitChange?: () => void): (() => void) => {
     return () => {
-        sqore.draw(container, 0, true, callbackFn);
-        if (callbackFn) callbackFn();
+        sqore.draw(container, 0, true, onCircuitChange);
+        if (onCircuitChange) onCircuitChange();
     };
 };
 
diff --git a/src/index.ts b/src/index.ts
index 49973b3f..5e5d770f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -13,7 +13,7 @@ import { StyleConfig } from './styles';
  * @param style Custom visualization style.
  * @param renderDepth Initial layer depth at which to render gates.
  * @param isEditable Optional value enabling/disabling editable feature
- * @param callbackFn Optional function to trigger when changing elements in circuit
+ * @param onCircuitChange Optional function to trigger when changing elements in circuit
  */
 export const draw = (
     circuit: Circuit,
@@ -21,11 +21,11 @@ export const draw = (
     style: StyleConfig | string = {},
     renderDepth = 0,
     isEditable?: boolean,
-    callbackFn?: () => void,
+    onCircuitChange?: () => void,
 ): void => {
     const sqore = new Sqore(circuit, style);
 
-    sqore.draw(container, renderDepth, isEditable, callbackFn);
+    sqore.draw(container, renderDepth, isEditable, onCircuitChange);
 };
 
 export { STYLES } from './styles';
diff --git a/src/sqore.ts b/src/sqore.ts
index 990ea3dc..878eef24 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -57,9 +57,9 @@ export class Sqore {
      * @param container HTML element for rendering visualization into.
      * @param renderDepth Initial layer depth at which to render gates.
      * @param isEditable Optional value enabling/disabling editable feature
-     * @param callbackFn Optional function to trigger when changing elements in circuit
+     * @param onCircuitChange Optional function to trigger when changing elements in circuit
      */
-    draw(container: HTMLElement, renderDepth = 0, isEditable?: boolean, callbackFn?: () => void): void {
+    draw(container: HTMLElement, renderDepth = 0, isEditable?: boolean, onCircuitChange?: () => void): void {
         // Inject into container
         if (container == null) throw new Error(`Container not provided.`);
 
@@ -81,7 +81,7 @@ export class Sqore {
             const id: string = circuit.operations[0].dataAttributes['id'];
             this.expandOperation(circuit.operations, id);
         }
-        this.renderCircuit(container, circuit, isEditable, callbackFn);
+        this.renderCircuit(container, circuit, isEditable, onCircuitChange);
     }
 
     /**
@@ -109,23 +109,23 @@ export class Sqore {
      * @param container HTML element for rendering visualization into.
      * @param circuit Circuit object to be rendered.
      * @param isEditable Optional value enabling/disabling editable feature
-     * @param callbackFn Optional function to trigger when changing elements in circuit
+     * @param onCircuitChange Optional function to trigger when changing elements in circuit
      */
     private renderCircuit(
         container: HTMLElement,
         circuit: Circuit,
         isEditable?: boolean,
-        callbackFn?: () => void,
+        onCircuitChange?: () => void,
     ): void {
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);
         container.innerHTML = '';
         container.appendChild(svg);
-        this.addGateClickHandlers(container, circuit, isEditable, callbackFn);
+        this.addGateClickHandlers(container, circuit, isEditable, onCircuitChange);
 
         if (isEditable) {
-            addEditable(container, this, callbackFn);
+            addEditable(container, this, onCircuitChange);
         }
     }
 
@@ -243,17 +243,17 @@ export class Sqore {
      * @param container HTML element containing visualized circuit.
      * @param circuit Circuit to be visualized.
      * @param isEditable Optional value enabling/disabling editable feature
-     * @param callbackFn Optional function to trigger when changing elements in circuit
+     * @param onCircuitChange Optional function to trigger when changing elements in circuit
      *
      */
     private addGateClickHandlers(
         container: HTMLElement,
         circuit: Circuit,
         isEditable?: boolean,
-        callbackFn?: () => void,
+        onCircuitChange?: () => void,
     ): void {
         this.addClassicalControlHandlers(container);
-        this.addZoomHandlers(container, circuit, isEditable, callbackFn);
+        this.addZoomHandlers(container, circuit, isEditable, onCircuitChange);
     }
 
     /**
@@ -310,14 +310,14 @@ export class Sqore {
      * @param container HTML element containing visualized circuit.
      * @param circuit Circuit to be visualized.
      * @param isEditable Optional value enabling/disabling editable feature
-     * @param callbackFn Optional function to trigger when changing elements in circuit
+     * @param onCircuitChange Optional function to trigger when changing elements in circuit
      *
      */
     private addZoomHandlers(
         container: HTMLElement,
         circuit: Circuit,
         isEditable?: boolean,
-        callbackFn?: () => void,
+        onCircuitChange?: () => void,
     ): void {
         container.querySelectorAll('.gate .gate-control').forEach((ctrl) => {
             // Zoom in on clicked gate
@@ -329,7 +329,7 @@ export class Sqore {
                     } else if (ctrl.classList.contains('gate-expand')) {
                         this.expandOperation(circuit.operations, gateId);
                     }
-                    this.renderCircuit(container, circuit, isEditable, callbackFn);
+                    this.renderCircuit(container, circuit, isEditable, onCircuitChange);
                     ev.stopPropagation();
                 }
             });

From b46060bb1695d3f455cd312dd0dd9cbd2fcb7968 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 13 Jun 2022 12:44:48 -0400
Subject: [PATCH 026/108] Document instructions on Editable feature

---
 example/script.js | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/example/script.js b/example/script.js
index 5c1aa464..76149998 100644
--- a/example/script.js
+++ b/example/script.js
@@ -12,7 +12,12 @@ if (typeof qviz != 'undefined') {
 
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
-        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, true, () => console.log('onCircuitChange'));
+        const isEditable = true;
+        const onCircuitChange = () => console.log('onCircuitChange triggered');
+        /* Pass in isEditable = true to allow circuit to be editable */
+        /* Pass in onCircuitChange to trigger callback function
+           whenever there is a change in circuit */
+        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, isEditable, onCircuitChange);
     }
 
     const teleportDiv = document.getElementById('teleport');

From 4bfea117a9b6e653bc5472ae4f0a9e698e8e5c97 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 13 Jun 2022 12:47:46 -0400
Subject: [PATCH 027/108] Add license

---
 __tests__/editable.test.ts | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index dc5651cf..50e7762d 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -1,3 +1,6 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
 import { Circuit, Operation } from '../src/circuit';
 import { exportedForTesting } from '../src/editable';
 import { RegisterType } from '../src/register';

From e41f5255738d2bf471497c258c5b779712904bb7 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 13 Jun 2022 12:51:58 -0400
Subject: [PATCH 028/108] Only allow scrollbar if neccessary

---
 example/index.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/example/index.html b/example/index.html
index c3060fdd..e612d7b8 100644
--- a/example/index.html
+++ b/example/index.html
@@ -28,7 +28,7 @@
         }
 
         .container {
-            overflow: scroll;
+            overflow: auto;
         }
     </style>
 </head>

From 8e0c9e6dedaebed135f82c5b9fbb5ab71a95eacf Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <37777232+aaronthangnguyen@users.noreply.github.com>
Date: Tue, 28 Jun 2022 12:43:09 -0400
Subject: [PATCH 029/108] Reimplement adding dropzones (#69)

* Hardcode dropzone-layer

* Create basic functionalities

* Add identifiable class to x-dot

* Add data-gate-id & data-gate-wire

* Extract _getCenter

* Add events

* Change function names

* Add move & copy

* Fix edge cases

* Add offset recursively on wires

* Hold Ctrl to copy

* Extra stuffs

* Let onCircuitChange taking in circuit object

* Add partial support for group gates

* Remove unnecessary function
---
 example/script.js               |   9 +-
 src/editable.ts                 | 540 ++++++++++++++++++--------------
 src/formatters/gateFormatter.ts |   2 +-
 src/index.ts                    |   2 +-
 src/sqore.ts                    |  25 +-
 src/styles.ts                   |  52 +--
 6 files changed, 334 insertions(+), 296 deletions(-)

diff --git a/example/script.js b/example/script.js
index 76149998..9c15533c 100644
--- a/example/script.js
+++ b/example/script.js
@@ -7,13 +7,16 @@ if (typeof qviz != 'undefined') {
     /* These examples shows how to draw circuits into containers. */
     const entangleDiv = document.getElementById('entangle');
     if (entangleDiv != null) {
-        qviz.draw(entangle, entangleDiv, qviz.STYLES['Default']);
+        qviz.draw(entangle, entangleDiv, qviz.STYLES['Default'], 0, true);
     }
 
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
         const isEditable = true;
-        const onCircuitChange = () => console.log('onCircuitChange triggered');
+        const onCircuitChange = (circuit) => {
+            console.log('New circuit ↓');
+            console.log(circuit);
+        };
         /* Pass in isEditable = true to allow circuit to be editable */
         /* Pass in onCircuitChange to trigger callback function
            whenever there is a change in circuit */
@@ -22,7 +25,7 @@ if (typeof qviz != 'undefined') {
 
     const teleportDiv = document.getElementById('teleport');
     if (teleportDiv != null) {
-        qviz.draw(teleport, teleportDiv, qviz.STYLES['Default']);
+        qviz.draw(teleport, teleportDiv, qviz.STYLES['Default'], 0, true);
     }
 
     const groverDiv = document.getElementById('grover');
diff --git a/src/editable.ts b/src/editable.ts
index e0407fb6..befbf36f 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -1,24 +1,21 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import { Operation } from './circuit';
-import { leftPadding } from './constants';
+import { Circuit, Operation } from './circuit';
 import { box } from './formatters/formatUtils';
 import { Sqore } from './sqore';
 
 interface Context {
     container: HTMLElement;
+    svg: SVGElement;
     operations: Operation[];
-    wires: Wires;
+    wireData: number[];
     renderFn: () => void;
+    paddingY: number;
+    selectedId: string | null;
+    selectedWire: string | null;
 }
 
-interface Wires {
-    [y: string]: string;
-}
-
-let _sourceTarget: SVGElement | null;
-
 /**
  * Add editable elements and events.
  *
@@ -28,301 +25,368 @@ let _sourceTarget: SVGElement | null;
  *
  */
 
-const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: () => void): void => {
+const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (circuit: Circuit) => void): void => {
+    const svg = container.querySelector('svg') as SVGElement;
+
     const context: Context = {
         container: container,
+        svg,
         operations: sqore.circuit.operations,
-        wires: getWireElemsY(container),
-        renderFn: getRenderFn(container, sqore, onCircuitChange),
+        wireData: _wireData(container),
+        renderFn: _renderFn(container, sqore, onCircuitChange),
+        paddingY: 20,
+        selectedId: null,
+        selectedWire: null,
     };
-    addDropzones(container);
-    addDocumentEvents(container);
-    addDropzoneEvents(context);
-    addMouseEvents(context);
-};
 
-// Commands
-
-const addDropzones = (container: HTMLElement): void => {
-    const gateElems = getGateElems(container);
-    gateElems.forEach((gateElem) => {
-        const { x, y, width, height } = gateElem.getBBox();
-        const dataId = getDataId(gateElem);
-        gateElem.append(createLeftDropzone(x, y, height, dataId));
-        gateElem.append(createRightDropzone(x, y, width, height, dataId));
-    });
+    // addDropzones(container);
+    // addDocumentEvents(container);
+    _addDataWires(container);
+    svg.appendChild(_dropzoneLayer(context));
+    // addDropzoneEvents(context);
+    // addMouseEvents(context);
+    _addEvents(context);
 };
 
-const addDocumentEvents = (container: HTMLElement): void => {
-    container.addEventListener('click', (ev: MouseEvent) => {
-        _sourceTarget = null;
-        if (ev.ctrlKey) return;
-    });
-    container.addEventListener('contextmenu', (ev: MouseEvent) => {
-        ev.preventDefault();
-    });
-    container.addEventListener('mouseup', () => {
-        cursorCopy(container, false);
-        cursorMove(container, false);
+const _addDataWires = (container: HTMLElement) => {
+    const elems = _hostElems(container);
+    elems.forEach((elem) => {
+        const { cY } = _center(elem);
+        // i.e. cY = 40, wireData returns [40, 100, 140, 180]
+        // dataWire will return 0, which is the index of 40 in wireData
+        const dataWire = _wireData(container).findIndex((y) => y === cY);
+        if (dataWire !== -1) {
+            elem.setAttribute('data-wire', `${dataWire}`);
+        } else {
+            const { y, height } = elem.getBBox();
+            const wireData = _wireData(container);
+            const groupDataWire = wireData.findIndex((wireY) => wireY > y && wireY < y + height);
+            elem.setAttribute('data-wire', `${groupDataWire}`);
+        }
     });
 };
 
-const addDropzoneEvents = (context: Context): void => {
-    const { container } = context;
-    const dropzoneElems = container.querySelectorAll<SVGRectElement>('.dropzone');
-    dropzoneElems.forEach((dropzoneElem) => {
-        dropzoneElem.addEventListener('mouseup', (ev: MouseEvent) => handleDropzoneMouseUp(ev, context));
-    });
+/**
+ *  Create a list of wires that element is spanning on.
+ *  i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140).
+ *       Function returns [40, 100, 140]
+ */
+const _elemWires = (elem: SVGGraphicsElement, wireData: number[]) => {
+    const { y, height } = elem.getBBox();
+    return wireData.filter((wireY) => wireY > y && wireY < y + height);
 };
 
-const addMouseEvents = (context: Context): void => {
-    const { container } = context;
-    const gateElems = getGateElems(container);
-    gateElems.forEach((gateElem) => {
-        gateElem.addEventListener('mousedown', (ev: MouseEvent) => handleGateMouseDown(ev, container));
-    });
+const _hostElems = (container: HTMLElement) => {
+    return container.querySelectorAll<SVGGraphicsElement>(
+        '[class^="gate-"]:not(.gate-control, .gate-swap), .control-dot, .oplus, .cross',
+    );
 };
 
-// Event handlers
-const handleGateMouseDown = (ev: MouseEvent, container: HTMLElement): void => {
-    ev.stopPropagation();
-    _sourceTarget = ev.currentTarget as SVGGElement;
+const _wirePrefixes = (wireData: number[]) => wireData.map((wireY, index) => ({ index, wireY, prefixX: 40 }));
 
-    // Ctrl + Mousedown to copy. Mousedown only to move.
-    ev.ctrlKey ? cursorCopy(container, true) : cursorMove(container, true);
+/**
+ *  Find center point of element
+ */
+const _center = (elem: SVGGraphicsElement) => {
+    const { x, y, width, height } = elem.getBBox();
+    return { cX: x + width / 2, cY: y + height / 2 };
 };
 
-const handleDropzoneMouseUp = (ev: MouseEvent, context: Context): void | false => {
-    ev.stopPropagation();
-
-    const { container, operations, wires, renderFn } = context;
-
-    const currentTarget = ev.currentTarget as SVGGElement;
-
-    if (!currentTarget) return false;
-
-    const dataId = getDataId(currentTarget);
-    const parent = getParent(dataId, operations);
-    const index = splitDataId(dataId).pop();
-    const position = getDropzonePosition(currentTarget);
-
-    if (_sourceTarget == null) return false;
-
-    const sourceDataId = getDataId(_sourceTarget);
-    const sourceParent = getParent(sourceDataId, operations);
-    const sourceIndex = splitDataId(sourceDataId).pop();
-
-    if (index == null || sourceIndex == null) return false;
-
-    const newGate = getGate(sourceDataId, operations);
-    const wireY = getClosestWireY(ev.offsetY, wires);
-
-    // Not allow Measure gate to move vertically
-    if (wireY != null && newGate.gate !== 'measure') {
-        // wires[wireY] returns qubit name (i.e: 'q0')
-        // this remove 'q' and assign an index (i.e: 0)
-        const index = Number(wires[wireY].slice(1));
-        const [firstTarget, ...targetsExceptFirst] = newGate.targets;
-        // Reserve all other properties, only change qId
-        Object.assign(firstTarget, { ...firstTarget, qId: index });
-        // Reserve all other targets, only change first target
-        Object.assign(newGate, { ...newGate, targets: [firstTarget, ...targetsExceptFirst] });
-    }
+/**
+ *  Create dropzone layer with all dropzones popullated
+ */
+const _dropzoneLayer = (context: Context) => {
+    const dropzoneLayer = document.createElementNS('http://www.w3.org/2000/svg', 'g');
+    dropzoneLayer.classList.add('dropzone-layer');
 
-    // Remove source element if moving using Ctrl + Mousedown
-    if (!ev.ctrlKey) {
-        deleteAt(sourceParent, sourceIndex);
-    }
+    const { container, svg, wireData, operations, paddingY } = context;
+    const elems = _hostElems(container);
 
-    // If dropzone is left of gate, insert before gate.
-    // Otherwise, insert after.
-    if (position === 'left') {
-        insertBefore(parent, index, newGate);
-    } else {
-        insertAfter(parent, index, newGate);
-    }
+    const wirePrefixes = _wirePrefixes(wireData);
 
-    // Remove cursor styles
-    cursorCopy(container, false);
-    cursorMove(container, false);
+    // Sort host elements by its x property
+    const sortedElems = Array.from(elems).sort((first, second) => {
+        const { x: x1 } = first.getBBox();
+        const { x: x2 } = second.getBBox();
+        return x1 - x2;
+    });
 
-    // Redraw the circuit
-    renderFn();
-};
+    // Add dropzones for each host elements
+    sortedElems.map((elem) => {
+        const { cX, cY } = _center(elem);
+        const wirePrefix = wirePrefixes.find((item) => item.wireY === cY);
+
+        // Check to prevent group gates creating dropzones between wires
+        if (wirePrefix) {
+            const prefixX = wirePrefix.prefixX;
+            const elemDropzone = box(prefixX, cY - paddingY, cX - prefixX, paddingY * 2, 'dropzone');
+            elemDropzone.setAttribute('data-dropzone-id', _equivDataId(elem) || '');
+            elemDropzone.setAttribute('data-dropzone-wire', `${wirePrefix.index}`);
+
+            wirePrefix.prefixX = cX;
+
+            dropzoneLayer.appendChild(elemDropzone);
+        } else {
+            // Let group gates creating dropzones for each wire
+            const { x } = elem.getBBox();
+            const elemWires = _elemWires(elem, wireData);
+
+            elemWires.map((wireY) => {
+                const wirePrefix = wirePrefixes.find((item) => item.wireY === wireY);
+                if (wirePrefix) {
+                    const prefixX = wirePrefix.prefixX;
+                    const elemDropzone = box(prefixX, wireY - paddingY, x - prefixX, paddingY * 2, 'dropzone');
+                    elemDropzone.setAttribute('data-dropzone-id', _equivDataId(elem) || '');
+                    elemDropzone.setAttribute('data-dropzone-wire', `${wirePrefix.index}`);
+
+                    wirePrefix.prefixX = x;
+
+                    dropzoneLayer.appendChild(elemDropzone);
+                }
+            });
+            console.log({ elem, x, elemWires });
+        }
+    });
 
-// Element getters
+    // Add remaining dropzones to fit max-width of the circuit
+    wirePrefixes.map(({ wireY, prefixX }) => {
+        const maxWidth = Number(svg.getAttribute('width'));
+        const elemDropzone = box(prefixX, wireY - paddingY, maxWidth - prefixX, paddingY * 2, 'dropzone');
+        elemDropzone.setAttribute('data-dropzone-id', `${operations.length}`);
+        const index = wireData.findIndex((item) => item === wireY);
+        elemDropzone.setAttribute('data-dropzone-wire', `${index}`);
+        dropzoneLayer.appendChild(elemDropzone);
+    });
 
-const getGateElems = (container: HTMLElement): SVGGElement[] => {
-    return Array.from(container.querySelectorAll('g.gate'));
+    return dropzoneLayer;
 };
 
-const getWireElems = (container: HTMLElement): SVGGElement[] => {
+const _wireData = (container: HTMLElement) => {
     // elems include qubit wires and lines of measure gates
     const elems = container.querySelectorAll<SVGGElement>('svg > g:nth-child(3) > g');
     // filter out <g> elements having more than 2 elements because
     // qubit wires contain only 2 elements: <line> and <text>
     // lines of measure gates contain 4 <line> elements
-    return Array.from(elems).filter((elem) => elem.childElementCount < 3);
+    const wireElems = Array.from(elems).filter((elem) => elem.childElementCount < 3);
+    const wireData = wireElems.map((wireElem) => {
+        const lineElem = wireElem.children[0] as SVGLineElement;
+        return lineElem.y1.baseVal.value;
+    });
+    return wireData;
 };
 
-// Element creators
-
-const createDropzone = (
-    x: number,
-    y: number,
-    width: number,
-    height: number,
-    dataId: string,
-    position: 'left' | 'right',
-): SVGElement => {
-    const dropzone = box(x, y, width, height, `dropzone`);
-    dropzone.setAttribute('data-id', dataId);
-    dropzone.setAttribute('data-dropzone-position', position);
-    return dropzone;
+/**
+ *  Find equivalent gate element of host element
+ */
+const _equivGateElem = (elem: SVGElement) => {
+    return elem.closest<SVGElement>('[data-id]');
 };
 
-const createLeftDropzone = (gateX: number, gateY: number, gateHeight: number, dataId: string): SVGElement => {
-    return createDropzone(gateX - leftPadding / 2, gateY, leftPadding / 2, gateHeight, dataId, 'left');
+/**
+ *  Find data-id of host element
+ */
+const _equivDataId = (elem: SVGElement) => {
+    const gateElem = _equivGateElem(elem);
+    return gateElem != null ? gateElem.getAttribute('data-id') : null;
 };
-const createRightDropzone = (
-    gateX: number,
-    gateY: number,
-    gateWidth: number,
-    gateHeight: number,
-    dataId: string,
-): SVGElement => {
-    return createDropzone(gateX + gateWidth, gateY, leftPadding / 2, gateHeight, dataId, 'right');
+
+/**
+ *  Disable contextmenu default behaviors
+ */
+const _addContextMenuEvents = (container: HTMLElement) => {
+    container.addEventListener('contextmenu', (ev: MouseEvent) => {
+        ev.preventDefault();
+    });
 };
 
-// Operation getters
+/**
+ *  Add events specifically for dropzoneLayer
+ */
+const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElement) => {
+    container.addEventListener('mouseup', () => (dropzoneLayer.style.display = 'none'));
+};
 
-const getParent = (dataId: string, operations: Operation[]): Operation[] => {
-    const segments = splitDataId(dataId);
-    // Remove last segment to navigate to parent instead of child
-    segments.pop();
+const _addEvents = (context: Context) => {
+    const { container, operations, renderFn } = context;
+    const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
+
+    _addContextMenuEvents(container);
+    _addDropzoneLayerEvents(container, dropzoneLayer);
+
+    // Host element events
+    const elems = _hostElems(container);
+    elems.forEach((elem) => {
+        elem.addEventListener('mousedown', () => {
+            context.selectedWire = elem.getAttribute('data-wire');
+        });
+
+        const gateElem = _equivGateElem(elem);
+        gateElem?.addEventListener('mousedown', (ev: MouseEvent) => {
+            ev.stopPropagation();
+            context.selectedId = _equivDataId(elem);
+            dropzoneLayer.style.display = 'block';
+        });
+    });
 
-    let parent = operations;
-    for (const segment of segments) {
-        parent = parent[segment].children || parent;
-    }
-    return parent;
+    // Dropzone element events
+    const dropzoneElems = dropzoneLayer.querySelectorAll<SVGRectElement>('.dropzone');
+    dropzoneElems.forEach((dropzoneElem) => {
+        dropzoneElem.addEventListener('mouseup', (ev: MouseEvent) => {
+            const targetId = dropzoneElem.getAttribute('data-dropzone-id');
+            const targetWire = dropzoneElem.getAttribute('data-dropzone-wire');
+            if (
+                targetId == null || //
+                targetWire == null ||
+                context.selectedId == null ||
+                context.selectedWire == null
+            )
+                return;
+
+            const newSourceOperation = ev.ctrlKey
+                ? _copyX(context.selectedId, targetId, operations)
+                : _moveX(context.selectedId, targetId, operations);
+
+            if (newSourceOperation != null) {
+                _moveY(context.selectedWire, targetWire, newSourceOperation, context.wireData);
+            }
+
+            renderFn();
+        });
+    });
 };
 
-const getGate = (dataId: string, operations: Operation[]): Operation => {
-    const parent = getParent(dataId, operations);
-    const index = splitDataId(dataId).pop();
+const _equivOperationParent = (dataId: string | null, operations: Operation[]): Operation[] | null => {
+    if (!dataId) return null;
 
-    if (index == null) {
-        throw new Error('Gate not found');
-    }
+    const indexes = _indexes(dataId);
+    indexes.pop();
 
-    return parent[index];
+    let operationParent = operations;
+    for (const index of indexes) {
+        operationParent = operationParent[index].children || operationParent;
+    }
+    return operationParent;
 };
 
-// Utilities
-const getRenderFn = (container: HTMLElement, sqore: Sqore, onCircuitChange?: () => void): (() => void) => {
-    return () => {
-        sqore.draw(container, 0, true, onCircuitChange);
-        if (onCircuitChange) onCircuitChange();
-    };
-};
+const _equivOperation = (dataId: string | null, operations: Operation[]): Operation | null => {
+    if (!dataId) return null;
 
-const getDataId = (element: Element): string => {
-    return element.getAttribute('data-id') || '';
-};
+    const index = _lastIndex(dataId);
+    const operationParent = _equivOperationParent(dataId, operations);
 
-const splitDataId = (dataId: string): number[] => {
-    return dataId === '' ? [] : dataId.split('-').map(Number);
+    if (
+        operationParent == null || //
+        index == null
+    )
+        return null;
+
+    return operationParent[index];
 };
 
-const getWireElemsY = (container: HTMLElement): Wires => {
-    const wireElems = getWireElems(container);
-    return wireElems.reduce((previous, current) => {
-        const y = getWireElemY(current);
-        const text = getWireElemText(current);
-        return { ...previous, [`${y}`]: text };
-    }, {});
+const _moveX = (sourceId: string, targetId: string, operations: Operation[]) => {
+    if (sourceId === targetId) return _equivOperation(sourceId, operations);
+    const sourceOperation = _equivOperation(sourceId, operations);
+    const sourceOperationParent = _equivOperationParent(sourceId, operations);
+    const targetOperationParent = _equivOperationParent(targetId, operations);
+    const targetLastIndex = _lastIndex(targetId);
+
+    if (
+        targetOperationParent == null || //
+        targetLastIndex == null ||
+        sourceOperation == null ||
+        sourceOperationParent == null
+    )
+        return;
+
+    // Insert sourceOperation to target last index
+    const newSourceOperation: Operation = { ...sourceOperation };
+    targetOperationParent.splice(targetLastIndex, 0, newSourceOperation);
+
+    // Delete sourceOperation
+    sourceOperation.gate = 'removed';
+    const indexToRemove = sourceOperationParent.findIndex((operation) => operation.gate === 'removed');
+    sourceOperationParent.splice(indexToRemove, 1);
+
+    return newSourceOperation;
 };
 
-const getWireElemY = (wireElem: SVGGElement): number => {
-    const lineElem = wireElem.querySelector<SVGLineElement>('line');
-    if (lineElem == null || lineElem.getAttribute('y1') == null) throw Error('y not found');
-    return Number(lineElem.getAttribute('y1'));
+const _copyX = (sourceId: string, targetId: string, operations: Operation[]) => {
+    if (sourceId === targetId) return _equivOperation(sourceId, operations);
+    const sourceOperation = _equivOperation(sourceId, operations);
+    const sourceOperationParent = _equivOperationParent(sourceId, operations);
+    const targetOperationParent = _equivOperationParent(targetId, operations);
+    const targetLastIndex = _lastIndex(targetId);
+
+    if (
+        targetOperationParent == null || //
+        targetLastIndex == null ||
+        sourceOperation == null ||
+        sourceOperationParent == null
+    )
+        return;
+
+    // Insert sourceOperation to target last index
+    const newSourceOperation: Operation = JSON.parse(JSON.stringify(sourceOperation));
+    targetOperationParent.splice(targetLastIndex, 0, newSourceOperation);
+
+    return newSourceOperation;
 };
 
-const getWireElemText = (wireElem: SVGGElement): string => {
-    const textElem = wireElem.querySelector<SVGTextElement>('text');
-    if (textElem == null || textElem.textContent == null || textElem.textContent === '')
-        throw new Error('Text not found');
-    return textElem.textContent;
+const _moveY = (sourceWire: string, targetWire: string, operation: Operation, wireData: number[]) => {
+    const offset = parseInt(targetWire) - parseInt(sourceWire);
+    _offsetRecursively(offset, operation, wireData);
 };
 
-const getClosestWireY = (offsetY: number, wires: { [y: number]: string }): number | null => {
-    for (const wire of Object.entries(wires)) {
-        const y = wire[0];
-        const distance = Math.abs(offsetY - Number(y));
-        // 15 is an arbitrary number to determine the closeness of distance
-        if (distance <= 15) {
-            return Number(y);
-        }
+const _offsetRecursively = (offsetY: number, operation: Operation, wireData: number[]) => {
+    const wireDataSize = wireData.length;
+
+    // Offset all targets by offsetY value
+    if (operation.targets != null) {
+        operation.targets.forEach((target) => {
+            target.qId = _circularMod(target.qId, offsetY, wireDataSize);
+            if (target.cId) target.cId = _circularMod(target.cId, offsetY, wireDataSize);
+        });
     }
-    return null;
-};
 
-const getDropzonePosition = (element: SVGElement): string => {
-    const position = element.getAttribute('data-dropzone-position');
-    if (position == null) throw new Error('Position not found');
-    return position;
-};
+    // Offset all controls by offsetY value
+    if (operation.controls != null) {
+        operation.controls.forEach((control) => {
+            control.qId = _circularMod(control.qId, offsetY, wireDataSize);
+            if (control.cId) control.cId = _circularMod(control.qId, offsetY, wireDataSize);
+        });
+    }
 
-const insertBefore = (parent: Operation[], index: number, newGate: Operation): void => {
-    parent.splice(index, 0, newGate);
+    // Offset recursively through all children
+    if (operation.children != null) {
+        operation.children.forEach((child) => _offsetRecursively(offsetY, child, wireData));
+    }
 };
 
-const insertAfter = (parent: Operation[], index: number, newGate: Operation): void => {
-    parent.splice(index + 1, 0, newGate);
+/**
+ *  This modulo function always returns positive value based on total.
+ *  i.e: value=0, offset=-1, total=4 returns 3 instead of -1
+ */
+const _circularMod = (value: number, offset: number, total: number) => {
+    return (((value + offset) % total) + total) % total;
 };
 
-const deleteAt = (parent: Operation[], index: number): void => {
-    parent.splice(index, 1);
-};
+const _indexes = (dataId: string) => dataId.split('-').map((segment) => parseInt(segment));
 
-const cursorMove = (container: HTMLElement, value: boolean): void => {
-    value ? container.classList.add('moving') : container.classList.remove('moving');
+const _lastIndex = (dataId: string) => {
+    return _indexes(dataId).pop();
 };
 
-const cursorCopy = (container: HTMLElement, value: boolean): void => {
-    value ? container.classList.add('copying') : container.classList.remove('copying');
+const _renderFn = (
+    container: HTMLElement,
+    sqore: Sqore,
+    onCircuitChange?: (circuit: Circuit) => void,
+): (() => void) => {
+    return () => {
+        sqore.draw(container, 0, true, onCircuitChange);
+        if (onCircuitChange) onCircuitChange(sqore.circuit);
+    };
 };
 
-const exportedForTesting = {
-    // addEditable
-    addDropzones,
-    addDocumentEvents,
-    addDropzoneEvents,
-    addMouseEvents,
-    handleGateMouseDown,
-    // handleDropzoneMouseUp
-    getGateElems,
-    getWireElems,
-    createDropzone,
-    createLeftDropzone,
-    createRightDropzone,
-    getParent,
-    getGate,
-    getDataId,
-    getRenderFn,
-    splitDataId,
-    getWireElemsY,
-    getWireElemY,
-    getWireElemText,
-    getClosestWireY,
-    getDropzonePosition,
-    insertBefore,
-    insertAfter,
-    deleteAt,
-    cursorMove,
-    cursorCopy,
-};
+const exportedForTesting = {};
 
 export { addEditable, exportedForTesting };
diff --git a/src/formatters/gateFormatter.ts b/src/formatters/gateFormatter.ts
index 16588ff3..607bdc16 100644
--- a/src/formatters/gateFormatter.ts
+++ b/src/formatters/gateFormatter.ts
@@ -302,7 +302,7 @@ const _cross = (x: number, y: number): SVGElement => {
     const radius = 8;
     const line1: SVGElement = line(x - radius, y - radius, x + radius, y + radius);
     const line2: SVGElement = line(x - radius, y + radius, x + radius, y - radius);
-    return group([line1, line2]);
+    return group([line1, line2], { class: 'cross' });
 };
 
 /**
diff --git a/src/index.ts b/src/index.ts
index 5e5d770f..56b35003 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -21,7 +21,7 @@ export const draw = (
     style: StyleConfig | string = {},
     renderDepth = 0,
     isEditable?: boolean,
-    onCircuitChange?: () => void,
+    onCircuitChange?: (circuit: Circuit) => void,
 ): void => {
     const sqore = new Sqore(circuit, style);
 
diff --git a/src/sqore.ts b/src/sqore.ts
index 878eef24..17934f74 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -1,16 +1,16 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import { formatInputs } from './formatters/inputFormatter';
+import { Circuit, ConditionalRender, Operation } from './circuit';
+import { svgNS } from './constants';
+import { addEditable } from './editable';
 import { formatGates } from './formatters/gateFormatter';
+import { formatInputs } from './formatters/inputFormatter';
 import { formatRegisters } from './formatters/registerFormatter';
+import { GateType, Metadata } from './metadata';
 import { processOperations } from './process';
-import { ConditionalRender, Circuit, Operation } from './circuit';
-import { Metadata, GateType } from './metadata';
-import { StyleConfig, style, STYLES } from './styles';
+import { style, StyleConfig, STYLES } from './styles';
 import { createUUID } from './utils';
-import { svgNS } from './constants';
-import { addEditable } from './editable';
 
 /**
  * Contains metadata for visualization.
@@ -59,7 +59,12 @@ export class Sqore {
      * @param isEditable Optional value enabling/disabling editable feature
      * @param onCircuitChange Optional function to trigger when changing elements in circuit
      */
-    draw(container: HTMLElement, renderDepth = 0, isEditable?: boolean, onCircuitChange?: () => void): void {
+    draw(
+        container: HTMLElement,
+        renderDepth = 0,
+        isEditable?: boolean,
+        onCircuitChange?: (circuit: Circuit) => void,
+    ): void {
         // Inject into container
         if (container == null) throw new Error(`Container not provided.`);
 
@@ -115,7 +120,7 @@ export class Sqore {
         container: HTMLElement,
         circuit: Circuit,
         isEditable?: boolean,
-        onCircuitChange?: () => void,
+        onCircuitChange?: (circuit: Circuit) => void,
     ): void {
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(circuit);
@@ -250,7 +255,7 @@ export class Sqore {
         container: HTMLElement,
         circuit: Circuit,
         isEditable?: boolean,
-        onCircuitChange?: () => void,
+        onCircuitChange?: (circuit: Circuit) => void,
     ): void {
         this.addClassicalControlHandlers(container);
         this.addZoomHandlers(container, circuit, isEditable, onCircuitChange);
@@ -317,7 +322,7 @@ export class Sqore {
         container: HTMLElement,
         circuit: Circuit,
         isEditable?: boolean,
-        onCircuitChange?: () => void,
+        onCircuitChange?: (circuit: Circuit) => void,
     ): void {
         container.querySelectorAll('.gate .gate-control').forEach((ctrl) => {
             // Zoom in on clicked gate
diff --git a/src/styles.ts b/src/styles.ts
index 10327841..481aea8c 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -237,53 +237,19 @@ const _expandCollapse = `
     }`;
 
 const _editable = `
-    .dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
-    text {
-        user-select: none;
-    }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
-    }.dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
     text {
         user-select: none;
+        pointer-events: none;
     }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
+    .dropzone-layer {
+        display: none;
     }
-    g.gate:not([data-expanded=true]) rect {
-        cursor: grab;
+    .dropzone {
+        fill-opacity: 0%;
+        stroke-opacity: 0%;
     }
-    g.gate:not([data-expanded=true]) circle {
-        cursor: grab;
+    .dropzone:hover {
+        fill: #EC7063;
+        fill-opacity: 50%;
     }
     `;

From 605c64c7706c3097a0e39ba420e3ffed9253dc8b Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 13:11:56 -0400
Subject: [PATCH 030/108] Update snapshots

---
 __tests__/__snapshots__/editable.test.ts.snap | 1147 -----------------
 .../__snapshots__/gateFormatter.test.ts.snap  |   72 +-
 2 files changed, 54 insertions(+), 1165 deletions(-)
 delete mode 100644 __tests__/__snapshots__/editable.test.ts.snap

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
deleted file mode 100644
index d8e2ae43..00000000
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ /dev/null
@@ -1,1147 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Testing addDocumentEvents verify document events 1`] = `<div />`;
-
-exports[`Testing addDocumentEvents verify document events 2`] = `<div />`;
-
-exports[`Testing addDropzoneEvents add 1 event 1`] = `
-<div>
-  <svg>
-    <rect />
-  </svg>
-</div>
-`;
-
-exports[`Testing addDropzoneEvents add 2 events 1`] = `
-<div>
-  <svg>
-    <rect />
-    <rect />
-  </svg>
-</div>
-`;
-
-exports[`Testing addDropzones verify dropzones 1`] = `
-<div>
-  <svg
-    class="qviz"
-    height="180"
-    style="max-width: fit-content;"
-    width="260"
-  >
-    <style>
-      
-    line,
-    circle,
-    rect {
-        stroke: #000000;
-        stroke-width: 1;
-    }
-    text {
-        fill: #000000;
-        dominant-baseline: middle;
-        text-anchor: middle;
-        font-family: Arial;
-    }
-    .control-dot {
-        fill: #000000;
-    }
-    .oplus line, .oplus circle {
-        fill: #FFFFFF;
-        stroke-width: 2;
-    }
-    .gate-unitary {
-        fill: #D9F1FA;
-    }
-    .gate-measure {
-        fill: #FFDE86;
-    }
-    rect.gate-swap {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .arc-measure {
-        stroke: #000000;
-        fill: none;
-        stroke-width: 1;
-    }
-    .register-classical {
-        stroke-width: 0.5;
-    }
-    
-    .hidden {
-        display: none;
-    }
-    .classically-controlled-unknown {
-        opacity: 0.25;
-    }
-
-    
-    .classically-controlled-one .classical-container,
-    .classically-controlled-one .classical-line {
-        stroke: #4059BD;
-        stroke-width: 1.3;
-        fill: #4059BD;
-        fill-opacity: 0.1;
-    }
-    .classically-controlled-zero .classical-container,
-    .classically-controlled-zero .classical-line {
-        stroke: #C40000;
-        stroke-width: 1.3;
-        fill: #C40000;
-        fill-opacity: 0.1;
-    }
-    
-    .classically-controlled-btn {
-        cursor: pointer;
-    }
-    .classically-controlled-unknown .classically-controlled-btn {
-        fill: #E5E5E5;
-    }
-    .classically-controlled-one .classically-controlled-btn {
-        fill: #4059BD;
-    }
-    .classically-controlled-zero .classically-controlled-btn {
-        fill: #C40000;
-    }
-    
-    .classically-controlled-btn text {
-        dominant-baseline: middle;
-        text-anchor: middle;
-        stroke: none;
-        font-family: Arial;
-    }
-    .classically-controlled-unknown .classically-controlled-btn text {
-        fill: #000000;
-    }
-    .classically-controlled-one .classically-controlled-btn text {
-        fill: #FFFFFF;
-    }
-    .classically-controlled-zero .classically-controlled-btn text {
-        fill: #FFFFFF;
-    }
-    
-    .qviz .gate-collapse,
-    .qviz .gate-expand {
-        opacity: 0;
-        transition: opacity 1s;
-    }
-
-    .qviz:hover .gate-collapse,
-    .qviz:hover .gate-expand {
-        visibility: visible;
-        opacity: 0.2;
-        transition: visibility 1s;
-        transition: opacity 1s;
-    }
-
-    .gate-expand, .gate-collapse {
-        cursor: pointer;
-    }
-
-    .gate-collapse circle,
-    .gate-expand circle {
-        fill: white;
-        stroke-width: 2px;
-        stroke: black;
-    }
-    .gate-collapse path,
-    .gate-expand path {
-        stroke-width: 4px;
-        stroke: black;
-    }
-
-    .gate:hover &gt; .gate-collapse,
-    .gate:hover &gt; .gate-expand {
-        visibility: visible;
-        opacity: 1;
-        transition: opacity 1s;
-    }
-    
-    .dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
-    text {
-        user-select: none;
-    }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
-    }.dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
-    text {
-        user-select: none;
-    }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
-    }
-    g.gate:not([data-expanded=true]) rect {
-        cursor: grab;
-    }
-    g.gate:not([data-expanded=true]) circle {
-        cursor: grab;
-    }
-    
-    </style>
-    <g>
-      <text
-        dominant-baseline="middle"
-        font-size="16"
-        text-anchor="start"
-        x="20"
-        y="40"
-      >
-        |0⟩
-      </text>
-      <text
-        dominant-baseline="middle"
-        font-size="16"
-        text-anchor="start"
-        x="20"
-        y="100"
-      >
-        |0⟩
-      </text>
-    </g>
-    <g>
-      <g>
-        <line
-          x1="40"
-          x2="260"
-          y1="40"
-          y2="40"
-        />
-        <text
-          dominant-baseline="hanging"
-          font-size="75%"
-          text-anchor="start"
-          x="40"
-          y="24"
-        >
-          q0
-        </text>
-      </g>
-      <g>
-        <line
-          x1="40"
-          x2="260"
-          y1="100"
-          y2="100"
-        />
-        <text
-          dominant-baseline="hanging"
-          font-size="75%"
-          text-anchor="start"
-          x="40"
-          y="84"
-        >
-          q1
-        </text>
-      </g>
-      <g>
-        <line
-          class="register-classical"
-          x1="221"
-          x2="221"
-          y1="100"
-          y2="139"
-        />
-        <line
-          class="register-classical"
-          x1="219"
-          x2="219"
-          y1="100"
-          y2="141"
-        />
-        <line
-          class="register-classical"
-          x1="221"
-          x2="260"
-          y1="139"
-          y2="139"
-        />
-        <line
-          class="register-classical"
-          x1="219"
-          x2="260"
-          y1="141"
-          y2="141"
-        />
-      </g>
-    </g>
-    <g>
-      <g
-        class="gate"
-        data-id="0"
-        data-zoom-in="false"
-        data-zoom-out="false"
-      >
-        <g>
-          <g>
-            <rect
-              class="gate-unitary"
-              height="40"
-              width="40"
-              x="80"
-              y="20"
-            />
-            <text
-              font-size="14"
-              x="100"
-              y="40"
-            >
-              H
-            </text>
-          </g>
-        </g>
-        <rect
-          class="dropzone"
-          data-dropzone-position="left"
-          data-id="0"
-          height="0"
-          width="10"
-          x="-10"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="right"
-          data-id="0"
-          height="0"
-          width="10"
-          x="0"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="left"
-          data-id="0"
-          height="0"
-          width="10"
-          x="-10"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="right"
-          data-id="0"
-          height="0"
-          width="10"
-          x="0"
-          y="0"
-        />
-      </g>
-      <g
-        class="gate"
-        data-id="1"
-        data-zoom-in="false"
-        data-zoom-out="false"
-      >
-        <line
-          x1="160"
-          x2="160"
-          y1="40"
-          y2="100"
-        />
-        <circle
-          class="control-dot"
-          cx="160"
-          cy="40"
-          r="5"
-        />
-        <g
-          class="oplus"
-        >
-          <circle
-            cx="160"
-            cy="100"
-            r="15"
-          />
-          <line
-            x1="160"
-            x2="160"
-            y1="85"
-            y2="115"
-          />
-          <line
-            x1="145"
-            x2="175"
-            y1="100"
-            y2="100"
-          />
-        </g>
-        <rect
-          class="dropzone"
-          data-dropzone-position="left"
-          data-id="1"
-          height="0"
-          width="10"
-          x="-10"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="right"
-          data-id="1"
-          height="0"
-          width="10"
-          x="0"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="left"
-          data-id="1"
-          height="0"
-          width="10"
-          x="-10"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="right"
-          data-id="1"
-          height="0"
-          width="10"
-          x="0"
-          y="0"
-        />
-      </g>
-      <g
-        class="gate"
-        data-id="2"
-        data-zoom-in="false"
-        data-zoom-out="false"
-      >
-        <g>
-          <rect
-            class="gate-measure"
-            height="40"
-            width="40"
-            x="200"
-            y="80"
-          />
-          <path
-            class="arc-measure"
-            d="M 235 102 A 15 12 0 0 0 205 102"
-          />
-          <line
-            x1="220"
-            x2="232"
-            y1="108"
-            y2="88"
-          />
-        </g>
-        <rect
-          class="dropzone"
-          data-dropzone-position="left"
-          data-id="2"
-          height="0"
-          width="10"
-          x="-10"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="right"
-          data-id="2"
-          height="0"
-          width="10"
-          x="0"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="left"
-          data-id="2"
-          height="0"
-          width="10"
-          x="-10"
-          y="0"
-        />
-        <rect
-          class="dropzone"
-          data-dropzone-position="right"
-          data-id="2"
-          height="0"
-          width="10"
-          x="0"
-          y="0"
-        />
-      </g>
-    </g>
-  </svg>
-</div>
-`;
-
-exports[`Testing addMouseEvents verify mouse events 1`] = `
-<div>
-  <svg
-    class="qviz"
-    height="180"
-    style="max-width: fit-content;"
-    width="140"
-  >
-    <style>
-      
-    line,
-    circle,
-    rect {
-        stroke: #000000;
-        stroke-width: 1;
-    }
-    text {
-        fill: #000000;
-        dominant-baseline: middle;
-        text-anchor: middle;
-        font-family: Arial;
-    }
-    .control-dot {
-        fill: #000000;
-    }
-    .oplus line, .oplus circle {
-        fill: #FFFFFF;
-        stroke-width: 2;
-    }
-    .gate-unitary {
-        fill: #D9F1FA;
-    }
-    .gate-measure {
-        fill: #FFDE86;
-    }
-    rect.gate-swap {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .arc-measure {
-        stroke: #000000;
-        fill: none;
-        stroke-width: 1;
-    }
-    .register-classical {
-        stroke-width: 0.5;
-    }
-    
-    .hidden {
-        display: none;
-    }
-    .classically-controlled-unknown {
-        opacity: 0.25;
-    }
-
-    
-    .classically-controlled-one .classical-container,
-    .classically-controlled-one .classical-line {
-        stroke: #4059BD;
-        stroke-width: 1.3;
-        fill: #4059BD;
-        fill-opacity: 0.1;
-    }
-    .classically-controlled-zero .classical-container,
-    .classically-controlled-zero .classical-line {
-        stroke: #C40000;
-        stroke-width: 1.3;
-        fill: #C40000;
-        fill-opacity: 0.1;
-    }
-    
-    .classically-controlled-btn {
-        cursor: pointer;
-    }
-    .classically-controlled-unknown .classically-controlled-btn {
-        fill: #E5E5E5;
-    }
-    .classically-controlled-one .classically-controlled-btn {
-        fill: #4059BD;
-    }
-    .classically-controlled-zero .classically-controlled-btn {
-        fill: #C40000;
-    }
-    
-    .classically-controlled-btn text {
-        dominant-baseline: middle;
-        text-anchor: middle;
-        stroke: none;
-        font-family: Arial;
-    }
-    .classically-controlled-unknown .classically-controlled-btn text {
-        fill: #000000;
-    }
-    .classically-controlled-one .classically-controlled-btn text {
-        fill: #FFFFFF;
-    }
-    .classically-controlled-zero .classically-controlled-btn text {
-        fill: #FFFFFF;
-    }
-    
-    .qviz .gate-collapse,
-    .qviz .gate-expand {
-        opacity: 0;
-        transition: opacity 1s;
-    }
-
-    .qviz:hover .gate-collapse,
-    .qviz:hover .gate-expand {
-        visibility: visible;
-        opacity: 0.2;
-        transition: visibility 1s;
-        transition: opacity 1s;
-    }
-
-    .gate-expand, .gate-collapse {
-        cursor: pointer;
-    }
-
-    .gate-collapse circle,
-    .gate-expand circle {
-        fill: white;
-        stroke-width: 2px;
-        stroke: black;
-    }
-    .gate-collapse path,
-    .gate-expand path {
-        stroke-width: 4px;
-        stroke: black;
-    }
-
-    .gate:hover &gt; .gate-collapse,
-    .gate:hover &gt; .gate-expand {
-        visibility: visible;
-        opacity: 1;
-        transition: opacity 1s;
-    }
-    
-    .dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
-    text {
-        user-select: none;
-    }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
-    }.dropzone {
-        fill: transparent;
-        stroke: transparent;
-    }
-    .dropzone:hover{
-        fill: red;
-        opacity: 25%;
-    }
-    text {
-        user-select: none;
-    }
-    .copying {
-        cursor: copy;
-    }
-    .moving {
-        cursor: move;
-    }
-    .detail-panel {
-        display: flex;
-        align-content: center;
-        gap: 12px;
-    }
-    g.gate:not([data-expanded=true]) rect {
-        cursor: grab;
-    }
-    g.gate:not([data-expanded=true]) circle {
-        cursor: grab;
-    }
-    
-    </style>
-    <g>
-      <text
-        dominant-baseline="middle"
-        font-size="16"
-        text-anchor="start"
-        x="20"
-        y="40"
-      >
-        |0⟩
-      </text>
-      <text
-        dominant-baseline="middle"
-        font-size="16"
-        text-anchor="start"
-        x="20"
-        y="100"
-      >
-        |0⟩
-      </text>
-    </g>
-    <g>
-      <g>
-        <line
-          x1="40"
-          x2="140"
-          y1="40"
-          y2="40"
-        />
-        <text
-          dominant-baseline="hanging"
-          font-size="75%"
-          text-anchor="start"
-          x="40"
-          y="24"
-        >
-          q0
-        </text>
-      </g>
-      <g>
-        <line
-          x1="40"
-          x2="140"
-          y1="100"
-          y2="100"
-        />
-        <text
-          dominant-baseline="hanging"
-          font-size="75%"
-          text-anchor="start"
-          x="40"
-          y="84"
-        >
-          q1
-        </text>
-      </g>
-      <g>
-        <line
-          class="register-classical"
-          x1="101"
-          x2="101"
-          y1="100"
-          y2="139"
-        />
-        <line
-          class="register-classical"
-          x1="99"
-          x2="99"
-          y1="100"
-          y2="141"
-        />
-        <line
-          class="register-classical"
-          x1="101"
-          x2="140"
-          y1="139"
-          y2="139"
-        />
-        <line
-          class="register-classical"
-          x1="99"
-          x2="140"
-          y1="141"
-          y2="141"
-        />
-      </g>
-    </g>
-    <g>
-      <g
-        class="gate"
-        data-id="0"
-        data-zoom-in="false"
-        data-zoom-out="false"
-      >
-        <g>
-          <g>
-            <rect
-              class="gate-unitary"
-              height="40"
-              width="40"
-              x="80"
-              y="20"
-            />
-            <text
-              font-size="14"
-              x="100"
-              y="40"
-            >
-              H
-            </text>
-          </g>
-        </g>
-      </g>
-      <g
-        class="gate"
-        data-id="1"
-        data-zoom-in="false"
-        data-zoom-out="false"
-      >
-        <g>
-          <rect
-            class="gate-measure"
-            height="40"
-            width="40"
-            x="80"
-            y="80"
-          />
-          <path
-            class="arc-measure"
-            d="M 115 102 A 15 12 0 0 0 85 102"
-          />
-          <line
-            x1="100"
-            x2="112"
-            y1="108"
-            y2="88"
-          />
-        </g>
-      </g>
-    </g>
-  </svg>
-</div>
-`;
-
-exports[`Testing createDropzone create dropzone on the left 1`] = `
-<rect
-  class="dropzone"
-  data-dropzone-position="left"
-  data-id="0"
-  height="20"
-  width="20"
-  x="0"
-  y="0"
-/>
-`;
-
-exports[`Testing createDropzone create dropzone on the right 1`] = `
-<rect
-  class="dropzone"
-  data-dropzone-position="right"
-  data-id="0"
-  height="20"
-  width="20"
-  x="0"
-  y="0"
-/>
-`;
-
-exports[`Testing createLeftDropzone create left dropzone 1`] = `
-<rect
-  class="dropzone"
-  data-dropzone-position="left"
-  data-id="0"
-  height="20"
-  width="10"
-  x="-10"
-  y="0"
-/>
-`;
-
-exports[`Testing createRightDropzone create dropzone right 1`] = `
-<rect
-  class="dropzone"
-  data-dropzone-position="right"
-  data-id="0"
-  height="20"
-  width="10"
-  x="20"
-  y="0"
-/>
-`;
-
-exports[`Testing cursorCopy turn off copy cursor 1`] = `
-<div
-  class="copying"
-/>
-`;
-
-exports[`Testing cursorCopy turn off copy cursor 2`] = `
-<div
-  class=""
-/>
-`;
-
-exports[`Testing cursorCopy turn on and off copy cursor 1`] = `
-<div
-  class=""
-/>
-`;
-
-exports[`Testing cursorCopy turn on and off copy cursor 2`] = `
-<div
-  class=""
-/>
-`;
-
-exports[`Testing cursorCopy turn on copy cursor 1`] = `<div />`;
-
-exports[`Testing cursorCopy turn on copy cursor 2`] = `
-<div
-  class="copying"
-/>
-`;
-
-exports[`Testing cursorMove turn off move cursor 1`] = `
-<div
-  class="moving"
-/>
-`;
-
-exports[`Testing cursorMove turn off move cursor 2`] = `
-<div
-  class=""
-/>
-`;
-
-exports[`Testing cursorMove turn on and off move cursor 1`] = `
-<div
-  class=""
-/>
-`;
-
-exports[`Testing cursorMove turn on and off move cursor 2`] = `
-<div
-  class=""
-/>
-`;
-
-exports[`Testing cursorMove turn on move cursor 1`] = `<div />`;
-
-exports[`Testing cursorMove turn on move cursor 2`] = `
-<div
-  class="moving"
-/>
-`;
-
-exports[`Testing getGateElems get 2 gates 1`] = `
-Array [
-  <g
-    class="gate"
-    data-id="0"
-    data-zoom-in="false"
-    data-zoom-out="false"
-  >
-    <g>
-      <g>
-        <rect
-          class="gate-unitary"
-          height="40"
-          width="40"
-          x="80"
-          y="20"
-        />
-        <text
-          font-size="14"
-          x="100"
-          y="40"
-        >
-          H
-        </text>
-      </g>
-    </g>
-  </g>,
-  <g
-    class="gate"
-    data-id="1"
-    data-zoom-in="false"
-    data-zoom-out="false"
-  >
-    <g>
-      <rect
-        class="gate-measure"
-        height="40"
-        width="40"
-        x="80"
-        y="80"
-      />
-      <path
-        class="arc-measure"
-        d="M 115 102 A 15 12 0 0 0 85 102"
-      />
-      <line
-        x1="100"
-        x2="112"
-        y1="108"
-        y2="88"
-      />
-    </g>
-  </g>,
-]
-`;
-
-exports[`Testing getGateElems get 3 gates 1`] = `
-Array [
-  <g
-    class="gate"
-    data-id="0"
-    data-zoom-in="false"
-    data-zoom-out="false"
-  >
-    <g>
-      <g>
-        <rect
-          class="gate-unitary"
-          height="40"
-          width="40"
-          x="80"
-          y="20"
-        />
-        <text
-          font-size="14"
-          x="100"
-          y="40"
-        >
-          H
-        </text>
-      </g>
-    </g>
-  </g>,
-  <g
-    class="gate"
-    data-id="1"
-    data-zoom-in="false"
-    data-zoom-out="false"
-  >
-    <line
-      x1="160"
-      x2="160"
-      y1="40"
-      y2="100"
-    />
-    <circle
-      class="control-dot"
-      cx="160"
-      cy="40"
-      r="5"
-    />
-    <g
-      class="oplus"
-    >
-      <circle
-        cx="160"
-        cy="100"
-        r="15"
-      />
-      <line
-        x1="160"
-        x2="160"
-        y1="85"
-        y2="115"
-      />
-      <line
-        x1="145"
-        x2="175"
-        y1="100"
-        y2="100"
-      />
-    </g>
-  </g>,
-  <g
-    class="gate"
-    data-id="2"
-    data-zoom-in="false"
-    data-zoom-out="false"
-  >
-    <g>
-      <rect
-        class="gate-measure"
-        height="40"
-        width="40"
-        x="200"
-        y="80"
-      />
-      <path
-        class="arc-measure"
-        d="M 235 102 A 15 12 0 0 0 205 102"
-      />
-      <line
-        x1="220"
-        x2="232"
-        y1="108"
-        y2="88"
-      />
-    </g>
-  </g>,
-]
-`;
-
-exports[`Testing getWireElems get 2 wires 1`] = `
-Array [
-  <g>
-    <line
-      x1="40"
-      x2="260"
-      y1="40"
-      y2="40"
-    />
-    <text
-      dominant-baseline="hanging"
-      font-size="75%"
-      text-anchor="start"
-      x="40"
-      y="24"
-    >
-      q0
-    </text>
-  </g>,
-  <g>
-    <line
-      x1="40"
-      x2="260"
-      y1="100"
-      y2="100"
-    />
-    <text
-      dominant-baseline="hanging"
-      font-size="75%"
-      text-anchor="start"
-      x="40"
-      y="84"
-    >
-      q1
-    </text>
-  </g>,
-]
-`;
-
-exports[`Testing handleGateMouseDown copying, ctrlKey is true 1`] = `
-<div
-  class="copying"
-/>
-`;
-
-exports[`Testing handleGateMouseDown moving, ctrlKey is false 1`] = `
-<div
-  class="moving"
-/>
-`;
diff --git a/__tests__/__snapshots__/gateFormatter.test.ts.snap b/__tests__/__snapshots__/gateFormatter.test.ts.snap
index e75b6f54..5596850a 100644
--- a/__tests__/__snapshots__/gateFormatter.test.ts.snap
+++ b/__tests__/__snapshots__/gateFormatter.test.ts.snap
@@ -1326,7 +1326,9 @@ exports[`Testing _controlledGate SWAP gate 1`] = `
     cy="40"
     r="5"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1340,7 +1342,9 @@ exports[`Testing _controlledGate SWAP gate 1`] = `
       y2="92"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1373,7 +1377,9 @@ exports[`Testing _controlledGate SWAP gate 2`] = `
     cy="160"
     r="5"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1387,7 +1393,9 @@ exports[`Testing _controlledGate SWAP gate 2`] = `
       y2="32"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1420,7 +1428,9 @@ exports[`Testing _controlledGate SWAP gate 3`] = `
     cy="100"
     r="5"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1434,7 +1444,9 @@ exports[`Testing _controlledGate SWAP gate 3`] = `
       y2="32"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1578,7 +1590,9 @@ exports[`Testing _formatGate controlled swap gate 1`] = `
     cy="40"
     r="5"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1592,7 +1606,9 @@ exports[`Testing _formatGate controlled swap gate 1`] = `
       y2="92"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -1875,7 +1891,9 @@ exports[`Testing _formatGate swap gate 1`] = `
       x="60"
       y="20"
     />
-    <g>
+    <g
+      class="cross"
+    >
       <line
         x1="72"
         x2="88"
@@ -1889,7 +1907,9 @@ exports[`Testing _formatGate swap gate 1`] = `
         y2="32"
       />
     </g>
-    <g>
+    <g
+      class="cross"
+    >
       <line
         x1="72"
         x2="88"
@@ -2419,7 +2439,9 @@ exports[`Testing _swap Adjacent swap 1`] = `
     x="60"
     y="20"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -2433,7 +2455,9 @@ exports[`Testing _swap Adjacent swap 1`] = `
       y2="32"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -2465,7 +2489,9 @@ exports[`Testing _swap Adjacent swap 2`] = `
     x="60"
     y="20"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -2479,7 +2505,9 @@ exports[`Testing _swap Adjacent swap 2`] = `
       y2="92"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -2511,7 +2539,9 @@ exports[`Testing _swap Non-adjacent swap 1`] = `
     x="60"
     y="20"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -2525,7 +2555,9 @@ exports[`Testing _swap Non-adjacent swap 1`] = `
       y2="32"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -2557,7 +2589,9 @@ exports[`Testing _swap Non-adjacent swap 2`] = `
     x="60"
     y="20"
   />
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"
@@ -2571,7 +2605,9 @@ exports[`Testing _swap Non-adjacent swap 2`] = `
       y2="152"
     />
   </g>
-  <g>
+  <g
+    class="cross"
+  >
     <line
       x1="72"
       x2="88"

From bddc2d18c86fd176f136b21a5e262bc5ff5e9413 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 13:12:17 -0400
Subject: [PATCH 031/108] Update tests

---
 __tests__/editable.test.ts | 1344 +-----------------------------------
 src/editable.ts            |    9 +-
 2 files changed, 44 insertions(+), 1309 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 50e7762d..209f21bd 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -1,1327 +1,59 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import { Circuit, Operation } from '../src/circuit';
 import { exportedForTesting } from '../src/editable';
-import { RegisterType } from '../src/register';
-import { draw, STYLES } from '../src/index';
-import { Sqore } from '../src/sqore';
 
-const {
-    // addEditable
-    addDropzones,
-    addDocumentEvents,
-    addDropzoneEvents,
-    addMouseEvents,
-    handleGateMouseDown,
-    // handleDropzoneMouseUp
-    getGateElems,
-    getWireElems,
-    createDropzone,
-    createLeftDropzone,
-    createRightDropzone,
-    getParent,
-    getGate,
-    getRenderFn,
-    getDataId,
-    splitDataId,
-    getWireElemsY,
-    getWireElemY,
-    getWireElemText,
-    getClosestWireY,
-    getDropzonePosition,
-    insertBefore,
-    insertAfter,
-    deleteAt,
-    cursorMove,
-    cursorCopy,
-} = exportedForTesting;
+const { _indexes, _lastIndex, _center } = exportedForTesting;
 
-// Utlities
-describe('Testing getDataId', () => {
-    const elem = document.createElement('div');
-    test('with with no data-id', () => {
-        expect(getDataId(elem)).toBe('');
-    });
-    test('with level 0 data-id', () => {
-        elem.setAttribute('data-id', '0');
-        expect(getDataId(elem)).toBe('0');
-    });
-
-    test('with level 1 data-id', () => {
-        elem.setAttribute('data-id', '0-1');
-        expect(getDataId(elem)).toBe('0-1');
-    });
-});
-
-describe('Testing splitDataId', () => {
-    test('with empty dataId', () => {
-        expect(splitDataId('')).toStrictEqual([]);
-    });
-    test('with level 0 data-id', () => {
-        expect(splitDataId('1')).toStrictEqual([1]);
-    });
-
-    test('with level 1 data-id', () => {
-        expect(splitDataId('1-2')).toStrictEqual([1, 2]);
-    });
-});
-
-describe('Testing cursorMove', () => {
-    const container = document.createElement('div');
-    test('turn on move cursor', () => {
-        expect(container).toMatchSnapshot();
-        cursorMove(container, true);
-        expect(container).toMatchSnapshot();
-    });
-    test('turn off move cursor', () => {
-        expect(container).toMatchSnapshot();
-        cursorMove(container, false);
-        expect(container).toMatchSnapshot();
-    });
-    test('turn on and off move cursor', () => {
-        expect(container).toMatchSnapshot();
-        cursorMove(container, true);
-        cursorMove(container, false);
-        expect(container).toMatchSnapshot();
-    });
-});
-
-describe('Testing cursorCopy', () => {
-    const container = document.createElement('div');
-    test('turn on copy cursor', () => {
-        expect(container).toMatchSnapshot();
-        cursorCopy(container, true);
-        expect(container).toMatchSnapshot();
-    });
-    test('turn off copy cursor', () => {
-        expect(container).toMatchSnapshot();
-        cursorCopy(container, false);
-        expect(container).toMatchSnapshot();
-    });
-    test('turn on and off copy cursor', () => {
-        expect(container).toMatchSnapshot();
-        cursorCopy(container, true);
-        cursorCopy(container, false);
-        expect(container).toMatchSnapshot();
-    });
-});
-
-describe('Testing deleteAt', () => {
-    const operations: Operation[] = [];
-    beforeEach(() => {
-        Object.assign(operations, [
-            {
-                gate: 'X',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Y',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Z',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ]);
-    });
-    test('delete X at index 0', () => {
-        deleteAt(operations, 0);
-        expect(operations).toStrictEqual([
-            {
-                gate: 'Y',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Z',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ]);
-    });
-    test('delete Y at index 1', () => {
-        deleteAt(operations, 1);
-        expect(operations).toStrictEqual([
-            {
-                gate: 'X',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Z',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ]);
-    });
-    test('delete Z and X at index 2, 0', () => {
-        deleteAt(operations, 2);
-        deleteAt(operations, 0);
-        expect(operations).toStrictEqual([
-            {
-                gate: 'Y',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ]);
-    });
-});
-
-describe('Testing insertBefore', () => {
-    test('insert before X', () => {
-        const operations = [
-            {
-                gate: 'X',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Z',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ];
-        const newGate = {
-            gate: 'Y',
-            isMeasurement: false,
-            isConditional: false,
-            isControlled: false,
-            isAdjoint: false,
-            controls: [],
-            targets: [{ type: RegisterType.Qubit, qId: 0 }],
-        };
-        insertBefore(operations, 0, newGate);
-        expect(operations).toStrictEqual([
-            {
-                gate: 'Y',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'X',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Z',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ]);
-    });
-});
-
-describe('Testing insertAfter', () => {
-    test('insert after X', () => {
-        const operations = [
-            {
-                gate: 'X',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Z',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ];
-        const newGate = {
-            gate: 'Y',
-            isMeasurement: false,
-            isConditional: false,
-            isControlled: false,
-            isAdjoint: false,
-            controls: [],
-            targets: [{ type: RegisterType.Qubit, qId: 0 }],
-        };
-        insertAfter(operations, 0, newGate);
-        expect(operations).toStrictEqual([
-            {
-                gate: 'X',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Y',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-            {
-                gate: 'Z',
-                isMeasurement: false,
-                isConditional: false,
-                isControlled: false,
-                isAdjoint: false,
-                controls: [],
-                targets: [{ type: RegisterType.Qubit, qId: 0 }],
-            },
-        ]);
-    });
-});
-
-describe('Testing getDropzonePosition', () => {
-    let svgElem: SVGElement;
-    let dropzoneElem: SVGElement;
-    beforeEach(() => {
-        svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGElement;
-        dropzoneElem = document.createElementNS('http://www.w3.org/2000/svg', 'rect') as SVGElement;
-        svgElem.append(dropzoneElem);
-    });
-    test('get position of non-dropzone', () => {
-        expect(() => getDropzonePosition(dropzoneElem)).toThrowError('Position not found');
-    });
-    test('get position of dropzone on the left', () => {
-        dropzoneElem.setAttribute('data-dropzone-position', 'left');
-        expect(getDropzonePosition(dropzoneElem)).toBe('left');
-    });
-    test('get position of dropzone on the right', () => {
-        dropzoneElem.setAttribute('data-dropzone-position', 'right');
-        expect(getDropzonePosition(dropzoneElem)).toBe('right');
-    });
-});
-
-describe('Testing getWireElementText', () => {
-    let svgElem: SVGElement;
-    let groupElem: SVGGElement;
-    let textElem: SVGGElement;
-    beforeEach(() => {
-        svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGElement;
-        groupElem = document.createElementNS('http://www.w3.org/2000/svg', 'g') as SVGGElement;
-        textElem = document.createElementNS('http://www.w3.org/2000/svg', 'text') as SVGTextElement;
-        groupElem.append(textElem);
-        svgElem.append(groupElem);
-    });
-    test('text element not exists', () => {
-        textElem.remove();
-        expect(() => getWireElemText(groupElem)).toThrowError('Text not found');
-    });
-    test('get text element without textContent', () => {
-        expect(() => getWireElemText(groupElem)).toThrowError('Text not found');
-    });
-    test('get text element empty textContent', () => {
-        expect(() => getWireElemText(groupElem)).toThrowError('Text not found');
-    });
-    test('should return q0', () => {
-        textElem.textContent = 'q0';
-        expect(getWireElemText(groupElem)).toEqual('q0');
-    });
-    test('should return q1', () => {
-        textElem.textContent = 'q1';
-        expect(getWireElemText(groupElem)).toEqual('q1');
-    });
-});
-
-describe('Testing getWireElemY', () => {
-    let svgElem: SVGElement;
-    let groupElem: SVGGElement;
-    let lineElem: SVGGElement;
-    beforeEach(() => {
-        svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg') as SVGElement;
-        groupElem = document.createElementNS('http://www.w3.org/2000/svg', 'g') as SVGGElement;
-        lineElem = document.createElementNS('http://www.w3.org/2000/svg', 'line') as SVGLineElement;
-        groupElem.append(lineElem);
-        svgElem.append(groupElem);
-    });
-    test('line element not exists', () => {
-        lineElem.remove();
-        expect(() => getWireElemY(groupElem)).toThrowError('y not found');
-    });
-    test('get y element without y value', () => {
-        expect(() => getWireElemY(groupElem)).toThrowError('y not found');
-    });
-    test('get text element empty textContent', () => {
-        expect(() => getWireElemY(groupElem)).toThrowError('y not found');
-    });
-    test('should return 40', () => {
-        lineElem.setAttribute('y1', '40');
-        expect(getWireElemY(groupElem)).toEqual(40);
-    });
-    test('should return 99', () => {
-        lineElem.setAttribute('y1', '99');
-        expect(getWireElemY(groupElem)).toEqual(99);
-    });
-});
-
-describe('Testing getParent', () => {
-    test('with level 0 gate', () => {
-        const operations = [
-            {
-                gate: 'H',
-                targets: [{ qId: 0 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 0 }],
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'Measure',
-                isMeasurement: true,
-                controls: [{ qId: 1 }],
-                targets: [{ type: 1, qId: 1, cId: 0 }],
-            },
-        ];
-        expect(getParent('0', operations)).toStrictEqual([
-            {
-                gate: 'H',
-                targets: [{ qId: 0 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 0 }],
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'Measure',
-                isMeasurement: true,
-                controls: [{ qId: 1 }],
-                targets: [{ type: 1, qId: 1, cId: 0 }],
-            },
-        ]);
-    });
-    test('with level 1 gate', () => {
-        const operations = [
-            {
-                gate: 'Foo',
-                conditionalRender: 3,
-                targets: [{ qId: 0 }, { qId: 1 }],
-                children: [
-                    {
-                        gate: 'H',
-                        targets: [{ qId: 1 }],
-                    },
-                    {
-                        gate: 'RX',
-                        displayArgs: '(0.25)',
-                        isControlled: true,
-                        controls: [{ qId: 1 }],
-                        targets: [{ qId: 0 }],
-                    },
-                ],
-            },
-            {
-                gate: 'X',
-                targets: [{ qId: 3 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 1 }],
-                targets: [{ qId: 2 }, { qId: 3 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 2 }, { qId: 3 }],
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 1 }, { qId: 3 }],
-                targets: [{ qId: 2 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 2 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'measure',
-                isMeasurement: true,
-                controls: [{ qId: 0 }],
-                targets: [{ type: 1, qId: 0, cId: 0 }],
-            },
-            {
-                gate: 'ApplyIfElseR',
-                isConditional: true,
-                controls: [{ type: 1, qId: 0, cId: 0 }],
-                targets: [],
-                children: [
-                    {
-                        gate: 'H',
-                        targets: [{ qId: 1 }],
-                        conditionalRender: 1,
-                    },
-                    {
-                        gate: 'X',
-                        targets: [{ qId: 1 }],
-                        conditionalRender: 1,
-                    },
-                    {
-                        gate: 'X',
-                        isControlled: true,
-                        controls: [{ qId: 0 }],
-                        targets: [{ qId: 1 }],
-                        conditionalRender: 2,
-                    },
-                    {
-                        gate: 'Foo',
-                        targets: [{ qId: 3 }],
-                        conditionalRender: 2,
-                    },
-                ],
-            },
-            {
-                gate: 'SWAP',
-                targets: [{ qId: 0 }, { qId: 2 }],
-                children: [
-                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                    { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
-                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                ],
-            },
-            {
-                gate: 'ZZ',
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'ZZ',
-                targets: [{ qId: 0 }, { qId: 1 }],
-            },
-            {
-                gate: 'XX',
-                isControlled: true,
-                controls: [{ qId: 0 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'XX',
-                isControlled: true,
-                controls: [{ qId: 2 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'XX',
-                isControlled: true,
-                controls: [{ qId: 0 }, { qId: 2 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-        ];
-        expect(getParent('0-1', operations)).toStrictEqual([
-            {
-                gate: 'H',
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'RX',
-                displayArgs: '(0.25)',
-                isControlled: true,
-                controls: [{ qId: 1 }],
-                targets: [{ qId: 0 }],
-            },
-        ]);
-    });
-});
-
-describe('Testing getGate', () => {
-    test('should return H gate', () => {
-        const operations = [
-            {
-                gate: 'H',
-                targets: [{ qId: 0 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 0 }],
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'Measure',
-                isMeasurement: true,
-                controls: [{ qId: 1 }],
-                targets: [{ type: 1, qId: 1, cId: 0 }],
-            },
-        ];
-        expect(getGate('0', operations)).toStrictEqual({
-            gate: 'H',
-            targets: [{ qId: 0 }],
-        });
-    });
-    test('should return X gate', () => {
-        const operations = [
-            {
-                gate: 'H',
-                targets: [{ qId: 0 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 0 }],
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'Measure',
-                isMeasurement: true,
-                controls: [{ qId: 1 }],
-                targets: [{ type: 1, qId: 1, cId: 0 }],
-            },
-        ];
-        expect(getGate('1', operations)).toStrictEqual({
-            gate: 'X',
-            isControlled: true,
-            controls: [{ qId: 0 }],
-            targets: [{ qId: 1 }],
-        });
-    });
-    test('should return RX', () => {
-        const operations = [
-            {
-                gate: 'Foo',
-                conditionalRender: 3,
-                targets: [{ qId: 0 }, { qId: 1 }],
-                children: [
-                    {
-                        gate: 'H',
-                        targets: [{ qId: 1 }],
-                    },
-                    {
-                        gate: 'RX',
-                        displayArgs: '(0.25)',
-                        isControlled: true,
-                        controls: [{ qId: 1 }],
-                        targets: [{ qId: 0 }],
-                    },
-                ],
-            },
-            {
-                gate: 'X',
-                targets: [{ qId: 3 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 1 }],
-                targets: [{ qId: 2 }, { qId: 3 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 2 }, { qId: 3 }],
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 1 }, { qId: 3 }],
-                targets: [{ qId: 2 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 2 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'measure',
-                isMeasurement: true,
-                controls: [{ qId: 0 }],
-                targets: [{ type: 1, qId: 0, cId: 0 }],
-            },
-            {
-                gate: 'ApplyIfElseR',
-                isConditional: true,
-                controls: [{ type: 1, qId: 0, cId: 0 }],
-                targets: [],
-                children: [
-                    {
-                        gate: 'H',
-                        targets: [{ qId: 1 }],
-                        conditionalRender: 1,
-                    },
-                    {
-                        gate: 'X',
-                        targets: [{ qId: 1 }],
-                        conditionalRender: 1,
-                    },
-                    {
-                        gate: 'X',
-                        isControlled: true,
-                        controls: [{ qId: 0 }],
-                        targets: [{ qId: 1 }],
-                        conditionalRender: 2,
-                    },
-                    {
-                        gate: 'Foo',
-                        targets: [{ qId: 3 }],
-                        conditionalRender: 2,
-                    },
-                ],
-            },
-            {
-                gate: 'SWAP',
-                targets: [{ qId: 0 }, { qId: 2 }],
-                children: [
-                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                    { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
-                    { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                ],
-            },
-            {
-                gate: 'ZZ',
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'ZZ',
-                targets: [{ qId: 0 }, { qId: 1 }],
-            },
-            {
-                gate: 'XX',
-                isControlled: true,
-                controls: [{ qId: 0 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'XX',
-                isControlled: true,
-                controls: [{ qId: 2 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-            {
-                gate: 'XX',
-                isControlled: true,
-                controls: [{ qId: 0 }, { qId: 2 }],
-                targets: [{ qId: 1 }, { qId: 3 }],
-            },
-        ];
-        expect(getGate('0-1', operations)).toStrictEqual({
-            gate: 'RX',
-            displayArgs: '(0.25)',
-            isControlled: true,
-            controls: [{ qId: 1 }],
-            targets: [{ qId: 0 }],
-        });
-    });
-});
-
-describe('Testing addDocumentEvents', () => {
-    test('verify document events', () => {
-        const container = document.createElement('div');
-        expect(container).toMatchSnapshot();
-        addDocumentEvents(container);
-        expect(container).toMatchSnapshot();
-    });
-});
-
-describe('Testing handleGateMouseDown', () => {
-    test('copying, ctrlKey is true', () => {
-        const container = document.createElement('div');
-        const ev = new MouseEvent('mousedown', { ctrlKey: true });
-        handleGateMouseDown(ev, container);
-        expect(container).toMatchSnapshot();
-    });
-    test('moving, ctrlKey is false', () => {
-        const container = document.createElement('div');
-        const ev = new MouseEvent('mousedown', { ctrlKey: false });
-        handleGateMouseDown(ev, container);
-        expect(container).toMatchSnapshot();
-    });
-});
-
-describe('Testing getGateElems', () => {
-    test('get 2 gates', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-        const gateElems = getGateElems(container);
-        expect(gateElems).toHaveLength(2);
-        expect(gateElems).toMatchSnapshot();
-    });
-    test('get 3 gates', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-        const gateElems = getGateElems(container);
-        expect(gateElems).toHaveLength(3);
-        expect(gateElems).toMatchSnapshot();
-    });
-});
-
-describe('Testing getWireElems', () => {
-    test('get 2 wires', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-        const wireElems = getWireElems(container);
-        expect(wireElems).toHaveLength(2);
-        expect(wireElems).toMatchSnapshot();
-    });
-});
-
-describe('Testing createDropzone', () => {
-    test('create dropzone on the left', () => {
-        expect(createDropzone(0, 0, 20, 20, '0', 'left')).toMatchSnapshot();
-    });
-    test('create dropzone on the right', () => {
-        expect(createDropzone(0, 0, 20, 20, '0', 'right')).toMatchSnapshot();
-    });
-});
-
-describe('Testing createLeftDropzone', () => {
-    test('create left dropzone', () => {
-        expect(createLeftDropzone(0, 0, 20, '0')).toMatchSnapshot();
-    });
-});
-
-describe('Testing createRightDropzone', () => {
-    test('create dropzone right', () => {
-        expect(createRightDropzone(0, 0, 20, 20, '0')).toMatchSnapshot();
-    });
-});
-
-describe('Testing getClosestWireY', () => {
-    test('should return 40', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        const wires = { '40': 'q0', '100': 'q1' };
-        draw(circuit, container, STYLES['default']);
-        expect(getClosestWireY(50, wires)).toEqual(40);
-    });
-    test('should return 100', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        const wires = { '40': 'q0', '100': 'q1' };
-        draw(circuit, container, STYLES['default']);
-        expect(getClosestWireY(85, wires)).toEqual(100);
-    });
-    test('should return null', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        const wires = { '40': 'q0', '100': 'q1' };
-        draw(circuit, container, STYLES['default']);
-        expect(getClosestWireY(120, wires)).toEqual(null);
-    });
-});
-
-describe('test getWireElemsY', () => {
-    test('get 2 wires', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        const expected = { '40': 'q0', '100': 'q1' };
-        draw(circuit, container, STYLES['default']);
-        expect(getWireElemsY(container)).toStrictEqual(expected);
-    });
-    test('get 4 wires', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
-            operations: [
-                {
-                    gate: 'Foo',
-                    conditionalRender: 3,
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                        },
-                        {
-                            gate: 'RX',
-                            displayArgs: '(0.25)',
-                            isControlled: true,
-                            controls: [{ qId: 1 }],
-                            targets: [{ qId: 0 }],
-                        },
-                    ],
-                },
-                {
-                    gate: 'X',
-                    targets: [{ qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ qId: 2 }, { qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }, { qId: 3 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }, { qId: 3 }],
-                    targets: [{ qId: 2 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ type: 1, qId: 0, cId: 0 }],
-                },
-                {
-                    gate: 'ApplyIfElseR',
-                    isConditional: true,
-                    controls: [{ type: 1, qId: 0, cId: 0 }],
-                    targets: [],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            isControlled: true,
-                            controls: [{ qId: 0 }],
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 2,
-                        },
-                        {
-                            gate: 'Foo',
-                            targets: [{ qId: 3 }],
-                            conditionalRender: 2,
-                        },
-                    ],
-                },
-                {
-                    gate: 'SWAP',
-                    targets: [{ qId: 0 }, { qId: 2 }],
-                    children: [
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                    ],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }, { qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-            ],
-        };
-        const expected = { '40': 'q0', '120': 'q1', '180': 'q2', '240': 'q3' };
-        draw(circuit, container, STYLES['default']);
-        expect(getWireElemsY(container)).toStrictEqual(expected);
-    });
-});
-
-describe('Testing addDropzoneEvents', () => {
-    interface Context {
-        container: HTMLElement;
-        operations: Operation[];
-        wires: Wires;
-        renderFn: () => void;
-    }
-
-    interface Wires {
-        [y: string]: string;
-    }
-
-    test('add 1 event', () => {
-        const container = document.createElement('div');
-        const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
-        const dropzoneElem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        svgElem.append(dropzoneElem);
-        container.append(svgElem);
-
-        const context: Context = {
-            container: container,
-            operations: [],
-            wires: {},
-            renderFn: () => {
-                return;
-            },
-        };
-        addDropzoneEvents(context);
-        expect(container).toMatchSnapshot();
-    });
-    test('add 2 events', () => {
-        const container = document.createElement('div');
-        const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
-        const dropzoneElem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        const dropzoneElem1 = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        svgElem.append(dropzoneElem);
-        svgElem.append(dropzoneElem1);
-        container.append(svgElem);
-        interface Context {
-            container: HTMLElement;
-            operations: Operation[];
-            wires: Wires;
-            renderFn: () => void;
-        }
-
-        const context: Context = {
-            container: container,
-            operations: [],
-            wires: {},
-            renderFn: () => {
-                return;
-            },
-        };
-        addDropzoneEvents(context);
-        expect(container).toMatchSnapshot();
-    });
-});
-
-describe('Testing addMouseEvents', () => {
-    interface Context {
-        container: HTMLElement;
-        operations: Operation[];
-        wires: Wires;
-        renderFn: () => void;
-    }
-    interface Wires {
-        [y: string]: string;
-    }
-    test('verify mouse events', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-        const context: Context = {
-            container: container,
-            operations: [],
-            wires: {},
-            renderFn: () => {
-                return;
-            },
-        };
-        const svgElem = container.querySelector('svg');
-        if (svgElem != null) svgElem.removeAttribute('id');
-        addMouseEvents(context);
-        expect(container).toMatchSnapshot();
-    });
-});
-
-describe('Testing getRenderFn', () => {
-    test('check console.log displaying "onCircuitChange is triggered"', () => {
+describe('Test _center', () => {
+    test('should return {25,50}', () => {
         Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
             writable: true,
             value: () => ({
                 x: 0,
                 y: 0,
-                width: 0,
-                height: 0,
+                width: 50,
+                height: 100,
             }),
         });
-        const container = document.createElement('div');
-        const circuit: Circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        const sqore = new Sqore(circuit, STYLES['default']);
-        const onCircuitChange = () => console.log('onCircuitChange is triggered');
-        const renderFn = getRenderFn(container, sqore, onCircuitChange);
-
-        jest.spyOn(console, 'log');
-        renderFn();
-        expect(console.log).toHaveBeenCalledWith('onCircuitChange is triggered');
+        const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+        expect(_center(elem)).toStrictEqual({ cX: 25, cY: 50 });
     });
-});
-
-describe('Testing addDropzones', () => {
-    test('verify dropzones', () => {
+    test('should return {105,210}', () => {
         Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
             writable: true,
             value: () => ({
-                x: 0,
-                y: 0,
-                width: 0,
-                height: 0,
+                x: 100,
+                y: 200,
+                width: 10,
+                height: 20,
             }),
         });
-        const container = document.createElement('div');
-        const circuit: Circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default'], 0, true);
-        const svgElem = container.querySelector('svg');
-        if (svgElem != null) svgElem.removeAttribute('id');
-        addDropzones(container);
-        expect(container).toMatchSnapshot();
+        const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+        expect(_center(elem)).toStrictEqual({ cX: 105, cY: 210 });
+    });
+});
+
+describe('Test _lastIndex', () => {
+    test('"" should return undefined', () => {
+        expect(_lastIndex('')).toBeUndefined();
+    });
+    test('"0-0-1" should return 1', () => {
+        expect(_lastIndex('0-0-1')).toEqual(1);
+    });
+    test('"1-0-5" should return [1,0,5]', () => {
+        expect(_lastIndex('1-0-5')).toEqual(5);
+    });
+});
+
+describe('Test _indexes', () => {
+    test('"" should return []', () => {
+        expect(_indexes('')).toStrictEqual([]);
+    });
+    test('"0-0-1" should return [0,0,1]', () => {
+        expect(_indexes('0-0-1')).toStrictEqual([0, 0, 1]);
+    });
+    test('"1-0-1" should return [1,0,1]', () => {
+        expect(_indexes('1-0-1')).toStrictEqual([1, 0, 1]);
     });
 });
diff --git a/src/editable.ts b/src/editable.ts
index befbf36f..9ef55d13 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -370,9 +370,12 @@ const _circularMod = (value: number, offset: number, total: number) => {
     return (((value + offset) % total) + total) % total;
 };
 
-const _indexes = (dataId: string) => dataId.split('-').map((segment) => parseInt(segment));
+const _indexes = (dataId: string): number[] =>
+    dataId !== '' //
+        ? dataId.split('-').map((segment) => parseInt(segment))
+        : [];
 
-const _lastIndex = (dataId: string) => {
+const _lastIndex = (dataId: string): number | undefined => {
     return _indexes(dataId).pop();
 };
 
@@ -387,6 +390,6 @@ const _renderFn = (
     };
 };
 
-const exportedForTesting = {};
+const exportedForTesting = { _center, _indexes, _lastIndex };
 
 export { addEditable, exportedForTesting };

From 4b22f075373e9096f70d1346049f4c8e90a49610 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 13:43:38 -0400
Subject: [PATCH 032/108] Add tests for _center, _wireData

---
 __tests__/editable.test.ts | 58 +++++++++++++++++++++++++++++++++++++-
 src/editable.ts            |  8 +++---
 2 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 209f21bd..66874c99 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -2,8 +2,9 @@
 // Licensed under the MIT license.
 
 import { exportedForTesting } from '../src/editable';
+import { draw, STYLES } from '../src/index';
 
-const { _indexes, _lastIndex, _center } = exportedForTesting;
+const { _center, _wireData, _indexes, _lastIndex } = exportedForTesting;
 
 describe('Test _center', () => {
     test('should return {25,50}', () => {
@@ -34,6 +35,61 @@ describe('Test _center', () => {
     });
 });
 
+describe('Test _wireData', () => {
+    test('2 wires should return [40,100]', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+        expect(_wireData(container)).toStrictEqual([40, 100]);
+    });
+    test('3 wires should return [40,100, 180]', () => {
+        const container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }, { id: 2 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 2 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+        expect(_wireData(container)).toStrictEqual([40, 100, 180]);
+    });
+});
+
 describe('Test _lastIndex', () => {
     test('"" should return undefined', () => {
         expect(_lastIndex('')).toBeUndefined();
diff --git a/src/editable.ts b/src/editable.ts
index 9ef55d13..679de361 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -87,7 +87,7 @@ const _wirePrefixes = (wireData: number[]) => wireData.map((wireY, index) => ({
 /**
  *  Find center point of element
  */
-const _center = (elem: SVGGraphicsElement) => {
+const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
     const { x, y, width, height } = elem.getBBox();
     return { cX: x + width / 2, cY: y + height / 2 };
 };
@@ -161,7 +161,7 @@ const _dropzoneLayer = (context: Context) => {
     return dropzoneLayer;
 };
 
-const _wireData = (container: HTMLElement) => {
+const _wireData = (container: HTMLElement): number[] => {
     // elems include qubit wires and lines of measure gates
     const elems = container.querySelectorAll<SVGGElement>('svg > g:nth-child(3) > g');
     // filter out <g> elements having more than 2 elements because
@@ -170,7 +170,7 @@ const _wireData = (container: HTMLElement) => {
     const wireElems = Array.from(elems).filter((elem) => elem.childElementCount < 3);
     const wireData = wireElems.map((wireElem) => {
         const lineElem = wireElem.children[0] as SVGLineElement;
-        return lineElem.y1.baseVal.value;
+        return Number(lineElem.getAttribute('y1'));
     });
     return wireData;
 };
@@ -390,6 +390,6 @@ const _renderFn = (
     };
 };
 
-const exportedForTesting = { _center, _indexes, _lastIndex };
+const exportedForTesting = { _center, _wireData, _indexes, _lastIndex };
 
 export { addEditable, exportedForTesting };

From d975f8863c433c5dedd7ec4dfc9a8787277ca3fc Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 15:30:25 -0400
Subject: [PATCH 033/108] Add tests for _equivOperation, _equivOperationParent

---
 __tests__/__snapshots__/editable.test.ts.snap | 100 ++++++++++
 __tests__/editable.test.ts                    | 184 +++++++++++++++++-
 src/editable.ts                               |   9 +-
 3 files changed, 287 insertions(+), 6 deletions(-)
 create mode 100644 __tests__/__snapshots__/editable.test.ts.snap

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
new file mode 100644
index 00000000..6dd2e3a4
--- /dev/null
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -0,0 +1,100 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Test _equivOperation should return H gate 1`] = `
+Object {
+  "gate": "H",
+  "targets": Array [
+    Object {
+      "qId": 0,
+    },
+  ],
+}
+`;
+
+exports[`Test _equivOperation should return X gate 1`] = `
+Object {
+  "controls": Array [
+    Object {
+      "qId": 0,
+    },
+  ],
+  "gate": "X",
+  "isControlled": true,
+  "targets": Array [
+    Object {
+      "qId": 1,
+    },
+  ],
+}
+`;
+
+exports[`Test _equivOperationParent should return Foo 1`] = `
+Array [
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+    "displayArgs": "(0.25)",
+    "gate": "RX",
+    "isControlled": true,
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+]
+`;
+
+exports[`Test _equivOperationParent should return all operations 1`] = `
+Array [
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+    "gate": "X",
+    "isControlled": true,
+    "targets": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+    "gate": "Measure",
+    "isMeasurement": true,
+    "targets": Array [
+      Object {
+        "cId": 0,
+        "qId": 1,
+        "type": 1,
+      },
+    ],
+  },
+]
+`;
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 66874c99..102fbc10 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -4,7 +4,7 @@
 import { exportedForTesting } from '../src/editable';
 import { draw, STYLES } from '../src/index';
 
-const { _center, _wireData, _indexes, _lastIndex } = exportedForTesting;
+const { _center, _wireData, _equivOperation, _equivOperationParent, _indexes, _lastIndex } = exportedForTesting;
 
 describe('Test _center', () => {
     test('should return {25,50}', () => {
@@ -90,6 +90,188 @@ describe('Test _wireData', () => {
     });
 });
 
+describe('Test _equivOperation', () => {
+    const circuit = {
+        qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+        operations: [
+            {
+                gate: 'H',
+                targets: [{ qId: 0 }],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [{ qId: 0 }],
+                targets: [{ qId: 1 }],
+            },
+            {
+                gate: 'Measure',
+                isMeasurement: true,
+                controls: [{ qId: 1 }],
+                targets: [{ type: 1, qId: 1, cId: 0 }],
+            },
+        ],
+    };
+    test('should return H gate', () => {
+        expect(_equivOperation('0', circuit.operations)).toMatchSnapshot();
+    });
+    test('should return X gate', () => {
+        expect(_equivOperation('1', circuit.operations)).toMatchSnapshot();
+    });
+});
+
+describe('Test _equivOperationParent', () => {
+    test('should return Foo', () => {
+        const circuit = {
+            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
+            operations: [
+                {
+                    gate: 'Foo',
+                    conditionalRender: 3,
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                        },
+                        {
+                            gate: 'RX',
+                            displayArgs: '(0.25)',
+                            isControlled: true,
+                            controls: [{ qId: 1 }],
+                            targets: [{ qId: 0 }],
+                        },
+                    ],
+                },
+                {
+                    gate: 'X',
+                    targets: [{ qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ qId: 2 }, { qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }, { qId: 3 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }, { qId: 3 }],
+                    targets: [{ qId: 2 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ type: 1, qId: 0, cId: 0 }],
+                },
+                {
+                    gate: 'ApplyIfElseR',
+                    isConditional: true,
+                    controls: [{ type: 1, qId: 0, cId: 0 }],
+                    targets: [],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            isControlled: true,
+                            controls: [{ qId: 0 }],
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 2,
+                        },
+                        {
+                            gate: 'Foo',
+                            targets: [{ qId: 3 }],
+                            conditionalRender: 2,
+                        },
+                    ],
+                },
+                {
+                    gate: 'SWAP',
+                    targets: [{ qId: 0 }, { qId: 2 }],
+                    children: [
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                    ],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }, { qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+            ],
+        };
+        expect(_equivOperationParent('0-1', circuit.operations)).toMatchSnapshot();
+    });
+    test('should return all operations', () => {
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        expect(_equivOperationParent('0', circuit.operations)).toMatchSnapshot();
+    });
+});
+
 describe('Test _lastIndex', () => {
     test('"" should return undefined', () => {
         expect(_lastIndex('')).toBeUndefined();
diff --git a/src/editable.ts b/src/editable.ts
index 679de361..90129440 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -71,7 +71,7 @@ const _addDataWires = (container: HTMLElement) => {
  *  i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140).
  *       Function returns [40, 100, 140]
  */
-const _elemWires = (elem: SVGGraphicsElement, wireData: number[]) => {
+const _elemWireYs = (elem: SVGGraphicsElement, wireData: number[]) => {
     const { y, height } = elem.getBBox();
     return wireData.filter((wireY) => wireY > y && wireY < y + height);
 };
@@ -129,9 +129,9 @@ const _dropzoneLayer = (context: Context) => {
         } else {
             // Let group gates creating dropzones for each wire
             const { x } = elem.getBBox();
-            const elemWires = _elemWires(elem, wireData);
+            const elemWireYs = _elemWireYs(elem, wireData);
 
-            elemWires.map((wireY) => {
+            elemWireYs.map((wireY) => {
                 const wirePrefix = wirePrefixes.find((item) => item.wireY === wireY);
                 if (wirePrefix) {
                     const prefixX = wirePrefix.prefixX;
@@ -144,7 +144,6 @@ const _dropzoneLayer = (context: Context) => {
                     dropzoneLayer.appendChild(elemDropzone);
                 }
             });
-            console.log({ elem, x, elemWires });
         }
     });
 
@@ -390,6 +389,6 @@ const _renderFn = (
     };
 };
 
-const exportedForTesting = { _center, _wireData, _indexes, _lastIndex };
+const exportedForTesting = { _center, _wireData, _equivOperation, _equivOperationParent, _indexes, _lastIndex };
 
 export { addEditable, exportedForTesting };

From 5daff513fa1ce9804a3ff374487443014c95d74e Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 15:45:50 -0400
Subject: [PATCH 034/108] Add tests for _moveX, _copyX

---
 __tests__/__snapshots__/editable.test.ts.snap | 188 ++++++++++++++++++
 __tests__/editable.test.ts                    |  75 ++++++-
 src/editable.ts                               |  19 +-
 3 files changed, 275 insertions(+), 7 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 6dd2e3a4..700b6219 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -1,5 +1,107 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`Test _copyX copy elem from index 0 to index 1 1`] = `
+Array [
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+    "gate": "X",
+    "isControlled": true,
+    "targets": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+  },
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+    "gate": "Measure",
+    "isMeasurement": true,
+    "targets": Array [
+      Object {
+        "cId": 0,
+        "qId": 1,
+        "type": 1,
+      },
+    ],
+  },
+]
+`;
+
+exports[`Test _copyX copy elem from index 0 to last 1`] = `
+Array [
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+    "gate": "X",
+    "isControlled": true,
+    "targets": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+    "gate": "Measure",
+    "isMeasurement": true,
+    "targets": Array [
+      Object {
+        "cId": 0,
+        "qId": 1,
+        "type": 1,
+      },
+    ],
+  },
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+]
+`;
+
 exports[`Test _equivOperation should return H gate 1`] = `
 Object {
   "gate": "H",
@@ -98,3 +200,89 @@ Array [
   },
 ]
 `;
+
+exports[`Test _moveX move elem from index 0 to index 1 1`] = `
+Array [
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+    "gate": "X",
+    "isControlled": true,
+    "targets": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+  },
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+    "gate": "Measure",
+    "isMeasurement": true,
+    "targets": Array [
+      Object {
+        "cId": 0,
+        "qId": 1,
+        "type": 1,
+      },
+    ],
+  },
+]
+`;
+
+exports[`Test _moveX move elem from index 0 to last 1`] = `
+Array [
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+    "gate": "X",
+    "isControlled": true,
+    "targets": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+  },
+  Object {
+    "controls": Array [
+      Object {
+        "qId": 1,
+      },
+    ],
+    "gate": "Measure",
+    "isMeasurement": true,
+    "targets": Array [
+      Object {
+        "cId": 0,
+        "qId": 1,
+        "type": 1,
+      },
+    ],
+  },
+  Object {
+    "gate": "H",
+    "targets": Array [
+      Object {
+        "qId": 0,
+      },
+    ],
+  },
+]
+`;
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 102fbc10..d8f38394 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -2,9 +2,10 @@
 // Licensed under the MIT license.
 
 import { exportedForTesting } from '../src/editable';
-import { draw, STYLES } from '../src/index';
+import { Circuit, draw, STYLES } from '../src/index';
 
-const { _center, _wireData, _equivOperation, _equivOperationParent, _indexes, _lastIndex } = exportedForTesting;
+const { _center, _wireData, _equivOperation, _equivOperationParent, _moveX, _copyX, _indexes, _lastIndex } =
+    exportedForTesting;
 
 describe('Test _center', () => {
     test('should return {25,50}', () => {
@@ -272,6 +273,76 @@ describe('Test _equivOperationParent', () => {
     });
 });
 
+describe('Test _moveX', () => {
+    let circuit: Circuit;
+    beforeEach(() => {
+        circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+    });
+    test('move elem from index 0 to index 1', () => {
+        _moveX('0', '2', circuit.operations);
+        expect(circuit.operations).toMatchSnapshot();
+    });
+    test('move elem from index 0 to last', () => {
+        _moveX('0', '3', circuit.operations);
+        expect(circuit.operations).toMatchSnapshot();
+    });
+});
+
+describe('Test _copyX', () => {
+    let circuit: Circuit;
+    beforeEach(() => {
+        circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+    });
+    test('copy elem from index 0 to index 1', () => {
+        _copyX('0', '2', circuit.operations);
+        expect(circuit.operations).toMatchSnapshot();
+    });
+    test('copy elem from index 0 to last', () => {
+        _copyX('0', '3', circuit.operations);
+        expect(circuit.operations).toMatchSnapshot();
+    });
+});
+
 describe('Test _lastIndex', () => {
     test('"" should return undefined', () => {
         expect(_lastIndex('')).toBeUndefined();
diff --git a/src/editable.ts b/src/editable.ts
index 90129440..d3639ab8 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -282,7 +282,7 @@ const _equivOperation = (dataId: string | null, operations: Operation[]): Operat
     return operationParent[index];
 };
 
-const _moveX = (sourceId: string, targetId: string, operations: Operation[]) => {
+const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
     if (sourceId === targetId) return _equivOperation(sourceId, operations);
     const sourceOperation = _equivOperation(sourceId, operations);
     const sourceOperationParent = _equivOperationParent(sourceId, operations);
@@ -295,7 +295,7 @@ const _moveX = (sourceId: string, targetId: string, operations: Operation[]) =>
         sourceOperation == null ||
         sourceOperationParent == null
     )
-        return;
+        return null;
 
     // Insert sourceOperation to target last index
     const newSourceOperation: Operation = { ...sourceOperation };
@@ -309,7 +309,7 @@ const _moveX = (sourceId: string, targetId: string, operations: Operation[]) =>
     return newSourceOperation;
 };
 
-const _copyX = (sourceId: string, targetId: string, operations: Operation[]) => {
+const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
     if (sourceId === targetId) return _equivOperation(sourceId, operations);
     const sourceOperation = _equivOperation(sourceId, operations);
     const sourceOperationParent = _equivOperationParent(sourceId, operations);
@@ -322,7 +322,7 @@ const _copyX = (sourceId: string, targetId: string, operations: Operation[]) =>
         sourceOperation == null ||
         sourceOperationParent == null
     )
-        return;
+        return null;
 
     // Insert sourceOperation to target last index
     const newSourceOperation: Operation = JSON.parse(JSON.stringify(sourceOperation));
@@ -389,6 +389,15 @@ const _renderFn = (
     };
 };
 
-const exportedForTesting = { _center, _wireData, _equivOperation, _equivOperationParent, _indexes, _lastIndex };
+const exportedForTesting = {
+    _center,
+    _wireData,
+    _equivOperation,
+    _equivOperationParent,
+    _moveX,
+    _copyX,
+    _indexes,
+    _lastIndex,
+};
 
 export { addEditable, exportedForTesting };

From 1c1d07379181951488a1d491df0abf9b3eb1706c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 18:02:23 -0400
Subject: [PATCH 035/108] Add tests for _circularMod

---
 __tests__/editable.test.ts | 25 +++++++++++++++++++++++--
 src/editable.ts            | 15 ++++++++-------
 2 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index d8f38394..bc8de679 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -4,8 +4,17 @@
 import { exportedForTesting } from '../src/editable';
 import { Circuit, draw, STYLES } from '../src/index';
 
-const { _center, _wireData, _equivOperation, _equivOperationParent, _moveX, _copyX, _indexes, _lastIndex } =
-    exportedForTesting;
+const {
+    _center,
+    _wireData,
+    _equivOperation,
+    _equivOperationParent,
+    _moveX,
+    _copyX,
+    _circularMod,
+    _indexes,
+    _lastIndex,
+} = exportedForTesting;
 
 describe('Test _center', () => {
     test('should return {25,50}', () => {
@@ -343,6 +352,18 @@ describe('Test _copyX', () => {
     });
 });
 
+describe('Test _circularMod', () => {
+    test('should return 2', () => {
+        expect(_circularMod(5, 1, 4)).toEqual(2);
+    });
+    test('should return 1', () => {
+        expect(_circularMod(100, 1, 2)).toEqual(1);
+    });
+    test('should return 3', () => {
+        expect(_circularMod(3, 0, 4)).toEqual(3);
+    });
+});
+
 describe('Test _lastIndex', () => {
     test('"" should return undefined', () => {
         expect(_lastIndex('')).toBeUndefined();
diff --git a/src/editable.ts b/src/editable.ts
index d3639ab8..8a78647a 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -336,28 +336,28 @@ const _moveY = (sourceWire: string, targetWire: string, operation: Operation, wi
     _offsetRecursively(offset, operation, wireData);
 };
 
-const _offsetRecursively = (offsetY: number, operation: Operation, wireData: number[]) => {
+const _offsetRecursively = (offset: number, operation: Operation, wireData: number[]) => {
     const wireDataSize = wireData.length;
 
     // Offset all targets by offsetY value
     if (operation.targets != null) {
         operation.targets.forEach((target) => {
-            target.qId = _circularMod(target.qId, offsetY, wireDataSize);
-            if (target.cId) target.cId = _circularMod(target.cId, offsetY, wireDataSize);
+            target.qId = _circularMod(target.qId, offset, wireDataSize);
+            if (target.cId) target.cId = _circularMod(target.cId, offset, wireDataSize);
         });
     }
 
     // Offset all controls by offsetY value
     if (operation.controls != null) {
         operation.controls.forEach((control) => {
-            control.qId = _circularMod(control.qId, offsetY, wireDataSize);
-            if (control.cId) control.cId = _circularMod(control.qId, offsetY, wireDataSize);
+            control.qId = _circularMod(control.qId, offset, wireDataSize);
+            if (control.cId) control.cId = _circularMod(control.qId, offset, wireDataSize);
         });
     }
 
     // Offset recursively through all children
     if (operation.children != null) {
-        operation.children.forEach((child) => _offsetRecursively(offsetY, child, wireData));
+        operation.children.forEach((child) => _offsetRecursively(offset, child, wireData));
     }
 };
 
@@ -365,7 +365,7 @@ const _offsetRecursively = (offsetY: number, operation: Operation, wireData: num
  *  This modulo function always returns positive value based on total.
  *  i.e: value=0, offset=-1, total=4 returns 3 instead of -1
  */
-const _circularMod = (value: number, offset: number, total: number) => {
+const _circularMod = (value: number, offset: number, total: number): number => {
     return (((value + offset) % total) + total) % total;
 };
 
@@ -396,6 +396,7 @@ const exportedForTesting = {
     _equivOperationParent,
     _moveX,
     _copyX,
+    _circularMod,
     _indexes,
     _lastIndex,
 };

From 2b36d737f50f7bdfa3ad3154ad98be70303207d7 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 18:18:23 -0400
Subject: [PATCH 036/108] Add tests for _offsetRecursively

---
 __tests__/editable.test.ts | 27 ++++++++++++++++++++++++++-
 src/editable.ts            | 17 ++++++++---------
 2 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index bc8de679..ccdb33b5 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -2,7 +2,7 @@
 // Licensed under the MIT license.
 
 import { exportedForTesting } from '../src/editable';
-import { Circuit, draw, STYLES } from '../src/index';
+import { Circuit, draw, Operation, STYLES } from '../src/index';
 
 const {
     _center,
@@ -11,6 +11,7 @@ const {
     _equivOperationParent,
     _moveX,
     _copyX,
+    _offsetRecursively,
     _circularMod,
     _indexes,
     _lastIndex,
@@ -352,6 +353,30 @@ describe('Test _copyX', () => {
     });
 });
 
+describe('Test _offsetRecursively', () => {
+    let operation: Operation;
+    beforeEach(() => {
+        operation = {
+            gate: 'ZZ',
+            targets: [{ qId: 1 }, { qId: 3 }],
+        };
+    });
+    test('offset by 1', () => {
+        _offsetRecursively(operation, 1, 4);
+        expect(operation).toStrictEqual({
+            gate: 'ZZ',
+            targets: [{ qId: 2 }, { qId: 0 }],
+        });
+    });
+    test('offset by 2', () => {
+        _offsetRecursively(operation, 2, 4);
+        expect(operation).toStrictEqual({
+            gate: 'ZZ',
+            targets: [{ qId: 3 }, { qId: 1 }],
+        });
+    });
+});
+
 describe('Test _circularMod', () => {
     test('should return 2', () => {
         expect(_circularMod(5, 1, 4)).toEqual(2);
diff --git a/src/editable.ts b/src/editable.ts
index 8a78647a..18c97c06 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -333,31 +333,29 @@ const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Op
 
 const _moveY = (sourceWire: string, targetWire: string, operation: Operation, wireData: number[]) => {
     const offset = parseInt(targetWire) - parseInt(sourceWire);
-    _offsetRecursively(offset, operation, wireData);
+    _offsetRecursively(operation, offset, wireData.length);
 };
 
-const _offsetRecursively = (offset: number, operation: Operation, wireData: number[]) => {
-    const wireDataSize = wireData.length;
-
+const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires: number): void => {
     // Offset all targets by offsetY value
     if (operation.targets != null) {
         operation.targets.forEach((target) => {
-            target.qId = _circularMod(target.qId, offset, wireDataSize);
-            if (target.cId) target.cId = _circularMod(target.cId, offset, wireDataSize);
+            target.qId = _circularMod(target.qId, wireOffset, totalWires);
+            if (target.cId) target.cId = _circularMod(target.cId, wireOffset, totalWires);
         });
     }
 
     // Offset all controls by offsetY value
     if (operation.controls != null) {
         operation.controls.forEach((control) => {
-            control.qId = _circularMod(control.qId, offset, wireDataSize);
-            if (control.cId) control.cId = _circularMod(control.qId, offset, wireDataSize);
+            control.qId = _circularMod(control.qId, wireOffset, totalWires);
+            if (control.cId) control.cId = _circularMod(control.qId, wireOffset, totalWires);
         });
     }
 
     // Offset recursively through all children
     if (operation.children != null) {
-        operation.children.forEach((child) => _offsetRecursively(offset, child, wireData));
+        operation.children.forEach((child) => _offsetRecursively(child, wireOffset, totalWires));
     }
 };
 
@@ -396,6 +394,7 @@ const exportedForTesting = {
     _equivOperationParent,
     _moveX,
     _copyX,
+    _offsetRecursively,
     _circularMod,
     _indexes,
     _lastIndex,

From 6e2776c41b00f36a160f292c05e0625049822248 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 18:27:06 -0400
Subject: [PATCH 037/108] Add tests for _moveY

---
 __tests__/editable.test.ts | 25 +++++++++++++++++++++++++
 src/editable.ts            | 12 ++++++++----
 2 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index ccdb33b5..40244c02 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -11,6 +11,7 @@ const {
     _equivOperationParent,
     _moveX,
     _copyX,
+    _moveY,
     _offsetRecursively,
     _circularMod,
     _indexes,
@@ -353,6 +354,30 @@ describe('Test _copyX', () => {
     });
 });
 
+describe('Test _moveY', () => {
+    let operation: Operation;
+    beforeEach(() => {
+        operation = {
+            gate: 'ZZ',
+            targets: [{ qId: 1 }, { qId: 3 }],
+        };
+    });
+    test('offset by 1', () => {
+        _moveY('1', '2', operation, 4);
+        expect(operation).toStrictEqual({
+            gate: 'ZZ',
+            targets: [{ qId: 2 }, { qId: 0 }],
+        });
+    });
+    test('offset by -3', () => {
+        _moveY('3', '0', operation, 4);
+        expect(operation).toStrictEqual({
+            gate: 'ZZ',
+            targets: [{ qId: 2 }, { qId: 0 }],
+        });
+    });
+});
+
 describe('Test _offsetRecursively', () => {
     let operation: Operation;
     beforeEach(() => {
diff --git a/src/editable.ts b/src/editable.ts
index 18c97c06..47b6e080 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -246,7 +246,7 @@ const _addEvents = (context: Context) => {
                 : _moveX(context.selectedId, targetId, operations);
 
             if (newSourceOperation != null) {
-                _moveY(context.selectedWire, targetWire, newSourceOperation, context.wireData);
+                _moveY(context.selectedWire, targetWire, newSourceOperation, context.wireData.length);
             }
 
             renderFn();
@@ -331,12 +331,13 @@ const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Op
     return newSourceOperation;
 };
 
-const _moveY = (sourceWire: string, targetWire: string, operation: Operation, wireData: number[]) => {
+const _moveY = (sourceWire: string, targetWire: string, operation: Operation, totalWires: number): Operation => {
     const offset = parseInt(targetWire) - parseInt(sourceWire);
-    _offsetRecursively(operation, offset, wireData.length);
+    _offsetRecursively(operation, offset, totalWires);
+    return operation;
 };
 
-const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires: number): void => {
+const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires: number): Operation => {
     // Offset all targets by offsetY value
     if (operation.targets != null) {
         operation.targets.forEach((target) => {
@@ -357,6 +358,8 @@ const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires
     if (operation.children != null) {
         operation.children.forEach((child) => _offsetRecursively(child, wireOffset, totalWires));
     }
+
+    return operation;
 };
 
 /**
@@ -394,6 +397,7 @@ const exportedForTesting = {
     _equivOperationParent,
     _moveX,
     _copyX,
+    _moveY,
     _offsetRecursively,
     _circularMod,
     _indexes,

From ee00dc4eb1074e863aa6672c790e5caf4eef1209 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 18:35:10 -0400
Subject: [PATCH 038/108] Add tests for _equivGateElem

---
 __tests__/__snapshots__/editable.test.ts.snap | 28 +++++++++++++++
 __tests__/editable.test.ts                    | 34 +++++++++++++++++++
 src/editable.ts                               |  1 +
 3 files changed, 63 insertions(+)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 700b6219..5655cf3a 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -102,6 +102,34 @@ Array [
 ]
 `;
 
+exports[`Test _equivGateElem should return gate H 1`] = `
+<g
+  class="gate"
+  data-id="0"
+  data-zoom-in="false"
+  data-zoom-out="false"
+>
+  <g>
+    <g>
+      <rect
+        class="gate-unitary"
+        height="40"
+        width="40"
+        x="80"
+        y="20"
+      />
+      <text
+        font-size="14"
+        x="100"
+        y="40"
+      >
+        H
+      </text>
+    </g>
+  </g>
+</g>
+`;
+
 exports[`Test _equivOperation should return H gate 1`] = `
 Object {
   "gate": "H",
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 40244c02..d56ce7b1 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -7,6 +7,7 @@ import { Circuit, draw, Operation, STYLES } from '../src/index';
 const {
     _center,
     _wireData,
+    _equivGateElem,
     _equivOperation,
     _equivOperationParent,
     _moveX,
@@ -102,6 +103,39 @@ describe('Test _wireData', () => {
     });
 });
 
+describe('Test _equivGateElem', () => {
+    let container: HTMLElement;
+    beforeAll(() => {
+        container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+    });
+    test('should return gate H', () => {
+        const elem = container.querySelector('[class^="gate-"]') as SVGElement;
+        expect(_equivGateElem(elem)).toMatchSnapshot();
+    });
+});
+
 describe('Test _equivOperation', () => {
     const circuit = {
         qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
diff --git a/src/editable.ts b/src/editable.ts
index 47b6e080..2bf074f0 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -393,6 +393,7 @@ const _renderFn = (
 const exportedForTesting = {
     _center,
     _wireData,
+    _equivGateElem,
     _equivOperation,
     _equivOperationParent,
     _moveX,

From f63ecc36ad687d457b7fdecb49e3bbe2f3658554 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 18:40:08 -0400
Subject: [PATCH 039/108] Add tests for _wirePrefixes

---
 __tests__/editable.test.ts | 17 +++++++++++++++++
 src/editable.ts            |  1 +
 2 files changed, 18 insertions(+)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index d56ce7b1..74d36ae8 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -5,6 +5,7 @@ import { exportedForTesting } from '../src/editable';
 import { Circuit, draw, Operation, STYLES } from '../src/index';
 
 const {
+    _wirePrefixes,
     _center,
     _wireData,
     _equivGateElem,
@@ -19,6 +20,22 @@ const {
     _lastIndex,
 } = exportedForTesting;
 
+describe('Test _wirePrefixes', () => {
+    test('2 wires', () => {
+        expect(_wirePrefixes([40, 100])).toStrictEqual([
+            { index: 0, prefixX: 40, wireY: 40 },
+            { index: 1, prefixX: 40, wireY: 100 },
+        ]);
+    });
+    test('3 wires', () => {
+        expect(_wirePrefixes([40, 100, 140])).toStrictEqual([
+            { index: 0, prefixX: 40, wireY: 40 },
+            { index: 1, prefixX: 40, wireY: 100 },
+            { index: 2, prefixX: 40, wireY: 140 },
+        ]);
+    });
+});
+
 describe('Test _center', () => {
     test('should return {25,50}', () => {
         Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
diff --git a/src/editable.ts b/src/editable.ts
index 2bf074f0..52166aad 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -391,6 +391,7 @@ const _renderFn = (
 };
 
 const exportedForTesting = {
+    _wirePrefixes,
     _center,
     _wireData,
     _equivGateElem,

From 100e2aa3e21423fff4bc6b2edbca59cf94a34ee5 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 18:48:25 -0400
Subject: [PATCH 040/108] Add tests for _wireYs

---
 __tests__/editable.test.ts | 17 +++++++++++++++++
 src/editable.ts            | 12 +++++++-----
 2 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 74d36ae8..b0123c40 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -5,6 +5,7 @@ import { exportedForTesting } from '../src/editable';
 import { Circuit, draw, Operation, STYLES } from '../src/index';
 
 const {
+    _wireYs,
     _wirePrefixes,
     _center,
     _wireData,
@@ -20,6 +21,22 @@ const {
     _lastIndex,
 } = exportedForTesting;
 
+describe('Test _wireYs', () => {
+    test('should return [40,100]', () => {
+        Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
+            writable: true,
+            value: () => ({
+                x: 0,
+                y: 20,
+                width: 0,
+                height: 120,
+            }),
+        });
+        const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
+        expect(_wireYs(elem, [40, 100, 140])).toStrictEqual([40, 100]);
+    });
+});
+
 describe('Test _wirePrefixes', () => {
     test('2 wires', () => {
         expect(_wirePrefixes([40, 100])).toStrictEqual([
diff --git a/src/editable.ts b/src/editable.ts
index 52166aad..0f6b224b 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -71,7 +71,7 @@ const _addDataWires = (container: HTMLElement) => {
  *  i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140).
  *       Function returns [40, 100, 140]
  */
-const _elemWireYs = (elem: SVGGraphicsElement, wireData: number[]) => {
+const _wireYs = (elem: SVGGraphicsElement, wireData: number[]): number[] => {
     const { y, height } = elem.getBBox();
     return wireData.filter((wireY) => wireY > y && wireY < y + height);
 };
@@ -82,7 +82,8 @@ const _hostElems = (container: HTMLElement) => {
     );
 };
 
-const _wirePrefixes = (wireData: number[]) => wireData.map((wireY, index) => ({ index, wireY, prefixX: 40 }));
+const _wirePrefixes = (wireData: number[]): { index: number; wireY: number; prefixX: number }[] =>
+    wireData.map((wireY, index) => ({ index, wireY, prefixX: 40 }));
 
 /**
  *  Find center point of element
@@ -129,9 +130,9 @@ const _dropzoneLayer = (context: Context) => {
         } else {
             // Let group gates creating dropzones for each wire
             const { x } = elem.getBBox();
-            const elemWireYs = _elemWireYs(elem, wireData);
+            const wireYs = _wireYs(elem, wireData);
 
-            elemWireYs.map((wireY) => {
+            wireYs.map((wireY) => {
                 const wirePrefix = wirePrefixes.find((item) => item.wireY === wireY);
                 if (wirePrefix) {
                     const prefixX = wirePrefix.prefixX;
@@ -177,7 +178,7 @@ const _wireData = (container: HTMLElement): number[] => {
 /**
  *  Find equivalent gate element of host element
  */
-const _equivGateElem = (elem: SVGElement) => {
+const _equivGateElem = (elem: SVGElement): SVGElement | null => {
     return elem.closest<SVGElement>('[data-id]');
 };
 
@@ -391,6 +392,7 @@ const _renderFn = (
 };
 
 const exportedForTesting = {
+    _wireYs,
     _wirePrefixes,
     _center,
     _wireData,

From 47fc810cbfd93ecefc52dbb109a95fd3eab79abe Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 18:58:36 -0400
Subject: [PATCH 041/108] Add tests for _hostElems

---
 __tests__/__snapshots__/editable.test.ts.snap | 46 +++++++++++++++++++
 __tests__/editable.test.ts                    | 34 ++++++++++++++
 src/editable.ts                               |  9 ++--
 3 files changed, 86 insertions(+), 3 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 5655cf3a..48af65d1 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -229,6 +229,52 @@ Array [
 ]
 `;
 
+exports[`Test _hostElems should return 4 elements 1`] = `
+Array [
+  <rect
+    class="gate-unitary"
+    height="40"
+    width="40"
+    x="80"
+    y="20"
+  />,
+  <circle
+    class="control-dot"
+    cx="160"
+    cy="40"
+    r="5"
+  />,
+  <g
+    class="oplus"
+  >
+    <circle
+      cx="160"
+      cy="100"
+      r="15"
+    />
+    <line
+      x1="160"
+      x2="160"
+      y1="85"
+      y2="115"
+    />
+    <line
+      x1="145"
+      x2="175"
+      y1="100"
+      y2="100"
+    />
+  </g>,
+  <rect
+    class="gate-measure"
+    height="40"
+    width="40"
+    x="200"
+    y="80"
+  />,
+]
+`;
+
 exports[`Test _moveX move elem from index 0 to index 1 1`] = `
 Array [
   Object {
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index b0123c40..0bde57d6 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -6,6 +6,7 @@ import { Circuit, draw, Operation, STYLES } from '../src/index';
 
 const {
     _wireYs,
+    _hostElems,
     _wirePrefixes,
     _center,
     _wireData,
@@ -21,6 +22,39 @@ const {
     _lastIndex,
 } = exportedForTesting;
 
+describe('Test _hostElems', () => {
+    let container: HTMLElement;
+    beforeAll(() => {
+        container = document.createElement('div');
+        const circuit = {
+            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
+            operations: [
+                {
+                    gate: 'H',
+                    targets: [{ qId: 0 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'Measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ type: 1, qId: 1, cId: 0 }],
+                },
+            ],
+        };
+        draw(circuit, container, STYLES['default']);
+    });
+    test('should return 4 elements', () => {
+        expect(_hostElems(container)).toMatchSnapshot();
+        expect(_hostElems(container)).toHaveLength(4);
+    });
+});
+
 describe('Test _wireYs', () => {
     test('should return [40,100]', () => {
         Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
diff --git a/src/editable.ts b/src/editable.ts
index 0f6b224b..fd863fb0 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -76,9 +76,11 @@ const _wireYs = (elem: SVGGraphicsElement, wireData: number[]): number[] => {
     return wireData.filter((wireY) => wireY > y && wireY < y + height);
 };
 
-const _hostElems = (container: HTMLElement) => {
-    return container.querySelectorAll<SVGGraphicsElement>(
-        '[class^="gate-"]:not(.gate-control, .gate-swap), .control-dot, .oplus, .cross',
+const _hostElems = (container: HTMLElement): SVGGraphicsElement[] => {
+    return Array.from(
+        container.querySelectorAll<SVGGraphicsElement>(
+            '[class^="gate-"]:not(.gate-control, .gate-swap), .control-dot, .oplus, .cross',
+        ),
     );
 };
 
@@ -393,6 +395,7 @@ const _renderFn = (
 
 const exportedForTesting = {
     _wireYs,
+    _hostElems,
     _wirePrefixes,
     _center,
     _wireData,

From cba0761f398890bea789219ca67193a4c6a02294 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 20:12:19 -0400
Subject: [PATCH 042/108] Remove unnecessary condition when copying

---
 src/editable.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/editable.ts b/src/editable.ts
index fd863fb0..46fd99b8 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -313,7 +313,6 @@ const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Op
 };
 
 const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
-    if (sourceId === targetId) return _equivOperation(sourceId, operations);
     const sourceOperation = _equivOperation(sourceId, operations);
     const sourceOperationParent = _equivOperationParent(sourceId, operations);
     const targetOperationParent = _equivOperationParent(targetId, operations);

From 9fbe5669f961b3eb8e870b3b9353e048b72e56a3 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 20:17:54 -0400
Subject: [PATCH 043/108] Clone operation when moving

---
 src/editable.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editable.ts b/src/editable.ts
index 46fd99b8..a18df3be 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -301,7 +301,7 @@ const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Op
         return null;
 
     // Insert sourceOperation to target last index
-    const newSourceOperation: Operation = { ...sourceOperation };
+    const newSourceOperation: Operation = JSON.parse(JSON.stringify(sourceOperation));
     targetOperationParent.splice(targetLastIndex, 0, newSourceOperation);
 
     // Delete sourceOperation

From cca0b1ca8b0d637b1975dfd2f984dc81d99972a6 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 20:34:06 -0400
Subject: [PATCH 044/108] Reformat comments

---
 src/editable.ts | 90 ++++++++++++++++++++++++++++++++++++++++---------
 src/styles.ts   |  9 +++++
 2 files changed, 83 insertions(+), 16 deletions(-)

diff --git a/src/editable.ts b/src/editable.ts
index a18df3be..71b95595 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -39,15 +39,17 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
         selectedWire: null,
     };
 
-    // addDropzones(container);
-    // addDocumentEvents(container);
+    _addStyles(container, _wireData(container));
     _addDataWires(container);
     svg.appendChild(_dropzoneLayer(context));
-    // addDropzoneEvents(context);
-    // addMouseEvents(context);
     _addEvents(context);
 };
 
+/**
+ * Add data-wire to all host elements
+ *
+ * @param container         HTML element for rendering visualization into
+ */
 const _addDataWires = (container: HTMLElement) => {
     const elems = _hostElems(container);
     elems.forEach((elem) => {
@@ -67,8 +69,8 @@ const _addDataWires = (container: HTMLElement) => {
 };
 
 /**
- *  Create a list of wires that element is spanning on.
- *  i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140).
+ *  Create a list of wires that element is spanning on
+ *  i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140)
  *       Function returns [40, 100, 140]
  */
 const _wireYs = (elem: SVGGraphicsElement, wireData: number[]): number[] => {
@@ -84,11 +86,20 @@ const _hostElems = (container: HTMLElement): SVGGraphicsElement[] => {
     );
 };
 
+const _addStyles = (container: HTMLElement, wireData: number[]): void => {
+    const elems = _hostElems(container);
+    elems.forEach((elem) => {
+        if (_wireYs(elem, wireData).length < 2) elem.style.cursor = 'grab';
+    });
+};
+
 const _wirePrefixes = (wireData: number[]): { index: number; wireY: number; prefixX: number }[] =>
     wireData.map((wireY, index) => ({ index, wireY, prefixX: 40 }));
 
 /**
- *  Find center point of element
+ * Find center point of element
+ *
+ * @param elem              Host element
  */
 const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
     const { x, y, width, height } = elem.getBBox();
@@ -96,7 +107,9 @@ const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
 };
 
 /**
- *  Create dropzone layer with all dropzones popullated
+ * Create dropzone layer with all dropzones popullated
+ *
+ * @param context           Context object
  */
 const _dropzoneLayer = (context: Context) => {
     const dropzoneLayer = document.createElementNS('http://www.w3.org/2000/svg', 'g');
@@ -178,14 +191,18 @@ const _wireData = (container: HTMLElement): number[] => {
 };
 
 /**
- *  Find equivalent gate element of host element
+ * Find equivalent gate element of host element
+ *
+ * @param elem              Host element
  */
 const _equivGateElem = (elem: SVGElement): SVGElement | null => {
     return elem.closest<SVGElement>('[data-id]');
 };
 
 /**
- *  Find data-id of host element
+ * Find data-id of host element
+ *
+ * @param elem              Host element
  */
 const _equivDataId = (elem: SVGElement) => {
     const gateElem = _equivGateElem(elem);
@@ -193,7 +210,9 @@ const _equivDataId = (elem: SVGElement) => {
 };
 
 /**
- *  Disable contextmenu default behaviors
+ * Disable contextmenu default behaviors
+ *
+ * @param container         HTML element for rendering visualization into
  */
 const _addContextMenuEvents = (container: HTMLElement) => {
     container.addEventListener('contextmenu', (ev: MouseEvent) => {
@@ -202,24 +221,60 @@ const _addContextMenuEvents = (container: HTMLElement) => {
 };
 
 /**
- *  Add events specifically for dropzoneLayer
+ * Add events specifically for dropzoneLayer
+ *
+ * @param container         HTML element for rendering visualization into
+ * @param dropzoneLayer     SVG group element representing dropzone layer
  */
 const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElement) => {
     container.addEventListener('mouseup', () => (dropzoneLayer.style.display = 'none'));
 };
 
+/**
+ * Add events for document
+ *
+ * @param context           Context object
+ */
+const _addDocumentEvents = (context: Context) => {
+    const { container } = context;
+
+    document.addEventListener('keydown', (ev: KeyboardEvent) => {
+        if (ev.ctrlKey && context.selectedId) {
+            container.classList.remove('moving');
+            container.classList.add('copying');
+        }
+    });
+
+    document.addEventListener('keyup', () => {
+        if (context.selectedId) {
+            container.classList.remove('moving');
+        }
+    });
+
+    document.addEventListener('mouseup', () => {
+        container.classList.remove('moving', 'copying');
+    });
+};
+
+/**
+ * Add all events
+ *
+ * @param context           Context object
+ */
 const _addEvents = (context: Context) => {
     const { container, operations, renderFn } = context;
     const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
 
     _addContextMenuEvents(container);
     _addDropzoneLayerEvents(container, dropzoneLayer);
+    _addDocumentEvents(context);
 
     // Host element events
     const elems = _hostElems(container);
     elems.forEach((elem) => {
         elem.addEventListener('mousedown', () => {
             context.selectedWire = elem.getAttribute('data-wire');
+            container.classList.add('moving');
         });
 
         const gateElem = _equivGateElem(elem);
@@ -334,8 +389,10 @@ const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Op
 };
 
 const _moveY = (sourceWire: string, targetWire: string, operation: Operation, totalWires: number): Operation => {
-    const offset = parseInt(targetWire) - parseInt(sourceWire);
-    _offsetRecursively(operation, offset, totalWires);
+    if (operation.gate !== 'measure') {
+        const offset = parseInt(targetWire) - parseInt(sourceWire);
+        _offsetRecursively(operation, offset, totalWires);
+    }
     return operation;
 };
 
@@ -365,8 +422,9 @@ const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires
 };
 
 /**
- *  This modulo function always returns positive value based on total.
- *  i.e: value=0, offset=-1, total=4 returns 3 instead of -1
+ * This modulo function always returns positive value based on total
+ *
+ * i.e: value=0, offset=-1, total=4 returns 3 instead of -1
  */
 const _circularMod = (value: number, offset: number, total: number): number => {
     return (((value + offset) % total) + total) % total;
diff --git a/src/styles.ts b/src/styles.ts
index 481aea8c..06e7d787 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -252,4 +252,13 @@ const _editable = `
         fill: #EC7063;
         fill-opacity: 50%;
     }
+    .grab {
+        cursor: grab;
+    }
+    .moving {
+        cursor: move;
+    }
+    .copying {
+        cursor: copy;
+    }
     `;

From 0347a80f14ffbe04f8da72d4adc6d5d3871d934c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 28 Jun 2022 20:35:32 -0400
Subject: [PATCH 045/108] Reformat comments

---
 src/editable.ts | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/editable.ts b/src/editable.ts
index 71b95595..1168f878 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -49,6 +49,7 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
  * Add data-wire to all host elements
  *
  * @param container         HTML element for rendering visualization into
+ *
  */
 const _addDataWires = (container: HTMLElement) => {
     const elems = _hostElems(container);
@@ -72,6 +73,7 @@ const _addDataWires = (container: HTMLElement) => {
  *  Create a list of wires that element is spanning on
  *  i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140)
  *       Function returns [40, 100, 140]
+ *
  */
 const _wireYs = (elem: SVGGraphicsElement, wireData: number[]): number[] => {
     const { y, height } = elem.getBBox();
@@ -100,6 +102,7 @@ const _wirePrefixes = (wireData: number[]): { index: number; wireY: number; pref
  * Find center point of element
  *
  * @param elem              Host element
+ *
  */
 const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
     const { x, y, width, height } = elem.getBBox();
@@ -110,6 +113,7 @@ const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
  * Create dropzone layer with all dropzones popullated
  *
  * @param context           Context object
+ *
  */
 const _dropzoneLayer = (context: Context) => {
     const dropzoneLayer = document.createElementNS('http://www.w3.org/2000/svg', 'g');
@@ -194,6 +198,7 @@ const _wireData = (container: HTMLElement): number[] => {
  * Find equivalent gate element of host element
  *
  * @param elem              Host element
+ *
  */
 const _equivGateElem = (elem: SVGElement): SVGElement | null => {
     return elem.closest<SVGElement>('[data-id]');
@@ -213,6 +218,7 @@ const _equivDataId = (elem: SVGElement) => {
  * Disable contextmenu default behaviors
  *
  * @param container         HTML element for rendering visualization into
+ *
  */
 const _addContextMenuEvents = (container: HTMLElement) => {
     container.addEventListener('contextmenu', (ev: MouseEvent) => {
@@ -225,6 +231,7 @@ const _addContextMenuEvents = (container: HTMLElement) => {
  *
  * @param container         HTML element for rendering visualization into
  * @param dropzoneLayer     SVG group element representing dropzone layer
+ *
  */
 const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElement) => {
     container.addEventListener('mouseup', () => (dropzoneLayer.style.display = 'none'));
@@ -234,6 +241,7 @@ const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElem
  * Add events for document
  *
  * @param context           Context object
+ *
  */
 const _addDocumentEvents = (context: Context) => {
     const { container } = context;
@@ -260,6 +268,7 @@ const _addDocumentEvents = (context: Context) => {
  * Add all events
  *
  * @param context           Context object
+ *
  */
 const _addEvents = (context: Context) => {
     const { container, operations, renderFn } = context;
@@ -425,6 +434,7 @@ const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires
  * This modulo function always returns positive value based on total
  *
  * i.e: value=0, offset=-1, total=4 returns 3 instead of -1
+ *
  */
 const _circularMod = (value: number, offset: number, total: number): number => {
     return (((value + offset) % total) + total) % total;

From 69dedb215c614995662fafffe1e720c14ed42917 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 29 Jun 2022 12:58:39 -0400
Subject: [PATCH 046/108] Fix keyup behaviors

---
 src/editable.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/editable.ts b/src/editable.ts
index 1168f878..49405309 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -208,6 +208,7 @@ const _equivGateElem = (elem: SVGElement): SVGElement | null => {
  * Find data-id of host element
  *
  * @param elem              Host element
+ *
  */
 const _equivDataId = (elem: SVGElement) => {
     const gateElem = _equivGateElem(elem);
@@ -255,12 +256,15 @@ const _addDocumentEvents = (context: Context) => {
 
     document.addEventListener('keyup', () => {
         if (context.selectedId) {
+            container.classList.remove('copying');
             container.classList.remove('moving');
         }
     });
 
     document.addEventListener('mouseup', () => {
         container.classList.remove('moving', 'copying');
+        context.selectedId = null;
+        context.selectedWire = null;
     });
 };
 

From bc94aff5685773f4a1ba75883d7d0acc087787f3 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 29 Jun 2022 13:10:34 -0400
Subject: [PATCH 047/108] Overhaul comments

---
 src/editable.ts | 82 +++++++++++++++++++++++++++----------------------
 1 file changed, 46 insertions(+), 36 deletions(-)

diff --git a/src/editable.ts b/src/editable.ts
index 49405309..05ac4522 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -22,9 +22,7 @@ interface Context {
  * @param container         HTML element for rendering visualization into.
  * @param sqore             Sqore object
  * @param onCircuitChange   User-provided callback function triggered when circuit is changed
- *
  */
-
 const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (circuit: Circuit) => void): void => {
     const svg = container.querySelector('svg') as SVGElement;
 
@@ -47,9 +45,6 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
 
 /**
  * Add data-wire to all host elements
- *
- * @param container         HTML element for rendering visualization into
- *
  */
 const _addDataWires = (container: HTMLElement) => {
     const elems = _hostElems(container);
@@ -70,16 +65,18 @@ const _addDataWires = (container: HTMLElement) => {
 };
 
 /**
- *  Create a list of wires that element is spanning on
- *  i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140)
- *       Function returns [40, 100, 140]
- *
+ * Create a list of wires that element is spanning on
+ * i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140)
+ *      Function returns [40, 100, 140]
  */
 const _wireYs = (elem: SVGGraphicsElement, wireData: number[]): number[] => {
     const { y, height } = elem.getBBox();
     return wireData.filter((wireY) => wireY > y && wireY < y + height);
 };
 
+/**
+ * Get list of host elements that dropzones can be attached to
+ */
 const _hostElems = (container: HTMLElement): SVGGraphicsElement[] => {
     return Array.from(
         container.querySelectorAll<SVGGraphicsElement>(
@@ -88,6 +85,9 @@ const _hostElems = (container: HTMLElement): SVGGraphicsElement[] => {
     );
 };
 
+/**
+ * Add custom styles specific to this module
+ */
 const _addStyles = (container: HTMLElement, wireData: number[]): void => {
     const elems = _hostElems(container);
     elems.forEach((elem) => {
@@ -95,14 +95,14 @@ const _addStyles = (container: HTMLElement, wireData: number[]): void => {
     });
 };
 
+/**
+ * Generate an array of wire prefixes from wire data
+ */
 const _wirePrefixes = (wireData: number[]): { index: number; wireY: number; prefixX: number }[] =>
     wireData.map((wireY, index) => ({ index, wireY, prefixX: 40 }));
 
 /**
  * Find center point of element
- *
- * @param elem              Host element
- *
  */
 const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
     const { x, y, width, height } = elem.getBBox();
@@ -111,9 +111,6 @@ const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
 
 /**
  * Create dropzone layer with all dropzones popullated
- *
- * @param context           Context object
- *
  */
 const _dropzoneLayer = (context: Context) => {
     const dropzoneLayer = document.createElementNS('http://www.w3.org/2000/svg', 'g');
@@ -180,6 +177,9 @@ const _dropzoneLayer = (context: Context) => {
     return dropzoneLayer;
 };
 
+/**
+ * Generate an array of y values based on circuit wires
+ */
 const _wireData = (container: HTMLElement): number[] => {
     // elems include qubit wires and lines of measure gates
     const elems = container.querySelectorAll<SVGGElement>('svg > g:nth-child(3) > g');
@@ -196,9 +196,6 @@ const _wireData = (container: HTMLElement): number[] => {
 
 /**
  * Find equivalent gate element of host element
- *
- * @param elem              Host element
- *
  */
 const _equivGateElem = (elem: SVGElement): SVGElement | null => {
     return elem.closest<SVGElement>('[data-id]');
@@ -206,9 +203,6 @@ const _equivGateElem = (elem: SVGElement): SVGElement | null => {
 
 /**
  * Find data-id of host element
- *
- * @param elem              Host element
- *
  */
 const _equivDataId = (elem: SVGElement) => {
     const gateElem = _equivGateElem(elem);
@@ -217,9 +211,6 @@ const _equivDataId = (elem: SVGElement) => {
 
 /**
  * Disable contextmenu default behaviors
- *
- * @param container         HTML element for rendering visualization into
- *
  */
 const _addContextMenuEvents = (container: HTMLElement) => {
     container.addEventListener('contextmenu', (ev: MouseEvent) => {
@@ -229,10 +220,6 @@ const _addContextMenuEvents = (container: HTMLElement) => {
 
 /**
  * Add events specifically for dropzoneLayer
- *
- * @param container         HTML element for rendering visualization into
- * @param dropzoneLayer     SVG group element representing dropzone layer
- *
  */
 const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElement) => {
     container.addEventListener('mouseup', () => (dropzoneLayer.style.display = 'none'));
@@ -240,9 +227,6 @@ const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElem
 
 /**
  * Add events for document
- *
- * @param context           Context object
- *
  */
 const _addDocumentEvents = (context: Context) => {
     const { container } = context;
@@ -270,9 +254,6 @@ const _addDocumentEvents = (context: Context) => {
 
 /**
  * Add all events
- *
- * @param context           Context object
- *
  */
 const _addEvents = (context: Context) => {
     const { container, operations, renderFn } = context;
@@ -325,6 +306,9 @@ const _addEvents = (context: Context) => {
     });
 };
 
+/**
+ * Find equivalent parent array of an operation
+ */
 const _equivOperationParent = (dataId: string | null, operations: Operation[]): Operation[] | null => {
     if (!dataId) return null;
 
@@ -338,6 +322,9 @@ const _equivOperationParent = (dataId: string | null, operations: Operation[]):
     return operationParent;
 };
 
+/**
+ * Find an equivalent operation of an element based on its data-id
+ */
 const _equivOperation = (dataId: string | null, operations: Operation[]): Operation | null => {
     if (!dataId) return null;
 
@@ -353,6 +340,9 @@ const _equivOperation = (dataId: string | null, operations: Operation[]): Operat
     return operationParent[index];
 };
 
+/**
+ * Move an operation horizontally
+ */
 const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
     if (sourceId === targetId) return _equivOperation(sourceId, operations);
     const sourceOperation = _equivOperation(sourceId, operations);
@@ -380,6 +370,9 @@ const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Op
     return newSourceOperation;
 };
 
+/**
+ * Copy an operation horizontally
+ */
 const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
     const sourceOperation = _equivOperation(sourceId, operations);
     const sourceOperationParent = _equivOperationParent(sourceId, operations);
@@ -401,6 +394,9 @@ const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Op
     return newSourceOperation;
 };
 
+/**
+ * Move an operation vertically by changing its controls and targets
+ */
 const _moveY = (sourceWire: string, targetWire: string, operation: Operation, totalWires: number): Operation => {
     if (operation.gate !== 'measure') {
         const offset = parseInt(targetWire) - parseInt(sourceWire);
@@ -409,6 +405,9 @@ const _moveY = (sourceWire: string, targetWire: string, operation: Operation, to
     return operation;
 };
 
+/**
+ * Recursively change object controls and targets
+ */
 const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires: number): Operation => {
     // Offset all targets by offsetY value
     if (operation.targets != null) {
@@ -436,23 +435,31 @@ const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires
 
 /**
  * This modulo function always returns positive value based on total
- *
  * i.e: value=0, offset=-1, total=4 returns 3 instead of -1
- *
  */
 const _circularMod = (value: number, offset: number, total: number): number => {
     return (((value + offset) % total) + total) % total;
 };
 
+/**
+ * Split data-id into an array of indexes
+ */
 const _indexes = (dataId: string): number[] =>
     dataId !== '' //
         ? dataId.split('-').map((segment) => parseInt(segment))
         : [];
 
+/**
+ * Get the last index of data-id
+ * i.e: data-id = "0-1-2", _lastIndex will return 2
+ */
 const _lastIndex = (dataId: string): number | undefined => {
     return _indexes(dataId).pop();
 };
 
+/**
+ * Return a render function with the onCircuitChange callback attached to it
+ */
 const _renderFn = (
     container: HTMLElement,
     sqore: Sqore,
@@ -464,6 +471,9 @@ const _renderFn = (
     };
 };
 
+/**
+ * Object exported for unit testing
+ */
 const exportedForTesting = {
     _wireYs,
     _hostElems,

From 2f5e3f8c4973d9f2e4cd4dd8bc74c4297dd12ee6 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 30 Jun 2022 12:43:55 -0400
Subject: [PATCH 048/108] Fix add instead of remove cursor style

---
 src/editable.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editable.ts b/src/editable.ts
index 05ac4522..4c363d69 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -241,7 +241,7 @@ const _addDocumentEvents = (context: Context) => {
     document.addEventListener('keyup', () => {
         if (context.selectedId) {
             container.classList.remove('copying');
-            container.classList.remove('moving');
+            container.classList.add('moving');
         }
     });
 

From 0019b5ec718397eb8f8f1eb13b73186e3071ca8e Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 6 Jul 2022 02:01:03 -0400
Subject: [PATCH 049/108] Rename _equivOperationParent to _equivParentArray

---
 __tests__/__snapshots__/editable.test.ts.snap |  4 ++--
 __tests__/editable.test.ts                    |  8 ++++----
 src/editable.ts                               | 20 +++++++++----------
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 48af65d1..9ea60e4d 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -158,7 +158,7 @@ Object {
 }
 `;
 
-exports[`Test _equivOperationParent should return Foo 1`] = `
+exports[`Test _equivParentArray should return Foo 1`] = `
 Array [
   Object {
     "gate": "H",
@@ -186,7 +186,7 @@ Array [
 ]
 `;
 
-exports[`Test _equivOperationParent should return all operations 1`] = `
+exports[`Test _equivParentArray should return all operations 1`] = `
 Array [
   Object {
     "gate": "H",
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 0bde57d6..96ce98ec 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -12,7 +12,7 @@ const {
     _wireData,
     _equivGateElem,
     _equivOperation,
-    _equivOperationParent,
+    _equivParentArray,
     _moveX,
     _copyX,
     _moveY,
@@ -234,7 +234,7 @@ describe('Test _equivOperation', () => {
     });
 });
 
-describe('Test _equivOperationParent', () => {
+describe('Test _equivParentArray', () => {
     test('should return Foo', () => {
         const circuit = {
             qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
@@ -358,7 +358,7 @@ describe('Test _equivOperationParent', () => {
                 },
             ],
         };
-        expect(_equivOperationParent('0-1', circuit.operations)).toMatchSnapshot();
+        expect(_equivParentArray('0-1', circuit.operations)).toMatchSnapshot();
     });
     test('should return all operations', () => {
         const circuit = {
@@ -382,7 +382,7 @@ describe('Test _equivOperationParent', () => {
                 },
             ],
         };
-        expect(_equivOperationParent('0', circuit.operations)).toMatchSnapshot();
+        expect(_equivParentArray('0', circuit.operations)).toMatchSnapshot();
     });
 });
 
diff --git a/src/editable.ts b/src/editable.ts
index 4c363d69..3293cab3 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -309,17 +309,17 @@ const _addEvents = (context: Context) => {
 /**
  * Find equivalent parent array of an operation
  */
-const _equivOperationParent = (dataId: string | null, operations: Operation[]): Operation[] | null => {
+const _equivParentArray = (dataId: string | null, operations: Operation[]): Operation[] | null => {
     if (!dataId) return null;
 
     const indexes = _indexes(dataId);
     indexes.pop();
 
-    let operationParent = operations;
+    let parentArray = operations;
     for (const index of indexes) {
-        operationParent = operationParent[index].children || operationParent;
+        parentArray = parentArray[index].children || parentArray;
     }
-    return operationParent;
+    return parentArray;
 };
 
 /**
@@ -329,7 +329,7 @@ const _equivOperation = (dataId: string | null, operations: Operation[]): Operat
     if (!dataId) return null;
 
     const index = _lastIndex(dataId);
-    const operationParent = _equivOperationParent(dataId, operations);
+    const operationParent = _equivParentArray(dataId, operations);
 
     if (
         operationParent == null || //
@@ -346,8 +346,8 @@ const _equivOperation = (dataId: string | null, operations: Operation[]): Operat
 const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
     if (sourceId === targetId) return _equivOperation(sourceId, operations);
     const sourceOperation = _equivOperation(sourceId, operations);
-    const sourceOperationParent = _equivOperationParent(sourceId, operations);
-    const targetOperationParent = _equivOperationParent(targetId, operations);
+    const sourceOperationParent = _equivParentArray(sourceId, operations);
+    const targetOperationParent = _equivParentArray(targetId, operations);
     const targetLastIndex = _lastIndex(targetId);
 
     if (
@@ -375,8 +375,8 @@ const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Op
  */
 const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
     const sourceOperation = _equivOperation(sourceId, operations);
-    const sourceOperationParent = _equivOperationParent(sourceId, operations);
-    const targetOperationParent = _equivOperationParent(targetId, operations);
+    const sourceOperationParent = _equivParentArray(sourceId, operations);
+    const targetOperationParent = _equivParentArray(targetId, operations);
     const targetLastIndex = _lastIndex(targetId);
 
     if (
@@ -482,7 +482,7 @@ const exportedForTesting = {
     _wireData,
     _equivGateElem,
     _equivOperation,
-    _equivOperationParent,
+    _equivParentArray,
     _moveX,
     _copyX,
     _moveY,

From eb1ea55e21d8f6d96432cceff95c60e58710b129 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 6 Jul 2022 03:34:04 -0400
Subject: [PATCH 050/108] Fix parent operation not updating to its children
 targets

---
 __tests__/__snapshots__/editable.test.ts.snap |  40 +++
 __tests__/editable.test.ts                    | 276 ++++++++++++++++++
 src/editable.ts                               |  64 ++++
 3 files changed, 380 insertions(+)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
index 9ea60e4d..c29c1bad 100644
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ b/__tests__/__snapshots__/editable.test.ts.snap
@@ -229,6 +229,46 @@ Array [
 ]
 `;
 
+exports[`Test _equivParentOperation should return Foo 1`] = `
+Object {
+  "children": Array [
+    Object {
+      "gate": "H",
+      "targets": Array [
+        Object {
+          "qId": 1,
+        },
+      ],
+    },
+    Object {
+      "controls": Array [
+        Object {
+          "qId": 1,
+        },
+      ],
+      "displayArgs": "(0.25)",
+      "gate": "RX",
+      "isControlled": true,
+      "targets": Array [
+        Object {
+          "qId": 0,
+        },
+      ],
+    },
+  ],
+  "conditionalRender": 3,
+  "gate": "Foo",
+  "targets": Array [
+    Object {
+      "qId": 0,
+    },
+    Object {
+      "qId": 1,
+    },
+  ],
+}
+`;
+
 exports[`Test _hostElems should return 4 elements 1`] = `
 Array [
   <rect
diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
index 96ce98ec..0fde4705 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/editable.test.ts
@@ -12,11 +12,13 @@ const {
     _wireData,
     _equivGateElem,
     _equivOperation,
+    _equivParentOperation,
     _equivParentArray,
     _moveX,
     _copyX,
     _moveY,
     _offsetRecursively,
+    _targets,
     _circularMod,
     _indexes,
     _lastIndex,
@@ -234,6 +236,134 @@ describe('Test _equivOperation', () => {
     });
 });
 
+describe('Test _equivParentOperation', () => {
+    test('should return Foo', () => {
+        const circuit = {
+            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
+            operations: [
+                {
+                    gate: 'Foo',
+                    conditionalRender: 3,
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                        },
+                        {
+                            gate: 'RX',
+                            displayArgs: '(0.25)',
+                            isControlled: true,
+                            controls: [{ qId: 1 }],
+                            targets: [{ qId: 0 }],
+                        },
+                    ],
+                },
+                {
+                    gate: 'X',
+                    targets: [{ qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ qId: 2 }, { qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }, { qId: 3 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }, { qId: 3 }],
+                    targets: [{ qId: 2 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ type: 1, qId: 0, cId: 0 }],
+                },
+                {
+                    gate: 'ApplyIfElseR',
+                    isConditional: true,
+                    controls: [{ type: 1, qId: 0, cId: 0 }],
+                    targets: [],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            isControlled: true,
+                            controls: [{ qId: 0 }],
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 2,
+                        },
+                        {
+                            gate: 'Foo',
+                            targets: [{ qId: 3 }],
+                            conditionalRender: 2,
+                        },
+                    ],
+                },
+                {
+                    gate: 'SWAP',
+                    targets: [{ qId: 0 }, { qId: 2 }],
+                    children: [
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                    ],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }, { qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+            ],
+        };
+        expect(_equivParentOperation('0-1', circuit.operations)).toMatchSnapshot();
+    });
+});
+
 describe('Test _equivParentArray', () => {
     test('should return Foo', () => {
         const circuit = {
@@ -504,6 +634,152 @@ describe('Test _offsetRecursively', () => {
     });
 });
 
+describe('Test _targets', () => {
+    let circuit: Circuit;
+    beforeEach(() => {
+        circuit = {
+            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
+            operations: [
+                {
+                    gate: 'Foo',
+                    conditionalRender: 3,
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                        },
+                        {
+                            gate: 'RX',
+                            displayArgs: '(0.25)',
+                            isControlled: true,
+                            controls: [{ qId: 1 }],
+                            targets: [{ qId: 0 }],
+                        },
+                    ],
+                },
+                {
+                    gate: 'X',
+                    targets: [{ qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }],
+                    targets: [{ qId: 2 }, { qId: 3 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }, { qId: 3 }],
+                    targets: [{ qId: 1 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 1 }, { qId: 3 }],
+                    targets: [{ qId: 2 }],
+                },
+                {
+                    gate: 'X',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'measure',
+                    isMeasurement: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ type: 1, qId: 0, cId: 0 }],
+                },
+                {
+                    gate: 'ApplyIfElseR',
+                    isConditional: true,
+                    controls: [{ type: 1, qId: 0, cId: 0 }],
+                    targets: [],
+                    children: [
+                        {
+                            gate: 'H',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 1,
+                        },
+                        {
+                            gate: 'X',
+                            isControlled: true,
+                            controls: [{ qId: 0 }],
+                            targets: [{ qId: 1 }],
+                            conditionalRender: 2,
+                        },
+                        {
+                            gate: 'Foo',
+                            targets: [{ qId: 3 }],
+                            conditionalRender: 2,
+                        },
+                    ],
+                },
+                {
+                    gate: 'SWAP',
+                    targets: [{ qId: 0 }, { qId: 2 }],
+                    children: [
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
+                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
+                    ],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'ZZ',
+                    targets: [{ qId: 0 }, { qId: 1 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+                {
+                    gate: 'XX',
+                    isControlled: true,
+                    controls: [{ qId: 0 }, { qId: 2 }],
+                    targets: [{ qId: 1 }, { qId: 3 }],
+                },
+            ],
+        };
+    });
+    test('move RX down 1, should return [{qId:1}, {qId:2}]', () => {
+        const parentOperation = circuit.operations[0];
+        const parentArray = parentOperation.children;
+        if (parentArray) {
+            const operation = parentArray[1];
+            _moveY('0', '1', operation, 4);
+        }
+        expect(_targets(parentOperation)).toStrictEqual([{ qId: 1 }, { qId: 2 }]);
+    });
+    test('move RX down 2, should return [{qId:1}, {qId:2}, {qId:3}]', () => {
+        const parentOperation = circuit.operations[0];
+        const parentArray = parentOperation.children;
+        if (parentArray) {
+            const operation = parentArray[1];
+            _moveY('0', '2', operation, 4);
+        }
+        expect(_targets(parentOperation)).toStrictEqual([{ qId: 1 }, { qId: 2 }, { qId: 3 }]);
+    });
+});
+
 describe('Test _circularMod', () => {
     test('should return 2', () => {
         expect(_circularMod(5, 1, 4)).toEqual(2);
diff --git a/src/editable.ts b/src/editable.ts
index 3293cab3..d556217c 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -3,6 +3,7 @@
 
 import { Circuit, Operation } from './circuit';
 import { box } from './formatters/formatUtils';
+import { Register } from './register';
 import { Sqore } from './sqore';
 
 interface Context {
@@ -299,6 +300,10 @@ const _addEvents = (context: Context) => {
 
             if (newSourceOperation != null) {
                 _moveY(context.selectedWire, targetWire, newSourceOperation, context.wireData.length);
+                const parentOperation = _equivParentOperation(context.selectedId, operations);
+                if (parentOperation) {
+                    parentOperation.targets = _targets(parentOperation);
+                }
             }
 
             renderFn();
@@ -306,6 +311,22 @@ const _addEvents = (context: Context) => {
     });
 };
 
+const _equivParentOperation = (dataId: string | null, operations: Operation[]): Operation | null => {
+    if (!dataId) return null;
+
+    const indexes = _indexes(dataId);
+    indexes.pop();
+    const lastIndex = indexes.pop();
+
+    if (lastIndex == null) return null;
+
+    let parentOperation = operations;
+    for (const index of indexes) {
+        parentOperation = parentOperation[index].children || parentOperation;
+    }
+    return parentOperation[lastIndex];
+};
+
 /**
  * Find equivalent parent array of an operation
  */
@@ -433,6 +454,47 @@ const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires
     return operation;
 };
 
+/**
+ * Find targets of an operation by recursively walkthrough all of its children controls and targets
+ * i.e. Gate Foo contains gate H and gate RX.
+ *      qIds of Gate H is 1
+ *      qIds of Gate RX is 1, 2
+ *      This should return [{qId: 1}, {qId: 2}]
+ */
+const _targets = (operation: Operation): Register[] | [] => {
+    const _recurse = (operation: Operation) => {
+        registers.push(...operation.targets);
+        if (operation.controls) {
+            registers.push(...operation.controls);
+            // If there is more children, keep adding more to registers
+            if (operation.children) {
+                for (const child of operation.children) {
+                    _recurse(child);
+                }
+            }
+        }
+    };
+
+    const registers: Register[] = [];
+    if (operation.children == null) return [];
+
+    // Recursively walkthrough all children to populate registers
+    for (const child of operation.children) {
+        _recurse(child);
+    }
+
+    // Extract qIds from array of object
+    // i.e. [{qId: 0}, {qId: 1}, {qId: 1}] -> [0, 1, 1]
+    const qIds = registers.map((register) => register.qId);
+    const uniqueQIds = Array.from(new Set(qIds));
+
+    // Transform array of numbers into array of qId object
+    // i.e.  -> [0, 1] -> [{qId: 0}, {qId: 1}, {qId: 1}]
+    return uniqueQIds.map((qId) => ({
+        qId,
+    }));
+};
+
 /**
  * This modulo function always returns positive value based on total
  * i.e: value=0, offset=-1, total=4 returns 3 instead of -1
@@ -482,11 +544,13 @@ const exportedForTesting = {
     _wireData,
     _equivGateElem,
     _equivOperation,
+    _equivParentOperation,
     _equivParentArray,
     _moveX,
     _copyX,
     _moveY,
     _offsetRecursively,
+    _targets,
     _circularMod,
     _indexes,
     _lastIndex,

From 2fe5929eaf16668905efbfaa8f83c74edaedd211 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 7 Jul 2022 11:34:01 -0400
Subject: [PATCH 051/108] Fix comment

---
 src/editable.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/editable.ts b/src/editable.ts
index d556217c..4c16c9cf 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -489,7 +489,7 @@ const _targets = (operation: Operation): Register[] | [] => {
     const uniqueQIds = Array.from(new Set(qIds));
 
     // Transform array of numbers into array of qId object
-    // i.e.  -> [0, 1] -> [{qId: 0}, {qId: 1}, {qId: 1}]
+    // i.e. [0, 1] -> [{qId: 0}, {qId: 1}]
     return uniqueQIds.map((qId) => ({
         qId,
     }));

From 352e74e9701ad2cf7998fff45bd7309718a837cf Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 7 Jul 2022 11:54:52 -0400
Subject: [PATCH 052/108] First commit new feature

---
 src/editable.ts |   6 +--
 src/panel.ts    | 124 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 127 insertions(+), 3 deletions(-)
 create mode 100755 src/panel.ts

diff --git a/src/editable.ts b/src/editable.ts
index 4c16c9cf..8625f3b1 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -3,6 +3,7 @@
 
 import { Circuit, Operation } from './circuit';
 import { box } from './formatters/formatUtils';
+import { addPanel } from './panel';
 import { Register } from './register';
 import { Sqore } from './sqore';
 
@@ -38,6 +39,7 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
         selectedWire: null,
     };
 
+    addPanel(context);
     _addStyles(container, _wireData(container));
     _addDataWires(container);
     svg.appendChild(_dropzoneLayer(context));
@@ -248,8 +250,6 @@ const _addDocumentEvents = (context: Context) => {
 
     document.addEventListener('mouseup', () => {
         container.classList.remove('moving', 'copying');
-        context.selectedId = null;
-        context.selectedWire = null;
     });
 };
 
@@ -556,4 +556,4 @@ const exportedForTesting = {
     _lastIndex,
 };
 
-export { addEditable, exportedForTesting };
+export { addEditable, Context, _equivOperation, exportedForTesting };
diff --git a/src/panel.ts b/src/panel.ts
new file mode 100755
index 00000000..45405920
--- /dev/null
+++ b/src/panel.ts
@@ -0,0 +1,124 @@
+import { Operation } from './circuit';
+import { Context, _equivOperation } from './editable';
+
+const addPanel = (context: Context): void => {
+    const { container } = context;
+
+    container.prepend(_panel(operation));
+};
+
+const _panel = (operation: Operation | null) => {
+    console.log(operation);
+    const options: Option[] = [
+        {
+            value: '0',
+            text: 'q0',
+        },
+        {
+            value: '1',
+            text: 'q1',
+        },
+        {
+            value: '2',
+            text: 'q2',
+        },
+        {
+            value: '3',
+            text: 'q3',
+        },
+    ];
+
+    const panelElem = _elem('div');
+    _children(panelElem, [
+        _select('Target', 'target-input', options, 0),
+        _checkboxes('Controls', 'controls-input', options, [2, 3]),
+        _text('Display', 'display-input', 'display-arg'),
+    ]);
+
+    return panelElem;
+};
+
+const _elem = (tag: string): HTMLElement => document.createElement(tag);
+
+/**
+ * Append all child elements to a parent element
+ */
+const _children = (parentElem: HTMLElement, childElems: HTMLElement[]) => {
+    childElems.map((elem) => parentElem.appendChild(elem));
+    return parentElem;
+};
+
+interface Option {
+    value: string;
+    text: string;
+}
+
+const _select = (label: string, className: string, options: Option[], selectedIndex: number) => {
+    const optionElems = options.map(({ value, text }) => _option(value, text));
+    const selectElem = _elem('select') as HTMLSelectElement;
+    _children(selectElem, optionElems);
+    selectElem.selectedIndex = selectedIndex;
+
+    const labelElem = _elem('label') as HTMLLabelElement;
+    labelElem.textContent = label;
+
+    const divElem = _elem('div') as HTMLDivElement;
+    divElem.className = className;
+    _children(divElem, [labelElem, selectElem]);
+
+    return divElem;
+};
+
+const _option = (value: string, text: string) => {
+    const elem = _elem('option') as HTMLOptionElement;
+    elem.value = value;
+    elem.textContent = text;
+    return elem;
+};
+
+const _checkboxes = (label: string, className: string, options: Option[], selectedIndexes: number[]) => {
+    const checkboxElems = options.map((option, index) => {
+        const elem = _checkbox(option.value, option.text);
+        if (selectedIndexes.includes(index)) {
+            elem.querySelector('input')?.setAttribute('checked', 'true');
+        }
+        return elem;
+    });
+
+    const labelElem = _elem('label');
+    labelElem.textContent = label;
+
+    const divElem = _elem('div') as HTMLDivElement;
+    divElem.className = className;
+    _children(divElem, [labelElem, ...checkboxElems]);
+
+    return divElem;
+};
+
+const _checkbox = (value: string, text: string) => {
+    const inputElem = _elem('input') as HTMLInputElement;
+    inputElem.type = 'checkbox';
+    inputElem.value = value;
+
+    const labelElem = _elem('label') as HTMLLabelElement;
+    labelElem.textContent = text;
+    labelElem.prepend(inputElem);
+    return labelElem;
+};
+
+const _text = (label: string, className: string, value: string) => {
+    const labelElem = _elem('label') as HTMLLabelElement;
+    labelElem.textContent = label;
+
+    const textElem = _elem('input') as HTMLInputElement;
+    textElem.type = 'text';
+    textElem.value = value;
+
+    const divElem = _elem('div');
+    divElem.className = className;
+    _children(divElem, [labelElem, textElem]);
+
+    return divElem;
+};
+
+export { addPanel };

From 22ff44b2add344226d2a492872c356a8660c0ee3 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 7 Jul 2022 12:48:20 -0400
Subject: [PATCH 053/108] Remove Context dependency

---
 src/editable.ts |  2 +-
 src/panel.ts    | 12 +++---------
 2 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/src/editable.ts b/src/editable.ts
index 8625f3b1..c89aa530 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -39,7 +39,7 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
         selectedWire: null,
     };
 
-    addPanel(context);
+    addPanel(container);
     _addStyles(container, _wireData(container));
     _addDataWires(container);
     svg.appendChild(_dropzoneLayer(context));
diff --git a/src/panel.ts b/src/panel.ts
index 45405920..7695f023 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,14 +1,8 @@
-import { Operation } from './circuit';
-import { Context, _equivOperation } from './editable';
-
-const addPanel = (context: Context): void => {
-    const { container } = context;
-
-    container.prepend(_panel(operation));
+const addPanel = (container: HTMLElement): void => {
+    container.prepend(_panel());
 };
 
-const _panel = (operation: Operation | null) => {
-    console.log(operation);
+const _panel = () => {
     const options: Option[] = [
         {
             value: '0',

From 16639dcf72bbfad7792c07593b5f4059d2ef5a2d Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 7 Jul 2022 18:59:44 -0400
Subject: [PATCH 054/108] Display data when gate is clicked

---
 src/editable.ts |  4 ++--
 src/panel.ts    | 11 ++++++++++-
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/editable.ts b/src/editable.ts
index c89aa530..e001ba16 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -39,7 +39,7 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
         selectedWire: null,
     };
 
-    addPanel(container);
+    addPanel(container, context.operations);
     _addStyles(container, _wireData(container));
     _addDataWires(container);
     svg.appendChild(_dropzoneLayer(context));
@@ -301,7 +301,7 @@ const _addEvents = (context: Context) => {
             if (newSourceOperation != null) {
                 _moveY(context.selectedWire, targetWire, newSourceOperation, context.wireData.length);
                 const parentOperation = _equivParentOperation(context.selectedId, operations);
-                if (parentOperation) {
+                if (parentOperation != null) {
                     parentOperation.targets = _targets(parentOperation);
                 }
             }
diff --git a/src/panel.ts b/src/panel.ts
index 7695f023..d69ad201 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,5 +1,14 @@
-const addPanel = (container: HTMLElement): void => {
+import { Operation } from './circuit';
+import { _equivOperation } from './editable';
+
+const addPanel = (container: HTMLElement, operations: Operation[]): void => {
     container.prepend(_panel());
+    container.querySelectorAll<SVGElement>('[data-id]').forEach((elem) =>
+        elem.addEventListener('mousedown', () => {
+            const dataId = elem.getAttribute('data-id');
+            console.log({ dataId, operation: _equivOperation(dataId, operations) });
+        }),
+    );
 };
 
 const _panel = () => {

From d45089b1d404412c844052506411336a1136dc61 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <37777232+aaronthangnguyen@users.noreply.github.com>
Date: Thu, 7 Jul 2022 19:04:28 -0400
Subject: [PATCH 055/108] Sync to `main` (#73)

* v 1.0.5

* Fixing jupyter viewer display (#70)

Co-authored-by: Andres Paz <anpaz@microsoft.com>
---
 package.json                      | 2 +-
 quantum-viz/pyproject.toml        | 2 +-
 quantum-viz/quantum_viz/widget.py | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/package.json b/package.json
index d9dbd88a..c138e1ac 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@microsoft/quantum-viz.js",
-  "version": "1.0.4",
+  "version": "1.0.5",
   "description": "quantum-viz.js is a configurable tool for visualizing quantum circuits.",
   "main": "dist/qviz.min.js",
   "scripts": {
diff --git a/quantum-viz/pyproject.toml b/quantum-viz/pyproject.toml
index 2914cf61..960aff8e 100644
--- a/quantum-viz/pyproject.toml
+++ b/quantum-viz/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "quantum-viz"
-version = "1.0.4"
+version = "1.0.5"
 description = "quantum-viz.js Python tools"
 authors = ["Microsoft Corporation <que-contacts@microsoft.com>"]
 license = "MIT"
diff --git a/quantum-viz/quantum_viz/widget.py b/quantum-viz/quantum_viz/widget.py
index 829a2a4d..3a33bba8 100644
--- a/quantum-viz/quantum_viz/widget.py
+++ b/quantum-viz/quantum_viz/widget.py
@@ -134,7 +134,7 @@ def html_str(self, uid: Optional[str] = None) -> str:
     def _ipython_display_(self) -> None:
         """Display the widget with IPython."""
         viewer = HTML(self.html_str())
-        display((viewer,))
+        display(viewer)
 
     def browser_display(self) -> None:
         """Display the widget in the browser."""

From 48c21447ce3217852fde82b6fb18084ed874ee72 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 11 Jul 2022 15:36:24 -0400
Subject: [PATCH 056/108] Allow target edit

---
 package.json    |  2 ++
 src/editable.ts |  9 ++++----
 src/index.ts    |  2 ++
 src/panel.ts    | 59 +++++++++++++++++++++++++++----------------------
 tsconfig.json   |  1 +
 5 files changed, 42 insertions(+), 31 deletions(-)

diff --git a/package.json b/package.json
index d9dbd88a..d103e7d4 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
   "license": "MIT",
   "devDependencies": {
     "@types/jest": "^26.0.4",
+    "@types/lodash": "^4.14.182",
     "@types/prettier": "2.6.0",
     "@typescript-eslint/eslint-plugin": "^3.9.0",
     "@typescript-eslint/parser": "^3.9.0",
@@ -41,6 +42,7 @@
     "eslint-plugin-jest": "^23.20.0",
     "eslint-plugin-prettier": "^3.1.4",
     "jest": "^26.6.3",
+    "lodash": "^4.17.21",
     "prettier": "2.6.0",
     "terser-webpack-plugin": "^4.1.0",
     "ts-jest": "^26.1.2",
diff --git a/src/editable.ts b/src/editable.ts
index e001ba16..f6e54776 100644
--- a/src/editable.ts
+++ b/src/editable.ts
@@ -1,9 +1,10 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
+import cloneDeep from 'lodash/cloneDeep';
+import isEqual from 'lodash/isEqual';
 import { Circuit, Operation } from './circuit';
 import { box } from './formatters/formatUtils';
-import { addPanel } from './panel';
 import { Register } from './register';
 import { Sqore } from './sqore';
 
@@ -38,8 +39,6 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
         selectedId: null,
         selectedWire: null,
     };
-
-    addPanel(container, context.operations);
     _addStyles(container, _wireData(container));
     _addDataWires(container);
     svg.appendChild(_dropzoneLayer(context));
@@ -284,6 +283,7 @@ const _addEvents = (context: Context) => {
     const dropzoneElems = dropzoneLayer.querySelectorAll<SVGRectElement>('.dropzone');
     dropzoneElems.forEach((dropzoneElem) => {
         dropzoneElem.addEventListener('mouseup', (ev: MouseEvent) => {
+            const originalOperations = cloneDeep(operations);
             const targetId = dropzoneElem.getAttribute('data-dropzone-id');
             const targetWire = dropzoneElem.getAttribute('data-dropzone-wire');
             if (
@@ -305,8 +305,7 @@ const _addEvents = (context: Context) => {
                     parentOperation.targets = _targets(parentOperation);
                 }
             }
-
-            renderFn();
+            if (isEqual(originalOperations, operations) === false) renderFn();
         });
     });
 };
diff --git a/src/index.ts b/src/index.ts
index 56b35003..fdc0c9a2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,6 +4,7 @@
 import { Sqore } from './sqore';
 import { Circuit } from './circuit';
 import { StyleConfig } from './styles';
+import { addPanel } from './panel';
 
 /**
  * Render `circuit` into `container` at the specified layer depth.
@@ -26,6 +27,7 @@ export const draw = (
     const sqore = new Sqore(circuit, style);
 
     sqore.draw(container, renderDepth, isEditable, onCircuitChange);
+    addPanel(container, sqore);
 };
 
 export { STYLES } from './styles';
diff --git a/src/panel.ts b/src/panel.ts
index d69ad201..754759af 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,40 +1,34 @@
+import range from 'lodash/range';
 import { Operation } from './circuit';
 import { _equivOperation } from './editable';
+import { Sqore } from './sqore';
 
-const addPanel = (container: HTMLElement, operations: Operation[]): void => {
-    container.prepend(_panel());
-    container.querySelectorAll<SVGElement>('[data-id]').forEach((elem) =>
+const addPanel = (container: HTMLElement, sqore: Sqore): void => {
+    const elems = container.querySelectorAll<SVGElement>('[data-id]');
+    elems.forEach((elem) =>
         elem.addEventListener('mousedown', () => {
             const dataId = elem.getAttribute('data-id');
-            console.log({ dataId, operation: _equivOperation(dataId, operations) });
+            const operation = _equivOperation(dataId, sqore.circuit.operations);
+            const newPanelElem = _panel(qubitSize, operation || undefined);
+            container.replaceChild(newPanelElem, panelElem);
+            panelElem = newPanelElem;
         }),
     );
+    const qubitSize = sqore.circuit.qubits.length;
+    let panelElem = _panel(qubitSize);
+    container.prepend(panelElem);
 };
 
-const _panel = () => {
-    const options: Option[] = [
-        {
-            value: '0',
-            text: 'q0',
-        },
-        {
-            value: '1',
-            text: 'q1',
-        },
-        {
-            value: '2',
-            text: 'q2',
-        },
-        {
-            value: '3',
-            text: 'q3',
-        },
-    ];
+const _panel = (qubitSize: number, operation?: Operation) => {
+    const options = range(qubitSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
+    const target = operation?.targets[0].qId;
+    const controls = operation?.controls?.map((control) => control.qId);
 
     const panelElem = _elem('div');
+    panelElem.className = 'panel';
     _children(panelElem, [
-        _select('Target', 'target-input', options, 0),
-        _checkboxes('Controls', 'controls-input', options, [2, 3]),
+        _select('Target', 'target-input', options, target || 0, operation),
+        _checkboxes('Controls', 'controls-input', options, controls || []),
         _text('Display', 'display-input', 'display-arg'),
     ]);
 
@@ -56,7 +50,13 @@ interface Option {
     text: string;
 }
 
-const _select = (label: string, className: string, options: Option[], selectedIndex: number) => {
+const _select = (
+    label: string,
+    className: string,
+    options: Option[],
+    selectedIndex: number,
+    operation?: Operation,
+): HTMLElement => {
     const optionElems = options.map(({ value, text }) => _option(value, text));
     const selectElem = _elem('select') as HTMLSelectElement;
     _children(selectElem, optionElems);
@@ -69,6 +69,13 @@ const _select = (label: string, className: string, options: Option[], selectedIn
     divElem.className = className;
     _children(divElem, [labelElem, selectElem]);
 
+    selectElem.onchange = () => {
+        if (operation != null) {
+            Object.assign(operation.targets, [{ qId: selectElem.value }]);
+            console.log(operation);
+        }
+    };
+
     return divElem;
 };
 
diff --git a/tsconfig.json b/tsconfig.json
index afc965bf..c3f66ab0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -9,6 +9,7 @@
         "declaration": true,
         "strict": true,
         "outDir": "lib",
+        "esModuleInterop": true
     },
     "include": [
         "src/**/*"

From 123c86944eafea9e361c12d3aa28b4b77578b90c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 11 Jul 2022 15:49:16 -0400
Subject: [PATCH 057/108] Change render mechanism

---
 src/sqore.ts | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/sqore.ts b/src/sqore.ts
index 17934f74..b88db601 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -125,13 +125,13 @@ export class Sqore {
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);
-        container.innerHTML = '';
-        container.appendChild(svg);
+        const previousSvg = container.querySelector('svg');
+        previousSvg == null //
+            ? container.appendChild(svg)
+            : container.replaceChild(svg, previousSvg);
         this.addGateClickHandlers(container, circuit, isEditable, onCircuitChange);
 
-        if (isEditable) {
-            addEditable(container, this, onCircuitChange);
-        }
+        isEditable && addEditable(container, this, onCircuitChange);
     }
 
     /**

From 863b7d27160e9338c4ec4e424ec1fb3aa41b9bc2 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 11 Jul 2022 16:27:38 -0400
Subject: [PATCH 058/108] use Reducer design pattern

---
 src/panel.ts | 51 ++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 42 insertions(+), 9 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 754759af..7b20d16d 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -19,6 +19,26 @@ const addPanel = (container: HTMLElement, sqore: Sqore): void => {
     container.prepend(panelElem);
 };
 
+interface Action {
+    type: string;
+    payload: unknown;
+}
+
+const reducer = (initial: Operation | undefined, action: Action) => {
+    if (initial == null) return;
+
+    switch (action.type) {
+        case 'TARGET': {
+            Object.assign(initial, { ...initial, targets: action.payload });
+        }
+        case 'CONTROLS': {
+            Object.assign(initial, { ...initial, controls: action.payload });
+        }
+    }
+    console.log(initial);
+    console.log('Re-rendering...');
+};
+
 const _panel = (qubitSize: number, operation?: Operation) => {
     const options = range(qubitSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
     const target = operation?.targets[0].qId;
@@ -28,7 +48,7 @@ const _panel = (qubitSize: number, operation?: Operation) => {
     panelElem.className = 'panel';
     _children(panelElem, [
         _select('Target', 'target-input', options, target || 0, operation),
-        _checkboxes('Controls', 'controls-input', options, controls || []),
+        _checkboxes('Controls', 'controls-input', options, controls || [], operation),
         _text('Display', 'display-input', 'display-arg'),
     ]);
 
@@ -60,6 +80,7 @@ const _select = (
     const optionElems = options.map(({ value, text }) => _option(value, text));
     const selectElem = _elem('select') as HTMLSelectElement;
     _children(selectElem, optionElems);
+    operation == null && selectElem.setAttribute('disabled', 'true');
     selectElem.selectedIndex = selectedIndex;
 
     const labelElem = _elem('label') as HTMLLabelElement;
@@ -70,10 +91,7 @@ const _select = (
     _children(divElem, [labelElem, selectElem]);
 
     selectElem.onchange = () => {
-        if (operation != null) {
-            Object.assign(operation.targets, [{ qId: selectElem.value }]);
-            console.log(operation);
-        }
+        reducer(operation, { type: 'TARGET', payload: [{ qId: selectElem.value }] });
     };
 
     return divElem;
@@ -86,12 +104,27 @@ const _option = (value: string, text: string) => {
     return elem;
 };
 
-const _checkboxes = (label: string, className: string, options: Option[], selectedIndexes: number[]) => {
+const _checkboxes = (
+    label: string,
+    className: string,
+    options: Option[],
+    selectedIndexes: number[],
+    operation?: Operation,
+) => {
     const checkboxElems = options.map((option, index) => {
         const elem = _checkbox(option.value, option.text);
-        if (selectedIndexes.includes(index)) {
-            elem.querySelector('input')?.setAttribute('checked', 'true');
-        }
+        const inputElem = elem.querySelector('input') as HTMLInputElement;
+        selectedIndexes.includes(index) && inputElem.setAttribute('checked', 'true');
+        operation == null && inputElem.setAttribute('disabled', 'true');
+
+        inputElem.onchange = () => {
+            const checkedElems = Array.from(divElem.querySelectorAll<HTMLInputElement>('input:checked'));
+            const newControls = checkedElems.map((elem) => ({
+                qId: elem.value,
+            }));
+            reducer(operation, { type: 'CONTROLS', payload: newControls });
+        };
+
         return elem;
     });
 

From 90b29bc62cac43196cafdb94be0b04ad701d1ac3 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 11 Jul 2022 19:13:43 -0400
Subject: [PATCH 059/108] Add basic rendering

---
 src/panel.ts | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 7b20d16d..7ffad9bd 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -9,13 +9,14 @@ const addPanel = (container: HTMLElement, sqore: Sqore): void => {
         elem.addEventListener('mousedown', () => {
             const dataId = elem.getAttribute('data-id');
             const operation = _equivOperation(dataId, sqore.circuit.operations);
-            const newPanelElem = _panel(qubitSize, operation || undefined);
+            const newPanelElem = _panel(qubitSize, dispatch, operation || undefined);
             container.replaceChild(newPanelElem, panelElem);
             panelElem = newPanelElem;
         }),
     );
+    const dispatch = reducer(container, sqore);
     const qubitSize = sqore.circuit.qubits.length;
-    let panelElem = _panel(qubitSize);
+    let panelElem = _panel(qubitSize, dispatch);
     container.prepend(panelElem);
 };
 
@@ -24,7 +25,7 @@ interface Action {
     payload: unknown;
 }
 
-const reducer = (initial: Operation | undefined, action: Action) => {
+const reducer = (container: HTMLElement, sqore: Sqore) => (initial: Operation | undefined, action: Action) => {
     if (initial == null) return;
 
     switch (action.type) {
@@ -35,11 +36,10 @@ const reducer = (initial: Operation | undefined, action: Action) => {
             Object.assign(initial, { ...initial, controls: action.payload });
         }
     }
-    console.log(initial);
-    console.log('Re-rendering...');
+    sqore.draw(container);
 };
 
-const _panel = (qubitSize: number, operation?: Operation) => {
+const _panel = (qubitSize: number, dispatch: Dispatch, operation?: Operation) => {
     const options = range(qubitSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
     const target = operation?.targets[0].qId;
     const controls = operation?.controls?.map((control) => control.qId);
@@ -47,8 +47,8 @@ const _panel = (qubitSize: number, operation?: Operation) => {
     const panelElem = _elem('div');
     panelElem.className = 'panel';
     _children(panelElem, [
-        _select('Target', 'target-input', options, target || 0, operation),
-        _checkboxes('Controls', 'controls-input', options, controls || [], operation),
+        _select('Target', 'target-input', options, target || 0, dispatch, operation),
+        _checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
         _text('Display', 'display-input', 'display-arg'),
     ]);
 
@@ -70,11 +70,16 @@ interface Option {
     text: string;
 }
 
+interface Dispatch {
+    (initial: Operation | undefined, action: Action): void;
+}
+
 const _select = (
     label: string,
     className: string,
     options: Option[],
     selectedIndex: number,
+    dispatch: Dispatch,
     operation?: Operation,
 ): HTMLElement => {
     const optionElems = options.map(({ value, text }) => _option(value, text));
@@ -91,7 +96,7 @@ const _select = (
     _children(divElem, [labelElem, selectElem]);
 
     selectElem.onchange = () => {
-        reducer(operation, { type: 'TARGET', payload: [{ qId: selectElem.value }] });
+        dispatch(operation, { type: 'TARGET', payload: [{ qId: selectElem.value }] });
     };
 
     return divElem;
@@ -109,6 +114,7 @@ const _checkboxes = (
     className: string,
     options: Option[],
     selectedIndexes: number[],
+    dispatch: Dispatch,
     operation?: Operation,
 ) => {
     const checkboxElems = options.map((option, index) => {
@@ -122,7 +128,7 @@ const _checkboxes = (
             const newControls = checkedElems.map((elem) => ({
                 qId: elem.value,
             }));
-            reducer(operation, { type: 'CONTROLS', payload: newControls });
+            dispatch(operation, { type: 'CONTROLS', payload: newControls });
         };
 
         return elem;

From 5b51043afd2c0ca8f8e2b9fd6944151ecf3241d9 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <37777232+aaronthangnguyen@users.noreply.github.com>
Date: Wed, 13 Jul 2022 15:12:26 -0400
Subject: [PATCH 060/108] Add extensions feature (#76)

* First commit

* Fix mutation

* Fix persistence in panel

* Allow editing displayArgs
---
 example/script.js                 |  26 ++++---
 src/{editable.ts => draggable.ts} |  23 ++----
 src/index.ts                      |  15 +---
 src/panel.ts                      |  79 ++++++++++++++-------
 src/sqore.ts                      | 112 +++++++++++++++++-------------
 5 files changed, 139 insertions(+), 116 deletions(-)
 rename src/{editable.ts => draggable.ts} (96%)

diff --git a/example/script.js b/example/script.js
index 9c15533c..d65eb0d7 100644
--- a/example/script.js
+++ b/example/script.js
@@ -7,30 +7,38 @@ if (typeof qviz != 'undefined') {
     /* These examples shows how to draw circuits into containers. */
     const entangleDiv = document.getElementById('entangle');
     if (entangleDiv != null) {
-        qviz.draw(entangle, entangleDiv, qviz.STYLES['Default'], 0, true);
+        qviz.create(entangle) //
+            .useDraggable()
+            .usePanel()
+            .draw(entangleDiv);
     }
 
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
-        const isEditable = true;
-        const onCircuitChange = (circuit) => {
-            console.log('New circuit ↓');
-            console.log(circuit);
-        };
         /* Pass in isEditable = true to allow circuit to be editable */
         /* Pass in onCircuitChange to trigger callback function
            whenever there is a change in circuit */
-        qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, isEditable, onCircuitChange);
+        // qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, isEditable, onCircuitChange);
+        qviz.create(sample) //
+            .useDraggable()
+            .usePanel()
+            .useOnCircuitChange((circuit) => {
+                console.log('New circuit ↓');
+                console.log(circuit);
+            })
+            .draw(sampleDiv);
     }
 
     const teleportDiv = document.getElementById('teleport');
     if (teleportDiv != null) {
-        qviz.draw(teleport, teleportDiv, qviz.STYLES['Default'], 0, true);
+        qviz.create(teleport) //
+            .draw(teleportDiv);
     }
 
     const groverDiv = document.getElementById('grover');
     if (groverDiv != null) {
-        qviz.draw(grover, groverDiv, qviz.STYLES['Default']);
+        qviz.create(grover) //
+            .draw(groverDiv);
     }
 } else {
     document.getElementById('group').remove();
diff --git a/src/editable.ts b/src/draggable.ts
similarity index 96%
rename from src/editable.ts
rename to src/draggable.ts
index f6e54776..5ab8c854 100644
--- a/src/editable.ts
+++ b/src/draggable.ts
@@ -3,7 +3,7 @@
 
 import cloneDeep from 'lodash/cloneDeep';
 import isEqual from 'lodash/isEqual';
-import { Circuit, Operation } from './circuit';
+import { Operation } from './circuit';
 import { box } from './formatters/formatUtils';
 import { Register } from './register';
 import { Sqore } from './sqore';
@@ -26,7 +26,8 @@ interface Context {
  * @param sqore             Sqore object
  * @param onCircuitChange   User-provided callback function triggered when circuit is changed
  */
-const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (circuit: Circuit) => void): void => {
+const extensionDraggable = (container: HTMLElement, sqore: Sqore, useRender: () => void): void => {
+    console.log('Draggable extension is running');
     const svg = container.querySelector('svg') as SVGElement;
 
     const context: Context = {
@@ -34,7 +35,7 @@ const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (ci
         svg,
         operations: sqore.circuit.operations,
         wireData: _wireData(container),
-        renderFn: _renderFn(container, sqore, onCircuitChange),
+        renderFn: useRender,
         paddingY: 20,
         selectedId: null,
         selectedWire: null,
@@ -518,20 +519,6 @@ const _lastIndex = (dataId: string): number | undefined => {
     return _indexes(dataId).pop();
 };
 
-/**
- * Return a render function with the onCircuitChange callback attached to it
- */
-const _renderFn = (
-    container: HTMLElement,
-    sqore: Sqore,
-    onCircuitChange?: (circuit: Circuit) => void,
-): (() => void) => {
-    return () => {
-        sqore.draw(container, 0, true, onCircuitChange);
-        if (onCircuitChange) onCircuitChange(sqore.circuit);
-    };
-};
-
 /**
  * Object exported for unit testing
  */
@@ -555,4 +542,4 @@ const exportedForTesting = {
     _lastIndex,
 };
 
-export { addEditable, Context, _equivOperation, exportedForTesting };
+export { extensionDraggable, Context, _equivOperation, exportedForTesting };
diff --git a/src/index.ts b/src/index.ts
index fdc0c9a2..2d384b84 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,7 +4,6 @@
 import { Sqore } from './sqore';
 import { Circuit } from './circuit';
 import { StyleConfig } from './styles';
-import { addPanel } from './panel';
 
 /**
  * Render `circuit` into `container` at the specified layer depth.
@@ -16,18 +15,8 @@ import { addPanel } from './panel';
  * @param isEditable Optional value enabling/disabling editable feature
  * @param onCircuitChange Optional function to trigger when changing elements in circuit
  */
-export const draw = (
-    circuit: Circuit,
-    container: HTMLElement,
-    style: StyleConfig | string = {},
-    renderDepth = 0,
-    isEditable?: boolean,
-    onCircuitChange?: (circuit: Circuit) => void,
-): void => {
-    const sqore = new Sqore(circuit, style);
-
-    sqore.draw(container, renderDepth, isEditable, onCircuitChange);
-    addPanel(container, sqore);
+export const create = (circuit: Circuit, style: StyleConfig | string = {}): Sqore => {
+    return new Sqore(circuit, style);
 };
 
 export { STYLES } from './styles';
diff --git a/src/panel.ts b/src/panel.ts
index 7ffad9bd..4f08b715 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,23 +1,34 @@
 import range from 'lodash/range';
 import { Operation } from './circuit';
-import { _equivOperation } from './editable';
+import { _equivOperation } from './draggable';
+import { Register } from './register';
 import { Sqore } from './sqore';
 
-const addPanel = (container: HTMLElement, sqore: Sqore): void => {
+interface Context {
+    operation: Operation | undefined;
+}
+
+const context: Context = {
+    operation: undefined,
+};
+
+const extensionPanel = (container: HTMLElement, sqore: Sqore, useRender: () => void): void => {
     const elems = container.querySelectorAll<SVGElement>('[data-id]');
     elems.forEach((elem) =>
         elem.addEventListener('mousedown', () => {
             const dataId = elem.getAttribute('data-id');
             const operation = _equivOperation(dataId, sqore.circuit.operations);
-            const newPanelElem = _panel(qubitSize, dispatch, operation || undefined);
+            context.operation = operation || undefined;
+            const newPanelElem = _panel(qubitSize, dispatch, context.operation);
             container.replaceChild(newPanelElem, panelElem);
             panelElem = newPanelElem;
         }),
     );
-    const dispatch = reducer(container, sqore);
+    const dispatch = reducer(container, sqore, useRender);
     const qubitSize = sqore.circuit.qubits.length;
-    let panelElem = _panel(qubitSize, dispatch);
-    container.prepend(panelElem);
+    let panelElem = _panel(qubitSize, dispatch, context.operation);
+    const prevPanelElem = container.querySelector('.panel');
+    prevPanelElem ? container.replaceChild(panelElem, prevPanelElem) : container.prepend(panelElem);
 };
 
 interface Action {
@@ -25,19 +36,27 @@ interface Action {
     payload: unknown;
 }
 
-const reducer = (container: HTMLElement, sqore: Sqore) => (initial: Operation | undefined, action: Action) => {
-    if (initial == null) return;
-
-    switch (action.type) {
-        case 'TARGET': {
-            Object.assign(initial, { ...initial, targets: action.payload });
+const reducer =
+    (_container: HTMLElement, _sqore: Sqore, useRender: () => void) =>
+    (operation: Operation | undefined, action: Action) => {
+        if (operation == undefined) return;
+
+        switch (action.type) {
+            case 'TARGET': {
+                operation.targets = action.payload as Register[];
+                break;
+            }
+            case 'CONTROLS': {
+                operation.controls = action.payload as Register[];
+                break;
+            }
+            case 'DISPLAY_ARGS': {
+                operation.displayArgs = action.payload as string;
+                break;
+            }
         }
-        case 'CONTROLS': {
-            Object.assign(initial, { ...initial, controls: action.payload });
-        }
-    }
-    sqore.draw(container);
-};
+        useRender();
+    };
 
 const _panel = (qubitSize: number, dispatch: Dispatch, operation?: Operation) => {
     const options = range(qubitSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
@@ -49,7 +68,7 @@ const _panel = (qubitSize: number, dispatch: Dispatch, operation?: Operation) =>
     _children(panelElem, [
         _select('Target', 'target-input', options, target || 0, dispatch, operation),
         _checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
-        _text('Display', 'display-input', 'display-arg'),
+        _text('Display', 'display-input', dispatch, operation),
     ]);
 
     return panelElem;
@@ -71,7 +90,7 @@ interface Option {
 }
 
 interface Dispatch {
-    (initial: Operation | undefined, action: Action): void;
+    (operation: Operation | undefined, action: Action): void;
 }
 
 const _select = (
@@ -85,7 +104,7 @@ const _select = (
     const optionElems = options.map(({ value, text }) => _option(value, text));
     const selectElem = _elem('select') as HTMLSelectElement;
     _children(selectElem, optionElems);
-    operation == null && selectElem.setAttribute('disabled', 'true');
+    operation == undefined && selectElem.setAttribute('disabled', 'true');
     selectElem.selectedIndex = selectedIndex;
 
     const labelElem = _elem('label') as HTMLLabelElement;
@@ -96,7 +115,7 @@ const _select = (
     _children(divElem, [labelElem, selectElem]);
 
     selectElem.onchange = () => {
-        dispatch(operation, { type: 'TARGET', payload: [{ qId: selectElem.value }] });
+        dispatch(operation, { type: 'TARGET', payload: [{ qId: parseInt(selectElem.value) }] });
     };
 
     return divElem;
@@ -121,12 +140,12 @@ const _checkboxes = (
         const elem = _checkbox(option.value, option.text);
         const inputElem = elem.querySelector('input') as HTMLInputElement;
         selectedIndexes.includes(index) && inputElem.setAttribute('checked', 'true');
-        operation == null && inputElem.setAttribute('disabled', 'true');
+        operation == undefined && inputElem.setAttribute('disabled', 'true');
 
         inputElem.onchange = () => {
             const checkedElems = Array.from(divElem.querySelectorAll<HTMLInputElement>('input:checked'));
             const newControls = checkedElems.map((elem) => ({
-                qId: elem.value,
+                qId: parseInt(elem.value),
             }));
             dispatch(operation, { type: 'CONTROLS', payload: newControls });
         };
@@ -155,13 +174,19 @@ const _checkbox = (value: string, text: string) => {
     return labelElem;
 };
 
-const _text = (label: string, className: string, value: string) => {
+const _text = (label: string, className: string, dispatch: Dispatch, operation?: Operation) => {
     const labelElem = _elem('label') as HTMLLabelElement;
     labelElem.textContent = label;
 
     const textElem = _elem('input') as HTMLInputElement;
+    operation == undefined && textElem.setAttribute('disabled', 'true');
     textElem.type = 'text';
-    textElem.value = value;
+    textElem.value = operation?.displayArgs || '';
+    textElem.setAttribute('autofocus', 'true');
+
+    textElem.onchange = () => {
+        dispatch(operation, { type: 'DISPLAY_ARGS', payload: textElem.value });
+    };
 
     const divElem = _elem('div');
     divElem.className = className;
@@ -170,4 +195,4 @@ const _text = (label: string, className: string, value: string) => {
     return divElem;
 };
 
-export { addPanel };
+export { extensionPanel };
diff --git a/src/sqore.ts b/src/sqore.ts
index b88db601..1433e54e 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -3,7 +3,8 @@
 
 import { Circuit, ConditionalRender, Operation } from './circuit';
 import { svgNS } from './constants';
-import { addEditable } from './editable';
+import { extensionDraggable } from './draggable';
+import { extensionPanel } from './panel';
 import { formatGates } from './formatters/gateFormatter';
 import { formatInputs } from './formatters/inputFormatter';
 import { formatRegisters } from './formatters/registerFormatter';
@@ -32,6 +33,10 @@ type GateRegistry = {
     [id: string]: Operation;
 };
 
+type Extension = {
+    (container: HTMLElement, sqore: Sqore, useRender: () => void): void;
+};
+
 /**
  * Entrypoint class for rendering circuit visualizations.
  */
@@ -39,6 +44,8 @@ export class Sqore {
     circuit: Circuit;
     style: StyleConfig = {};
     gateRegistry: GateRegistry = {};
+    extensions: Extension[] = [];
+    renderDepth = 0;
 
     /**
      * Initializes Sqore object with custom styles.
@@ -49,6 +56,7 @@ export class Sqore {
     constructor(circuit: Circuit, style: StyleConfig | string = {}) {
         this.circuit = circuit;
         this.style = this.getStyle(style);
+        this.extensions = [];
     }
 
     /**
@@ -56,37 +64,15 @@ export class Sqore {
      *
      * @param container HTML element for rendering visualization into.
      * @param renderDepth Initial layer depth at which to render gates.
-     * @param isEditable Optional value enabling/disabling editable feature
-     * @param onCircuitChange Optional function to trigger when changing elements in circuit
      */
-    draw(
-        container: HTMLElement,
-        renderDepth = 0,
-        isEditable?: boolean,
-        onCircuitChange?: (circuit: Circuit) => void,
-    ): void {
+    draw(container: HTMLElement, renderDepth = 0): Sqore {
         // Inject into container
         if (container == null) throw new Error(`Container not provided.`);
 
-        // Create copy of circuit to prevent mutation
-        const circuit: Circuit = JSON.parse(JSON.stringify(this.circuit));
-
-        // Assign unique IDs to each operation
-        circuit.operations.forEach((op, i) => this.fillGateRegistry(op, i.toString()));
-
-        // Render operations at starting at given depth
-        circuit.operations = this.selectOpsAtDepth(circuit.operations, renderDepth);
+        this.renderDepth = renderDepth;
+        this.renderCircuit(container);
 
-        // If only one top-level operation, expand automatically:
-        if (
-            circuit.operations.length == 1 &&
-            circuit.operations[0].dataAttributes != null &&
-            circuit.operations[0].dataAttributes.hasOwnProperty('id')
-        ) {
-            const id: string = circuit.operations[0].dataAttributes['id'];
-            this.expandOperation(circuit.operations, id);
-        }
-        this.renderCircuit(container, circuit, isEditable, onCircuitChange);
+        return this;
     }
 
     /**
@@ -116,12 +102,27 @@ export class Sqore {
      * @param isEditable Optional value enabling/disabling editable feature
      * @param onCircuitChange Optional function to trigger when changing elements in circuit
      */
-    private renderCircuit(
-        container: HTMLElement,
-        circuit: Circuit,
-        isEditable?: boolean,
-        onCircuitChange?: (circuit: Circuit) => void,
-    ): void {
+    private renderCircuit(container: HTMLElement): void {
+        // Create copy of circuit to prevent mutation
+        const circuit: Circuit = JSON.parse(JSON.stringify(this.circuit));
+        const renderDepth = this.renderDepth;
+
+        // Assign unique IDs to each operation
+        circuit.operations.forEach((op, i) => this.fillGateRegistry(op, i.toString()));
+
+        // Render operations at starting at given depth
+        circuit.operations = this.selectOpsAtDepth(circuit.operations, renderDepth);
+
+        // If only one top-level operation, expand automatically:
+        if (
+            circuit.operations.length == 1 &&
+            circuit.operations[0].dataAttributes != null &&
+            circuit.operations[0].dataAttributes.hasOwnProperty('id')
+        ) {
+            const id: string = circuit.operations[0].dataAttributes['id'];
+            this.expandOperation(circuit.operations, id);
+        }
+
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);
@@ -129,9 +130,12 @@ export class Sqore {
         previousSvg == null //
             ? container.appendChild(svg)
             : container.replaceChild(svg, previousSvg);
-        this.addGateClickHandlers(container, circuit, isEditable, onCircuitChange);
+        this.addGateClickHandlers(container, circuit);
 
-        isEditable && addEditable(container, this, onCircuitChange);
+        // Run extensions after every render or re-render
+        const extensions = this.extensions;
+        extensions != null &&
+            extensions.map((extension) => extension(container, this, () => this.renderCircuit(container)));
     }
 
     /**
@@ -251,14 +255,9 @@ export class Sqore {
      * @param onCircuitChange Optional function to trigger when changing elements in circuit
      *
      */
-    private addGateClickHandlers(
-        container: HTMLElement,
-        circuit: Circuit,
-        isEditable?: boolean,
-        onCircuitChange?: (circuit: Circuit) => void,
-    ): void {
+    private addGateClickHandlers(container: HTMLElement, circuit: Circuit): void {
         this.addClassicalControlHandlers(container);
-        this.addZoomHandlers(container, circuit, isEditable, onCircuitChange);
+        this.addZoomHandlers(container, circuit);
     }
 
     /**
@@ -318,12 +317,7 @@ export class Sqore {
      * @param onCircuitChange Optional function to trigger when changing elements in circuit
      *
      */
-    private addZoomHandlers(
-        container: HTMLElement,
-        circuit: Circuit,
-        isEditable?: boolean,
-        onCircuitChange?: (circuit: Circuit) => void,
-    ): void {
+    private addZoomHandlers(container: HTMLElement, circuit: Circuit): void {
         container.querySelectorAll('.gate .gate-control').forEach((ctrl) => {
             // Zoom in on clicked gate
             ctrl.addEventListener('click', (ev: Event) => {
@@ -334,7 +328,7 @@ export class Sqore {
                     } else if (ctrl.classList.contains('gate-expand')) {
                         this.expandOperation(circuit.operations, gateId);
                     }
-                    this.renderCircuit(container, circuit, isEditable, onCircuitChange);
+                    this.renderCircuit(container);
                     ev.stopPropagation();
                 }
             });
@@ -379,4 +373,24 @@ export class Sqore {
             }
         });
     }
+
+    public useDraggable(): Sqore {
+        this.extensions.push(extensionDraggable);
+        return this;
+    }
+
+    public usePanel(): Sqore {
+        this.extensions.push(extensionPanel);
+        return this;
+    }
+
+    public useOnCircuitChange(callback: (circuit: Circuit) => void): Sqore {
+        const extensionOnCircuitChange = (
+            _container: HTMLElement, //
+            _sqore: Sqore,
+            _useRender: () => void,
+        ) => callback(this.circuit);
+        this.extensions.push(extensionOnCircuitChange);
+        return this;
+    }
 }

From 2a5c35efc8ce9ec88010b9322786ef118e86ad58 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <37777232+aaronthangnguyen@users.noreply.github.com>
Date: Wed, 13 Jul 2022 15:20:41 -0400
Subject: [PATCH 061/108] Sync with `main` (#77)

* v 1.0.5

* Fixing jupyter viewer display (#70)

Co-authored-by: Andres Paz <anpaz@microsoft.com>

From 473914ad69e7d331e6e18d50c33b71806adbdde6 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 13 Jul 2022 17:22:06 -0400
Subject: [PATCH 062/108] Add styles

---
 example/index.html |  1 +
 src/draggable.ts   | 13 +------------
 src/panel.ts       | 14 +++++++++++++-
 src/sqore.ts       | 26 ++++++++++++--------------
 src/styles.ts      | 27 +++++++++++++++++++++++++--
 5 files changed, 52 insertions(+), 29 deletions(-)

diff --git a/example/index.html b/example/index.html
index e612d7b8..196a80c5 100644
--- a/example/index.html
+++ b/example/index.html
@@ -11,6 +11,7 @@
     <script src="./script.js" defer></script>
     <style>
         body {
+            font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
             display: flex;
             justify-content: center;
         }
diff --git a/src/draggable.ts b/src/draggable.ts
index 5ab8c854..93c43f3e 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -27,7 +27,6 @@ interface Context {
  * @param onCircuitChange   User-provided callback function triggered when circuit is changed
  */
 const extensionDraggable = (container: HTMLElement, sqore: Sqore, useRender: () => void): void => {
-    console.log('Draggable extension is running');
     const svg = container.querySelector('svg') as SVGElement;
 
     const context: Context = {
@@ -212,15 +211,6 @@ const _equivDataId = (elem: SVGElement) => {
     return gateElem != null ? gateElem.getAttribute('data-id') : null;
 };
 
-/**
- * Disable contextmenu default behaviors
- */
-const _addContextMenuEvents = (container: HTMLElement) => {
-    container.addEventListener('contextmenu', (ev: MouseEvent) => {
-        ev.preventDefault();
-    });
-};
-
 /**
  * Add events specifically for dropzoneLayer
  */
@@ -260,7 +250,6 @@ const _addEvents = (context: Context) => {
     const { container, operations, renderFn } = context;
     const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
 
-    _addContextMenuEvents(container);
     _addDropzoneLayerEvents(container, dropzoneLayer);
     _addDocumentEvents(context);
 
@@ -542,4 +531,4 @@ const exportedForTesting = {
     _lastIndex,
 };
 
-export { extensionDraggable, Context, _equivOperation, exportedForTesting };
+export { extensionDraggable, Context, _equivOperation, _equivGateElem, exportedForTesting };
diff --git a/src/panel.ts b/src/panel.ts
index 4f08b715..2b1f1f52 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -43,7 +43,8 @@ const reducer =
 
         switch (action.type) {
             case 'TARGET': {
-                operation.targets = action.payload as Register[];
+                const payload = action.payload as Register[];
+                operation.targets = payload;
                 break;
             }
             case 'CONTROLS': {
@@ -66,6 +67,7 @@ const _panel = (qubitSize: number, dispatch: Dispatch, operation?: Operation) =>
     const panelElem = _elem('div');
     panelElem.className = 'panel';
     _children(panelElem, [
+        _title('EDIT'),
         _select('Target', 'target-input', options, target || 0, dispatch, operation),
         _checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
         _text('Display', 'display-input', dispatch, operation),
@@ -84,6 +86,13 @@ const _children = (parentElem: HTMLElement, childElems: HTMLElement[]) => {
     return parentElem;
 };
 
+const _title = (text: string) => {
+    const elem = _elem('h1');
+    elem.className = 'title';
+    elem.innerText = text;
+    return elem;
+};
+
 interface Option {
     value: string;
     text: string;
@@ -108,6 +117,7 @@ const _select = (
     selectElem.selectedIndex = selectedIndex;
 
     const labelElem = _elem('label') as HTMLLabelElement;
+    labelElem.className = 'block';
     labelElem.textContent = label;
 
     const divElem = _elem('div') as HTMLDivElement;
@@ -154,6 +164,7 @@ const _checkboxes = (
     });
 
     const labelElem = _elem('label');
+    labelElem.className = 'block';
     labelElem.textContent = label;
 
     const divElem = _elem('div') as HTMLDivElement;
@@ -176,6 +187,7 @@ const _checkbox = (value: string, text: string) => {
 
 const _text = (label: string, className: string, dispatch: Dispatch, operation?: Operation) => {
     const labelElem = _elem('label') as HTMLLabelElement;
+    labelElem.className = 'block';
     labelElem.textContent = label;
 
     const textElem = _elem('input') as HTMLInputElement;
diff --git a/src/sqore.ts b/src/sqore.ts
index 1433e54e..0300c5d5 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -99,38 +99,36 @@ export class Sqore {
      *
      * @param container HTML element for rendering visualization into.
      * @param circuit Circuit object to be rendered.
-     * @param isEditable Optional value enabling/disabling editable feature
-     * @param onCircuitChange Optional function to trigger when changing elements in circuit
      */
-    private renderCircuit(container: HTMLElement): void {
+    private renderCircuit(container: HTMLElement, circuit?: Circuit): void {
         // Create copy of circuit to prevent mutation
-        const circuit: Circuit = JSON.parse(JSON.stringify(this.circuit));
+        const _circuit: Circuit = circuit ?? JSON.parse(JSON.stringify(this.circuit));
         const renderDepth = this.renderDepth;
 
         // Assign unique IDs to each operation
-        circuit.operations.forEach((op, i) => this.fillGateRegistry(op, i.toString()));
+        _circuit.operations.forEach((op, i) => this.fillGateRegistry(op, i.toString()));
 
         // Render operations at starting at given depth
-        circuit.operations = this.selectOpsAtDepth(circuit.operations, renderDepth);
+        _circuit.operations = this.selectOpsAtDepth(_circuit.operations, renderDepth);
 
         // If only one top-level operation, expand automatically:
         if (
-            circuit.operations.length == 1 &&
-            circuit.operations[0].dataAttributes != null &&
-            circuit.operations[0].dataAttributes.hasOwnProperty('id')
+            _circuit.operations.length == 1 &&
+            _circuit.operations[0].dataAttributes != null &&
+            _circuit.operations[0].dataAttributes.hasOwnProperty('id')
         ) {
-            const id: string = circuit.operations[0].dataAttributes['id'];
-            this.expandOperation(circuit.operations, id);
+            const id: string = _circuit.operations[0].dataAttributes['id'];
+            this.expandOperation(_circuit.operations, id);
         }
 
         // Create visualization components
-        const composedSqore: ComposedSqore = this.compose(circuit);
+        const composedSqore: ComposedSqore = this.compose(_circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);
         const previousSvg = container.querySelector('svg');
         previousSvg == null //
             ? container.appendChild(svg)
             : container.replaceChild(svg, previousSvg);
-        this.addGateClickHandlers(container, circuit);
+        this.addGateClickHandlers(container, _circuit);
 
         // Run extensions after every render or re-render
         const extensions = this.extensions;
@@ -328,7 +326,7 @@ export class Sqore {
                     } else if (ctrl.classList.contains('gate-expand')) {
                         this.expandOperation(circuit.operations, gateId);
                     }
-                    this.renderCircuit(container);
+                    this.renderCircuit(container, circuit);
                     ev.stopPropagation();
                 }
             });
diff --git a/src/styles.ts b/src/styles.ts
index 06e7d787..764ed6e2 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -96,7 +96,8 @@ export const style = (customStyle: StyleConfig = {}): string => {
     return `${_defaultGates(styleConfig)}
     ${_classicallyControlledGates(styleConfig)}
     ${_expandCollapse}
-    ${_editable}`;
+    ${_draggable}
+    ${_panel}`;
 };
 
 const _defaultGates = (styleConfig: StyleConfig): string => `
@@ -236,7 +237,7 @@ const _expandCollapse = `
         transition: opacity 1s;
     }`;
 
-const _editable = `
+const _draggable = `
     text {
         user-select: none;
         pointer-events: none;
@@ -262,3 +263,25 @@ const _editable = `
         cursor: copy;
     }
     `;
+
+const _panel = `
+    .container {
+        display: flex;
+    }
+    .panel {
+        display: flex;
+        flex-direction: column;
+        border: solid 2px black;
+        padding: 8px;
+    }
+    .title {
+        margin: 0 0 4px;
+        font-size: 1.2rem;
+    }
+    .block {
+        display: block;
+    }
+    .target-input, .controls-input, .display-input {
+        margin-bottom: 8px;
+    }
+`;

From acae20311964151f5267f7734c6cb09fa75be063 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 20 Jul 2022 10:55:30 -0400
Subject: [PATCH 063/108] Fix context

---
 src/panel.ts | 91 +++++++++++++++++++++++++++++-----------------------
 1 file changed, 51 insertions(+), 40 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 2b1f1f52..41e3e19f 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -6,29 +6,35 @@ import { Sqore } from './sqore';
 
 interface Context {
     operation: Operation | undefined;
+    registerSize: number;
 }
 
 const context: Context = {
     operation: undefined,
+    registerSize: 0,
 };
 
-const extensionPanel = (container: HTMLElement, sqore: Sqore, useRender: () => void): void => {
+const extensionPanel = (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
     const elems = container.querySelectorAll<SVGElement>('[data-id]');
     elems.forEach((elem) =>
         elem.addEventListener('mousedown', () => {
             const dataId = elem.getAttribute('data-id');
             const operation = _equivOperation(dataId, sqore.circuit.operations);
             context.operation = operation || undefined;
-            const newPanelElem = _panel(qubitSize, dispatch, context.operation);
+            context.registerSize = sqore.circuit.qubits.length;
+            const newPanelElem = _panel(dispatch, context);
             container.replaceChild(newPanelElem, panelElem);
             panelElem = newPanelElem;
         }),
     );
-    const dispatch = reducer(container, sqore, useRender);
-    const qubitSize = sqore.circuit.qubits.length;
-    let panelElem = _panel(qubitSize, dispatch, context.operation);
+
+    const dispatch = update(context, useRefresh);
+
+    let panelElem = _panel(dispatch, context);
     const prevPanelElem = container.querySelector('.panel');
-    prevPanelElem ? container.replaceChild(panelElem, prevPanelElem) : container.prepend(panelElem);
+    prevPanelElem //
+        ? container.replaceChild(panelElem, prevPanelElem)
+        : container.prepend(panelElem);
 };
 
 interface Action {
@@ -36,44 +42,49 @@ interface Action {
     payload: unknown;
 }
 
-const reducer =
-    (_container: HTMLElement, _sqore: Sqore, useRender: () => void) =>
-    (operation: Operation | undefined, action: Action) => {
-        if (operation == undefined) return;
-
-        switch (action.type) {
-            case 'TARGET': {
-                const payload = action.payload as Register[];
-                operation.targets = payload;
-                break;
-            }
-            case 'CONTROLS': {
-                operation.controls = action.payload as Register[];
-                break;
-            }
-            case 'DISPLAY_ARGS': {
-                operation.displayArgs = action.payload as string;
-                break;
-            }
+const update = (context: Context, useRefresh: () => void) => (action: Action) => {
+    switch (action.type) {
+        case 'TARGET': {
+            const { operation } = context;
+            const payload = action.payload as Register[];
+            operation && (operation.targets = payload);
+            break;
         }
-        useRender();
-    };
-
-const _panel = (qubitSize: number, dispatch: Dispatch, operation?: Operation) => {
-    const options = range(qubitSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
-    const target = operation?.targets[0].qId;
-    const controls = operation?.controls?.map((control) => control.qId);
+        case 'CONTROLS': {
+            const { operation } = context;
+            operation && (operation.controls = action.payload as Register[]);
+            break;
+        }
+        case 'DISPLAY_ARGS': {
+            const { operation } = context;
+            operation && (operation.displayArgs = action.payload as string);
+            break;
+        }
+    }
+    useRefresh();
+};
 
+const _panel = (dispatch: Dispatch, context: Context) => {
     const panelElem = _elem('div');
     panelElem.className = 'panel';
-    _children(panelElem, [
+    _children(panelElem, editPanel(dispatch, context));
+
+    return panelElem;
+};
+
+// const addPanel = () => {};
+
+const editPanel = (dispatch: Dispatch, context: Context) => {
+    const { operation, registerSize } = context;
+    const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
+    const target = operation?.targets[0].qId;
+    const controls = operation?.controls?.map((control) => control.qId);
+    return [
         _title('EDIT'),
         _select('Target', 'target-input', options, target || 0, dispatch, operation),
         _checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
         _text('Display', 'display-input', dispatch, operation),
-    ]);
-
-    return panelElem;
+    ];
 };
 
 const _elem = (tag: string): HTMLElement => document.createElement(tag);
@@ -99,7 +110,7 @@ interface Option {
 }
 
 interface Dispatch {
-    (operation: Operation | undefined, action: Action): void;
+    (action: Action): void;
 }
 
 const _select = (
@@ -125,7 +136,7 @@ const _select = (
     _children(divElem, [labelElem, selectElem]);
 
     selectElem.onchange = () => {
-        dispatch(operation, { type: 'TARGET', payload: [{ qId: parseInt(selectElem.value) }] });
+        dispatch({ type: 'TARGET', payload: [{ qId: parseInt(selectElem.value) }] });
     };
 
     return divElem;
@@ -157,7 +168,7 @@ const _checkboxes = (
             const newControls = checkedElems.map((elem) => ({
                 qId: parseInt(elem.value),
             }));
-            dispatch(operation, { type: 'CONTROLS', payload: newControls });
+            dispatch({ type: 'CONTROLS', payload: newControls });
         };
 
         return elem;
@@ -197,7 +208,7 @@ const _text = (label: string, className: string, dispatch: Dispatch, operation?:
     textElem.setAttribute('autofocus', 'true');
 
     textElem.onchange = () => {
-        dispatch(operation, { type: 'DISPLAY_ARGS', payload: textElem.value });
+        dispatch({ type: 'DISPLAY_ARGS', payload: textElem.value });
     };
 
     const divElem = _elem('div');

From 72357ba8873d584638e319fdbc5389b55419e54e Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 20 Jul 2022 11:49:23 -0400
Subject: [PATCH 064/108] Allow basic operation appending

---
 src/panel.ts  | 99 ++++++++++++++++++++++++++++++++-------------------
 src/styles.ts |  3 +-
 2 files changed, 64 insertions(+), 38 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 41e3e19f..4f021993 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -5,11 +5,13 @@ import { Register } from './register';
 import { Sqore } from './sqore';
 
 interface Context {
+    operations: Operation[];
     operation: Operation | undefined;
     registerSize: number;
 }
 
-const context: Context = {
+let context: Context = {
+    operations: [],
     operation: undefined,
     registerSize: 0,
 };
@@ -21,16 +23,18 @@ const extensionPanel = (container: HTMLElement, sqore: Sqore, useRefresh: () =>
             const dataId = elem.getAttribute('data-id');
             const operation = _equivOperation(dataId, sqore.circuit.operations);
             context.operation = operation || undefined;
-            context.registerSize = sqore.circuit.qubits.length;
-            const newPanelElem = _panel(dispatch, context);
+            const newPanelElem = panel(dispatch, context);
             container.replaceChild(newPanelElem, panelElem);
             panelElem = newPanelElem;
         }),
     );
+    container.addEventListener('mouseover', () => {
+        context.registerSize = sqore.circuit.qubits.length;
+        context.operations = sqore.circuit.operations;
+    });
 
     const dispatch = update(context, useRefresh);
-
-    let panelElem = _panel(dispatch, context);
+    let panelElem = panel(dispatch, context);
     const prevPanelElem = container.querySelector('.panel');
     prevPanelElem //
         ? container.replaceChild(panelElem, prevPanelElem)
@@ -44,35 +48,56 @@ interface Action {
 
 const update = (context: Context, useRefresh: () => void) => (action: Action) => {
     switch (action.type) {
+        case 'OPERATION': {
+            context.operation = action.payload as Operation;
+        }
         case 'TARGET': {
             const { operation } = context;
             const payload = action.payload as Register[];
             operation && (operation.targets = payload);
+            useRefresh();
             break;
         }
         case 'CONTROLS': {
             const { operation } = context;
             operation && (operation.controls = action.payload as Register[]);
+            useRefresh();
             break;
         }
         case 'DISPLAY_ARGS': {
             const { operation } = context;
             operation && (operation.displayArgs = action.payload as string);
+            useRefresh();
+            break;
+        }
+        case 'ADD_OPERATION': {
+            context.operations.push(action.payload as Operation);
+            useRefresh();
             break;
         }
     }
-    useRefresh();
 };
 
-const _panel = (dispatch: Dispatch, context: Context) => {
-    const panelElem = _elem('div');
+const panel = (dispatch: Dispatch, context: Context) => {
+    const panelElem = elem('div');
     panelElem.className = 'panel';
-    _children(panelElem, editPanel(dispatch, context));
-
+    children(panelElem, addPanel(dispatch, context));
+    // _children(panelElem, editPanel(dispatch, context));
     return panelElem;
 };
 
-// const addPanel = () => {};
+const addPanel = (dispatch: Dispatch, context: Context) => {
+    const buttonElem = elem('button') as HTMLButtonElement;
+    buttonElem.textContent = 'H';
+    buttonElem.addEventListener('mouseup', () => {
+        const operation = {
+            gate: 'H',
+            targets: [{ qId: 0 }],
+        };
+        dispatch({ type: 'ADD_OPERATION', payload: operation });
+    });
+    return [title('ADD'), buttonElem];
+};
 
 const editPanel = (dispatch: Dispatch, context: Context) => {
     const { operation, registerSize } = context;
@@ -80,28 +105,28 @@ const editPanel = (dispatch: Dispatch, context: Context) => {
     const target = operation?.targets[0].qId;
     const controls = operation?.controls?.map((control) => control.qId);
     return [
-        _title('EDIT'),
+        title('EDIT'),
         _select('Target', 'target-input', options, target || 0, dispatch, operation),
         _checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
         _text('Display', 'display-input', dispatch, operation),
     ];
 };
 
-const _elem = (tag: string): HTMLElement => document.createElement(tag);
+const elem = (tag: string): HTMLElement => document.createElement(tag);
 
 /**
  * Append all child elements to a parent element
  */
-const _children = (parentElem: HTMLElement, childElems: HTMLElement[]) => {
+const children = (parentElem: HTMLElement, childElems: HTMLElement[]) => {
     childElems.map((elem) => parentElem.appendChild(elem));
     return parentElem;
 };
 
-const _title = (text: string) => {
-    const elem = _elem('h1');
-    elem.className = 'title';
-    elem.innerText = text;
-    return elem;
+const title = (text: string) => {
+    const titleElem = elem('h2');
+    titleElem.className = 'title';
+    titleElem.innerText = text;
+    return titleElem;
 };
 
 interface Option {
@@ -122,18 +147,18 @@ const _select = (
     operation?: Operation,
 ): HTMLElement => {
     const optionElems = options.map(({ value, text }) => _option(value, text));
-    const selectElem = _elem('select') as HTMLSelectElement;
-    _children(selectElem, optionElems);
+    const selectElem = elem('select') as HTMLSelectElement;
+    children(selectElem, optionElems);
     operation == undefined && selectElem.setAttribute('disabled', 'true');
     selectElem.selectedIndex = selectedIndex;
 
-    const labelElem = _elem('label') as HTMLLabelElement;
+    const labelElem = elem('label') as HTMLLabelElement;
     labelElem.className = 'block';
     labelElem.textContent = label;
 
-    const divElem = _elem('div') as HTMLDivElement;
+    const divElem = elem('div') as HTMLDivElement;
     divElem.className = className;
-    _children(divElem, [labelElem, selectElem]);
+    children(divElem, [labelElem, selectElem]);
 
     selectElem.onchange = () => {
         dispatch({ type: 'TARGET', payload: [{ qId: parseInt(selectElem.value) }] });
@@ -143,10 +168,10 @@ const _select = (
 };
 
 const _option = (value: string, text: string) => {
-    const elem = _elem('option') as HTMLOptionElement;
-    elem.value = value;
-    elem.textContent = text;
-    return elem;
+    const optionElem = elem('option') as HTMLOptionElement;
+    optionElem.value = value;
+    optionElem.textContent = text;
+    return optionElem;
 };
 
 const _checkboxes = (
@@ -174,34 +199,34 @@ const _checkboxes = (
         return elem;
     });
 
-    const labelElem = _elem('label');
+    const labelElem = elem('label');
     labelElem.className = 'block';
     labelElem.textContent = label;
 
-    const divElem = _elem('div') as HTMLDivElement;
+    const divElem = elem('div') as HTMLDivElement;
     divElem.className = className;
-    _children(divElem, [labelElem, ...checkboxElems]);
+    children(divElem, [labelElem, ...checkboxElems]);
 
     return divElem;
 };
 
 const _checkbox = (value: string, text: string) => {
-    const inputElem = _elem('input') as HTMLInputElement;
+    const inputElem = elem('input') as HTMLInputElement;
     inputElem.type = 'checkbox';
     inputElem.value = value;
 
-    const labelElem = _elem('label') as HTMLLabelElement;
+    const labelElem = elem('label') as HTMLLabelElement;
     labelElem.textContent = text;
     labelElem.prepend(inputElem);
     return labelElem;
 };
 
 const _text = (label: string, className: string, dispatch: Dispatch, operation?: Operation) => {
-    const labelElem = _elem('label') as HTMLLabelElement;
+    const labelElem = elem('label') as HTMLLabelElement;
     labelElem.className = 'block';
     labelElem.textContent = label;
 
-    const textElem = _elem('input') as HTMLInputElement;
+    const textElem = elem('input') as HTMLInputElement;
     operation == undefined && textElem.setAttribute('disabled', 'true');
     textElem.type = 'text';
     textElem.value = operation?.displayArgs || '';
@@ -211,9 +236,9 @@ const _text = (label: string, className: string, dispatch: Dispatch, operation?:
         dispatch({ type: 'DISPLAY_ARGS', payload: textElem.value });
     };
 
-    const divElem = _elem('div');
+    const divElem = elem('div');
     divElem.className = className;
-    _children(divElem, [labelElem, textElem]);
+    children(divElem, [labelElem, textElem]);
 
     return divElem;
 };
diff --git a/src/styles.ts b/src/styles.ts
index 764ed6e2..7fdde268 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -269,9 +269,10 @@ const _panel = `
         display: flex;
     }
     .panel {
+        min-width: 96px;
         display: flex;
         flex-direction: column;
-        border: solid 2px black;
+        border: solid 1.5px black;
         padding: 8px;
     }
     .title {

From b66957c4ee4eb5b243b309d80d205ca8d75746f8 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 20 Jul 2022 14:14:07 -0400
Subject: [PATCH 065/108] Allow toggling between Add or Edit Panel

---
 src/panel.ts | 96 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 67 insertions(+), 29 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 4f021993..5e846676 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -5,40 +5,57 @@ import { Register } from './register';
 import { Sqore } from './sqore';
 
 interface Context {
+    addMode: boolean;
     operations: Operation[];
     operation: Operation | undefined;
     registerSize: number;
 }
 
-let context: Context = {
+const context: Context = {
+    addMode: true,
     operations: [],
     operation: undefined,
     registerSize: 0,
 };
 
 const extensionPanel = (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
+    const dispatch = (action: Action) => {
+        update(action, context, useRefresh);
+
+        const panelElem = panel(dispatch, context);
+        const prevPanelElem = container.querySelector('.panel');
+
+        prevPanelElem && container.replaceChild(panelElem, prevPanelElem);
+    };
+
+    addEvents(dispatch, container, sqore);
+
+    const panelElem = panel(dispatch, context);
+    const prevPanelElem = container.querySelector('.panel');
+
+    prevPanelElem == null && container.prepend(panelElem);
+};
+
+const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) => {
     const elems = container.querySelectorAll<SVGElement>('[data-id]');
     elems.forEach((elem) =>
-        elem.addEventListener('mousedown', () => {
+        elem.addEventListener('mousedown', (ev: MouseEvent) => {
+            ev.stopImmediatePropagation();
             const dataId = elem.getAttribute('data-id');
             const operation = _equivOperation(dataId, sqore.circuit.operations);
-            context.operation = operation || undefined;
-            const newPanelElem = panel(dispatch, context);
-            container.replaceChild(newPanelElem, panelElem);
-            panelElem = newPanelElem;
+            dispatch({ type: 'OPERATION', payload: operation });
+            dispatch({ type: 'ADD_MODE', payload: false });
         }),
     );
+
     container.addEventListener('mouseover', () => {
         context.registerSize = sqore.circuit.qubits.length;
         context.operations = sqore.circuit.operations;
     });
 
-    const dispatch = update(context, useRefresh);
-    let panelElem = panel(dispatch, context);
-    const prevPanelElem = container.querySelector('.panel');
-    prevPanelElem //
-        ? container.replaceChild(panelElem, prevPanelElem)
-        : container.prepend(panelElem);
+    container.addEventListener('mousedown', () => {
+        dispatch({ type: 'ADD_MODE', payload: true });
+    });
 };
 
 interface Action {
@@ -46,10 +63,15 @@ interface Action {
     payload: unknown;
 }
 
-const update = (context: Context, useRefresh: () => void) => (action: Action) => {
+const update = (action: Action, context: Context, useRefresh: () => void) => {
     switch (action.type) {
+        case 'ADD_MODE': {
+            context.addMode = action.payload as boolean;
+            break;
+        }
         case 'OPERATION': {
             context.operation = action.payload as Operation;
+            break;
         }
         case 'TARGET': {
             const { operation } = context;
@@ -81,22 +103,35 @@ const update = (context: Context, useRefresh: () => void) => (action: Action) =>
 const panel = (dispatch: Dispatch, context: Context) => {
     const panelElem = elem('div');
     panelElem.className = 'panel';
-    children(panelElem, addPanel(dispatch, context));
-    // _children(panelElem, editPanel(dispatch, context));
+    children(
+        panelElem,
+        context.addMode //
+            ? addPanel(dispatch, context)
+            : editPanel(dispatch, context),
+    );
     return panelElem;
 };
 
 const addPanel = (dispatch: Dispatch, context: Context) => {
-    const buttonElem = elem('button') as HTMLButtonElement;
-    buttonElem.textContent = 'H';
-    buttonElem.addEventListener('mouseup', () => {
+    const hElem = elem('button') as HTMLButtonElement;
+    hElem.textContent = 'H';
+    const xElem = elem('button') as HTMLButtonElement;
+    xElem.textContent = 'X';
+    hElem.addEventListener('mouseup', () => {
         const operation = {
             gate: 'H',
             targets: [{ qId: 0 }],
         };
         dispatch({ type: 'ADD_OPERATION', payload: operation });
     });
-    return [title('ADD'), buttonElem];
+    xElem.addEventListener('mouseup', () => {
+        const operation = {
+            gate: 'X',
+            targets: [{ qId: 1 }],
+        };
+        dispatch({ type: 'ADD_OPERATION', payload: operation });
+    });
+    return [title('ADD'), hElem, xElem];
 };
 
 const editPanel = (dispatch: Dispatch, context: Context) => {
@@ -106,9 +141,9 @@ const editPanel = (dispatch: Dispatch, context: Context) => {
     const controls = operation?.controls?.map((control) => control.qId);
     return [
         title('EDIT'),
-        _select('Target', 'target-input', options, target || 0, dispatch, operation),
-        _checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
-        _text('Display', 'display-input', dispatch, operation),
+        select('Target', 'target-input', options, target || 0, dispatch, operation),
+        checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
+        text('Display', 'display-input', dispatch, operation),
     ];
 };
 
@@ -138,7 +173,7 @@ interface Dispatch {
     (action: Action): void;
 }
 
-const _select = (
+const select = (
     label: string,
     className: string,
     options: Option[],
@@ -146,7 +181,7 @@ const _select = (
     dispatch: Dispatch,
     operation?: Operation,
 ): HTMLElement => {
-    const optionElems = options.map(({ value, text }) => _option(value, text));
+    const optionElems = options.map(({ value, text }) => option(value, text));
     const selectElem = elem('select') as HTMLSelectElement;
     children(selectElem, optionElems);
     operation == undefined && selectElem.setAttribute('disabled', 'true');
@@ -167,14 +202,14 @@ const _select = (
     return divElem;
 };
 
-const _option = (value: string, text: string) => {
+const option = (value: string, text: string) => {
     const optionElem = elem('option') as HTMLOptionElement;
     optionElem.value = value;
     optionElem.textContent = text;
     return optionElem;
 };
 
-const _checkboxes = (
+const checkboxes = (
     label: string,
     className: string,
     options: Option[],
@@ -183,16 +218,19 @@ const _checkboxes = (
     operation?: Operation,
 ) => {
     const checkboxElems = options.map((option, index) => {
-        const elem = _checkbox(option.value, option.text);
+        const elem = checkbox(option.value, option.text);
         const inputElem = elem.querySelector('input') as HTMLInputElement;
         selectedIndexes.includes(index) && inputElem.setAttribute('checked', 'true');
         operation == undefined && inputElem.setAttribute('disabled', 'true');
 
         inputElem.onchange = () => {
+            // Get all checked options
             const checkedElems = Array.from(divElem.querySelectorAll<HTMLInputElement>('input:checked'));
+            // Generate new controls from checked options
             const newControls = checkedElems.map((elem) => ({
                 qId: parseInt(elem.value),
             }));
+            // Dispatch new controls
             dispatch({ type: 'CONTROLS', payload: newControls });
         };
 
@@ -210,7 +248,7 @@ const _checkboxes = (
     return divElem;
 };
 
-const _checkbox = (value: string, text: string) => {
+const checkbox = (value: string, text: string) => {
     const inputElem = elem('input') as HTMLInputElement;
     inputElem.type = 'checkbox';
     inputElem.value = value;
@@ -221,7 +259,7 @@ const _checkbox = (value: string, text: string) => {
     return labelElem;
 };
 
-const _text = (label: string, className: string, dispatch: Dispatch, operation?: Operation) => {
+const text = (label: string, className: string, dispatch: Dispatch, operation?: Operation) => {
     const labelElem = elem('label') as HTMLLabelElement;
     labelElem.className = 'block';
     labelElem.textContent = label;

From acb6bb89eff9a8eaf20aa813b6fee9151127258e Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 21 Jul 2022 11:10:44 -0400
Subject: [PATCH 066/108] Add basic functionalities with Add Panel

---
 src/panel.ts  | 81 ++++++++++++++++++++++++++++++++++++---------------
 src/sqore.ts  |  2 +-
 src/styles.ts | 14 ++++++++-
 3 files changed, 71 insertions(+), 26 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 5e846676..e054aabd 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -53,9 +53,11 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
         context.operations = sqore.circuit.operations;
     });
 
-    container.addEventListener('mousedown', () => {
-        dispatch({ type: 'ADD_MODE', payload: true });
-    });
+    const svgElem = container.querySelector('svg[id]');
+    svgElem &&
+        svgElem.addEventListener('mousedown', () => {
+            dispatch({ type: 'ADD_MODE', payload: true });
+        });
 };
 
 interface Action {
@@ -82,7 +84,11 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
         }
         case 'CONTROLS': {
             const { operation } = context;
-            operation && (operation.controls = action.payload as Register[]);
+            if (operation) {
+                const payload = action.payload as Register[];
+                operation.controls = payload;
+                operation.isControlled = payload.length > 0 ? true : false;
+            }
             useRefresh();
             break;
         }
@@ -113,25 +119,10 @@ const panel = (dispatch: Dispatch, context: Context) => {
 };
 
 const addPanel = (dispatch: Dispatch, context: Context) => {
-    const hElem = elem('button') as HTMLButtonElement;
-    hElem.textContent = 'H';
-    const xElem = elem('button') as HTMLButtonElement;
-    xElem.textContent = 'X';
-    hElem.addEventListener('mouseup', () => {
-        const operation = {
-            gate: 'H',
-            targets: [{ qId: 0 }],
-        };
-        dispatch({ type: 'ADD_OPERATION', payload: operation });
-    });
-    xElem.addEventListener('mouseup', () => {
-        const operation = {
-            gate: 'X',
-            targets: [{ qId: 1 }],
-        };
-        dispatch({ type: 'ADD_OPERATION', payload: operation });
-    });
-    return [title('ADD'), hElem, xElem];
+    const hGate = gate(dispatch, 'H');
+    const xGate = gate(dispatch, 'X');
+    const zzGate = gate(dispatch, 'ZZ');
+    return [title('ADD'), hGate, xGate, zzGate];
 };
 
 const editPanel = (dispatch: Dispatch, context: Context) => {
@@ -147,7 +138,11 @@ const editPanel = (dispatch: Dispatch, context: Context) => {
     ];
 };
 
-const elem = (tag: string): HTMLElement => document.createElement(tag);
+const elem = (tag: string, className?: string): HTMLElement => {
+    const _elem = document.createElement(tag);
+    _elem.className = className || '';
+    return _elem;
+};
 
 /**
  * Append all child elements to a parent element
@@ -281,4 +276,42 @@ const text = (label: string, className: string, dispatch: Dispatch, operation?:
     return divElem;
 };
 
+/**
+ * Generate gate element for Add Panel based on type of gate
+ * @param dispatch
+ * @param type i.e. 'H' or 'X'
+ */
+const gate = (dispatch: Dispatch, type: string) => {
+    const operation = defaultGates[type];
+    if (operation == null) throw new Error(`Gate ${type} not available`);
+    const divElem = elem('div', `panel-gate gate-${type}`);
+    divElem.addEventListener('mousedown', () => {
+        dispatch({ type: 'ADD_OPERATION', payload: operation });
+    });
+    divElem.textContent = type;
+    return divElem;
+};
+
+interface DefaultGates {
+    [index: string]: Operation;
+}
+
+const defaultGates: DefaultGates = {
+    H: {
+        gate: 'H',
+        targets: [{ qId: 0 }],
+    },
+    X: {
+        gate: 'X',
+        isControlled: true,
+        controls: [{ qId: 0 }],
+        targets: [{ qId: 1 }],
+    },
+    ZZ: {
+        gate: 'ZZ',
+        targets: [{ qId: 0 }, { qId: 1 }],
+        isControlled: true,
+    },
+};
+
 export { extensionPanel };
diff --git a/src/sqore.ts b/src/sqore.ts
index 0300c5d5..15d89f96 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -124,7 +124,7 @@ export class Sqore {
         // Create visualization components
         const composedSqore: ComposedSqore = this.compose(_circuit);
         const svg: SVGElement = this.generateSvg(composedSqore);
-        const previousSvg = container.querySelector('svg');
+        const previousSvg = container.querySelector('svg[id]');
         previousSvg == null //
             ? container.appendChild(svg)
             : container.replaceChild(svg, previousSvg);
diff --git a/src/styles.ts b/src/styles.ts
index 7fdde268..37fe5f29 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -269,7 +269,7 @@ const _panel = `
         display: flex;
     }
     .panel {
-        min-width: 96px;
+        width: 144px;
         display: flex;
         flex-direction: column;
         border: solid 1.5px black;
@@ -285,4 +285,16 @@ const _panel = `
     .target-input, .controls-input, .display-input {
         margin-bottom: 8px;
     }
+    .panel-gate {
+        width: 40px;
+        height: 40px;
+        border: solid 1px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        font-size: 14px;
+    }
+    .panel-gate.gate-H {
+        background-color: #D9F1FA;
+    }
 `;

From 59fd7e01ed347d71a9d4dd95cb4c31829fe40f07 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 25 Jul 2022 10:51:48 -0400
Subject: [PATCH 067/108] Basic drag and drop

---
 src/draggable.ts | 10 ++++++-
 src/panel.ts     | 68 +++++++++++++++++++++++++++++++++++++++---------
 src/styles.ts    | 17 +++++-------
 3 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index 93c43f3e..d358219c 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -531,4 +531,12 @@ const exportedForTesting = {
     _lastIndex,
 };
 
-export { extensionDraggable, Context, _equivOperation, _equivGateElem, exportedForTesting };
+export {
+    extensionDraggable,
+    Context,
+    _equivOperation,
+    _equivGateElem,
+    _equivParentArray,
+    _lastIndex,
+    exportedForTesting,
+};
diff --git a/src/panel.ts b/src/panel.ts
index e054aabd..6893e6cc 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,6 +1,7 @@
 import range from 'lodash/range';
+import cloneDeep from 'lodash/cloneDeep';
 import { Operation } from './circuit';
-import { _equivOperation } from './draggable';
+import { _equivOperation, _equivParentArray, _lastIndex } from './draggable';
 import { Register } from './register';
 import { Sqore } from './sqore';
 
@@ -9,6 +10,7 @@ interface Context {
     operations: Operation[];
     operation: Operation | undefined;
     registerSize: number;
+    container: HTMLElement | undefined;
 }
 
 const context: Context = {
@@ -16,6 +18,7 @@ const context: Context = {
     operations: [],
     operation: undefined,
     registerSize: 0,
+    container: undefined,
 };
 
 const extensionPanel = (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
@@ -51,6 +54,7 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
     container.addEventListener('mouseover', () => {
         context.registerSize = sqore.circuit.qubits.length;
         context.operations = sqore.circuit.operations;
+        context.container = container;
     });
 
     const svgElem = container.querySelector('svg[id]');
@@ -58,6 +62,22 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
         svgElem.addEventListener('mousedown', () => {
             dispatch({ type: 'ADD_MODE', payload: true });
         });
+
+    const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
+    const dropzoneElems = dropzoneLayer.querySelectorAll<SVGRectElement>('.dropzone');
+    dropzoneElems.forEach((dropzoneElem) =>
+        dropzoneElem.addEventListener('mouseup', () => {
+            if (
+                context.operation && //
+                context.addMode
+            ) {
+                const targetId = dropzoneElem.getAttribute('data-dropzone-id');
+                const targetWire = dropzoneElem.getAttribute('data-dropzone-wire');
+                dispatch({ type: 'ADD_OPERATION', payload: targetId });
+                dispatch({ type: 'TARGET', payload: [{ qId: parseInt(targetWire || '') }] });
+            }
+        }),
+    );
 };
 
 interface Action {
@@ -99,30 +119,48 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
             break;
         }
         case 'ADD_OPERATION': {
-            context.operations.push(action.payload as Operation);
+            const targetId = action.payload as string;
+            const targetOperationParent = _equivParentArray(targetId, context.operations);
+            const targetLastIndex = _lastIndex(targetId);
+            if (
+                targetOperationParent != null && //
+                targetLastIndex != null &&
+                context.operation != null
+            ) {
+                targetOperationParent.splice(targetLastIndex, 0, context.operation);
+            }
+            // context.operations.push(action.payload as Operation);
             useRefresh();
             break;
         }
+        case 'DROPZONE_LAYER': {
+            const isVisible = action.payload as boolean;
+            const { container } = context;
+            if (container) {
+                const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
+                isVisible ? (dropzoneLayer.style.display = 'block') : 'none';
+            }
+            break;
+        }
     }
 };
 
 const panel = (dispatch: Dispatch, context: Context) => {
     const panelElem = elem('div');
     panelElem.className = 'panel';
-    children(
-        panelElem,
+    children(panelElem, [
         context.addMode //
             ? addPanel(dispatch, context)
             : editPanel(dispatch, context),
-    );
+    ]);
     return panelElem;
 };
 
 const addPanel = (dispatch: Dispatch, context: Context) => {
+    const addPanelElem = elem('div', 'add-panel');
     const hGate = gate(dispatch, 'H');
-    const xGate = gate(dispatch, 'X');
-    const zzGate = gate(dispatch, 'ZZ');
-    return [title('ADD'), hGate, xGate, zzGate];
+    children(addPanelElem, [title('ADD'), hGate]);
+    return addPanelElem;
 };
 
 const editPanel = (dispatch: Dispatch, context: Context) => {
@@ -130,12 +168,15 @@ const editPanel = (dispatch: Dispatch, context: Context) => {
     const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
     const target = operation?.targets[0].qId;
     const controls = operation?.controls?.map((control) => control.qId);
-    return [
+
+    const editPanelElem = elem('div', 'edit-panel');
+    children(editPanelElem, [
         title('EDIT'),
         select('Target', 'target-input', options, target || 0, dispatch, operation),
         checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
         text('Display', 'display-input', dispatch, operation),
-    ];
+    ]);
+    return editPanelElem;
 };
 
 const elem = (tag: string, className?: string): HTMLElement => {
@@ -275,7 +316,6 @@ const text = (label: string, className: string, dispatch: Dispatch, operation?:
 
     return divElem;
 };
-
 /**
  * Generate gate element for Add Panel based on type of gate
  * @param dispatch
@@ -284,9 +324,11 @@ const text = (label: string, className: string, dispatch: Dispatch, operation?:
 const gate = (dispatch: Dispatch, type: string) => {
     const operation = defaultGates[type];
     if (operation == null) throw new Error(`Gate ${type} not available`);
-    const divElem = elem('div', `panel-gate gate-${type}`);
+    const divElem = elem('div', `add-panel-gate`);
     divElem.addEventListener('mousedown', () => {
-        dispatch({ type: 'ADD_OPERATION', payload: operation });
+        // dispatch({ type: 'ADD_OPERATION', payload: operation });
+        dispatch({ type: 'OPERATION', payload: cloneDeep(operation) });
+        dispatch({ type: 'DROPZONE_LAYER', payload: true });
     });
     divElem.textContent = type;
     return divElem;
diff --git a/src/styles.ts b/src/styles.ts
index 37fe5f29..82e00cf5 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -270,10 +270,12 @@ const _panel = `
     }
     .panel {
         width: 144px;
+        border: solid 1px black;
+        padding: 8px;
+    }
+    .edit-panel {
         display: flex;
         flex-direction: column;
-        border: solid 1.5px black;
-        padding: 8px;
     }
     .title {
         margin: 0 0 4px;
@@ -285,16 +287,9 @@ const _panel = `
     .target-input, .controls-input, .display-input {
         margin-bottom: 8px;
     }
-    .panel-gate {
+    .add-panel-gate {
         width: 40px;
         height: 40px;
-        border: solid 1px;
-        display: flex;
-        justify-content: center;
-        align-items: center;
-        font-size: 14px;
-    }
-    .panel-gate.gate-H {
-        background-color: #D9F1FA;
+        border: 1px solid black;
     }
 `;

From ea16116a89d135594a59a7f76100f0003278c300 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 26 Jul 2022 11:07:13 -0400
Subject: [PATCH 068/108] Basic Add Panel gate rendering

---
 src/draggable.ts | 29 ++++++++++++------
 src/panel.ts     | 80 ++++++++++++++++++++++++++++++++++++++++--------
 2 files changed, 88 insertions(+), 21 deletions(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index d358219c..9af6b9d9 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -27,7 +27,7 @@ interface Context {
  * @param onCircuitChange   User-provided callback function triggered when circuit is changed
  */
 const extensionDraggable = (container: HTMLElement, sqore: Sqore, useRender: () => void): void => {
-    const svg = container.querySelector('svg') as SVGElement;
+    const svg = container.querySelector('svg[id]') as SVGElement;
 
     const context: Context = {
         container: container,
@@ -80,11 +80,14 @@ const _wireYs = (elem: SVGGraphicsElement, wireData: number[]): number[] => {
  * Get list of host elements that dropzones can be attached to
  */
 const _hostElems = (container: HTMLElement): SVGGraphicsElement[] => {
-    return Array.from(
-        container.querySelectorAll<SVGGraphicsElement>(
-            '[class^="gate-"]:not(.gate-control, .gate-swap), .control-dot, .oplus, .cross',
-        ),
-    );
+    const svgElem = container.querySelector('svg[id]');
+    return svgElem != null
+        ? Array.from(
+              svgElem.querySelectorAll<SVGGraphicsElement>(
+                  '[class^="gate-"]:not(.gate-control, .gate-swap), .control-dot, .oplus, .cross',
+              ),
+          )
+        : [];
 };
 
 /**
@@ -137,7 +140,7 @@ const _dropzoneLayer = (context: Context) => {
 
         // Check to prevent group gates creating dropzones between wires
         if (wirePrefix) {
-            const prefixX = wirePrefix.prefixX;
+            const { prefixX } = wirePrefix;
             const elemDropzone = box(prefixX, cY - paddingY, cX - prefixX, paddingY * 2, 'dropzone');
             elemDropzone.setAttribute('data-dropzone-id', _equivDataId(elem) || '');
             elemDropzone.setAttribute('data-dropzone-wire', `${wirePrefix.index}`);
@@ -153,7 +156,7 @@ const _dropzoneLayer = (context: Context) => {
             wireYs.map((wireY) => {
                 const wirePrefix = wirePrefixes.find((item) => item.wireY === wireY);
                 if (wirePrefix) {
-                    const prefixX = wirePrefix.prefixX;
+                    const { prefixX } = wirePrefix;
                     const elemDropzone = box(prefixX, wireY - paddingY, x - prefixX, paddingY * 2, 'dropzone');
                     elemDropzone.setAttribute('data-dropzone-id', _equivDataId(elem) || '');
                     elemDropzone.setAttribute('data-dropzone-wire', `${wirePrefix.index}`);
@@ -222,7 +225,7 @@ const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElem
  * Add events for document
  */
 const _addDocumentEvents = (context: Context) => {
-    const { container } = context;
+    const { container, svg } = context;
 
     document.addEventListener('keydown', (ev: KeyboardEvent) => {
         if (ev.ctrlKey && context.selectedId) {
@@ -241,6 +244,14 @@ const _addDocumentEvents = (context: Context) => {
     document.addEventListener('mouseup', () => {
         container.classList.remove('moving', 'copying');
     });
+
+    document.addEventListener(
+        'contextmenu',
+        (ev: Event) => {
+            ev.preventDefault();
+        },
+        false,
+    );
 };
 
 /**
diff --git a/src/panel.ts b/src/panel.ts
index 6893e6cc..3efdc8b1 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,9 +1,13 @@
-import range from 'lodash/range';
 import cloneDeep from 'lodash/cloneDeep';
+import range from 'lodash/range';
 import { Operation } from './circuit';
+import { gateHeight, minGateWidth } from './constants';
 import { _equivOperation, _equivParentArray, _lastIndex } from './draggable';
+import { _formatGate } from './formatters/gateFormatter';
+import { GateType, Metadata } from './metadata';
 import { Register } from './register';
 import { Sqore } from './sqore';
+import { getGateWidth } from './utils';
 
 interface Context {
     addMode: boolean;
@@ -129,7 +133,6 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
             ) {
                 targetOperationParent.splice(targetLastIndex, 0, context.operation);
             }
-            // context.operations.push(action.payload as Operation);
             useRefresh();
             break;
         }
@@ -138,7 +141,7 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
             const { container } = context;
             if (container) {
                 const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
-                isVisible ? (dropzoneLayer.style.display = 'block') : 'none';
+                dropzoneLayer.style.display = isVisible ? 'block' : 'none';
             }
             break;
         }
@@ -158,8 +161,17 @@ const panel = (dispatch: Dispatch, context: Context) => {
 
 const addPanel = (dispatch: Dispatch, context: Context) => {
     const addPanelElem = elem('div', 'add-panel');
-    const hGate = gate(dispatch, 'H');
-    children(addPanelElem, [title('ADD'), hGate]);
+    const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+    svgElem.setAttribute('width', '144px');
+    svgElem.setAttribute('height', '100%');
+    const hGate = gate(dispatch, 'H', 0, 0);
+    const xGate = gate(dispatch, 'X', 50, 0);
+    const x2Gate = gate(dispatch, 'X', 100, 0);
+    svgElem.appendChild(hGate);
+    svgElem.appendChild(xGate);
+    svgElem.appendChild(x2Gate);
+    children(addPanelElem, [title('ADD')]);
+    addPanelElem.appendChild(svgElem);
     return addPanelElem;
 };
 
@@ -316,22 +328,67 @@ const text = (label: string, className: string, dispatch: Dispatch, operation?:
 
     return divElem;
 };
+
+const toMetadata = (operation: Operation | undefined, x: number, y: number): Metadata => {
+    const metadata: Metadata = {
+        type: GateType.Invalid,
+        x: x + minGateWidth / 2,
+        controlsY: [],
+        targetsY: [y + gateHeight / 2],
+        label: '',
+        width: -1,
+    };
+
+    if (operation == null) return metadata;
+
+    const {
+        gate,
+        displayArgs,
+        isMeasurement,
+        // isConditional,
+        isControlled,
+        // isAdjoint,
+        // conditionalRender,
+    } = operation;
+
+    if (isMeasurement) {
+        metadata.type = GateType.Measure;
+    } else if (gate === 'SWAP') {
+        metadata.type = GateType.Swap;
+    } else if (isControlled) {
+        metadata.type = gate === 'X' ? GateType.Cnot : GateType.ControlledUnitary;
+        metadata.label = gate;
+    } else if (gate === 'X') {
+        metadata.type = GateType.X;
+        metadata.label = gate;
+    } else {
+        metadata.type = GateType.Unitary;
+        metadata.label = gate;
+        metadata.targetsY = [[y + gateHeight / 2]];
+    }
+
+    if (displayArgs != null) metadata.displayArgs = displayArgs;
+    metadata.width = getGateWidth(metadata);
+
+    return metadata;
+};
+
 /**
  * Generate gate element for Add Panel based on type of gate
  * @param dispatch
  * @param type i.e. 'H' or 'X'
  */
-const gate = (dispatch: Dispatch, type: string) => {
+const gate = (dispatch: Dispatch, type: string, x: number, y: number) => {
     const operation = defaultGates[type];
     if (operation == null) throw new Error(`Gate ${type} not available`);
-    const divElem = elem('div', `add-panel-gate`);
-    divElem.addEventListener('mousedown', () => {
+    const metadata = toMetadata(operation, x, y);
+    const gateElem = _formatGate(metadata).cloneNode(true);
+    gateElem.addEventListener('mousedown', () => {
         // dispatch({ type: 'ADD_OPERATION', payload: operation });
         dispatch({ type: 'OPERATION', payload: cloneDeep(operation) });
         dispatch({ type: 'DROPZONE_LAYER', payload: true });
     });
-    divElem.textContent = type;
-    return divElem;
+    return gateElem;
 };
 
 interface DefaultGates {
@@ -346,8 +403,7 @@ const defaultGates: DefaultGates = {
     X: {
         gate: 'X',
         isControlled: true,
-        controls: [{ qId: 0 }],
-        targets: [{ qId: 1 }],
+        targets: [{ qId: 0 }],
     },
     ZZ: {
         gate: 'ZZ',

From ab19cd03cc6600c7b7ca33daf1796925f1dc9bbd Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 26 Jul 2022 11:22:20 -0400
Subject: [PATCH 069/108] Disable contextmenu & change cursor when dragging
 from Add Panel

---
 src/draggable.ts | 19 +++++++++++--------
 src/panel.ts     | 16 ++++++++++------
 2 files changed, 21 insertions(+), 14 deletions(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index 9af6b9d9..302d2549 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -7,6 +7,7 @@ import { Operation } from './circuit';
 import { box } from './formatters/formatUtils';
 import { Register } from './register';
 import { Sqore } from './sqore';
+import { panelContext } from './panel';
 
 interface Context {
     container: HTMLElement;
@@ -225,7 +226,7 @@ const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElem
  * Add events for document
  */
 const _addDocumentEvents = (context: Context) => {
-    const { container, svg } = context;
+    const { container } = context;
 
     document.addEventListener('keydown', (ev: KeyboardEvent) => {
         if (ev.ctrlKey && context.selectedId) {
@@ -244,14 +245,15 @@ const _addDocumentEvents = (context: Context) => {
     document.addEventListener('mouseup', () => {
         container.classList.remove('moving', 'copying');
     });
+};
 
-    document.addEventListener(
-        'contextmenu',
-        (ev: Event) => {
-            ev.preventDefault();
-        },
-        false,
-    );
+/**
+ * Disable contextmenu default behaviors
+ */
+const _addContextMenuEvent = (container: HTMLElement) => {
+    container.addEventListener('contextmenu', (ev: MouseEvent) => {
+        ev.preventDefault();
+    });
 };
 
 /**
@@ -261,6 +263,7 @@ const _addEvents = (context: Context) => {
     const { container, operations, renderFn } = context;
     const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
 
+    _addContextMenuEvent(container);
     _addDropzoneLayerEvents(container, dropzoneLayer);
     _addDocumentEvents(context);
 
diff --git a/src/panel.ts b/src/panel.ts
index 3efdc8b1..7a6ea828 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -86,7 +86,7 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
 
 interface Action {
     type: string;
-    payload: unknown;
+    payload?: unknown;
 }
 
 const update = (action: Action, context: Context, useRefresh: () => void) => {
@@ -136,15 +136,18 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
             useRefresh();
             break;
         }
-        case 'DROPZONE_LAYER': {
-            const isVisible = action.payload as boolean;
+        case 'DISPLAY_DROPZONE_LAYER': {
             const { container } = context;
             if (container) {
                 const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
-                dropzoneLayer.style.display = isVisible ? 'block' : 'none';
+                dropzoneLayer.style.display = 'block';
             }
             break;
         }
+        case 'DISPLAY_CURSOR_MOVING': {
+            const { container } = context;
+            container && container.classList.add('moving');
+        }
     }
 };
 
@@ -386,7 +389,8 @@ const gate = (dispatch: Dispatch, type: string, x: number, y: number) => {
     gateElem.addEventListener('mousedown', () => {
         // dispatch({ type: 'ADD_OPERATION', payload: operation });
         dispatch({ type: 'OPERATION', payload: cloneDeep(operation) });
-        dispatch({ type: 'DROPZONE_LAYER', payload: true });
+        dispatch({ type: 'DISPLAY_DROPZONE_LAYER' });
+        dispatch({ type: 'DISPLAY_CURSOR_MOVING', payload: true });
     });
     return gateElem;
 };
@@ -412,4 +416,4 @@ const defaultGates: DefaultGates = {
     },
 };
 
-export { extensionPanel };
+export { extensionPanel, context as panelContext };

From 74f2f5f9a3827588abf4be4fbaaf5996635a057f Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 26 Jul 2022 11:29:13 -0400
Subject: [PATCH 070/108] Remove unnecessary export

---
 src/draggable.ts | 1 -
 src/panel.ts     | 2 +-
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index 302d2549..10ece347 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -7,7 +7,6 @@ import { Operation } from './circuit';
 import { box } from './formatters/formatUtils';
 import { Register } from './register';
 import { Sqore } from './sqore';
-import { panelContext } from './panel';
 
 interface Context {
     container: HTMLElement;
diff --git a/src/panel.ts b/src/panel.ts
index 7a6ea828..bbba259c 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -416,4 +416,4 @@ const defaultGates: DefaultGates = {
     },
 };
 
-export { extensionPanel, context as panelContext };
+export { extensionPanel };

From b1e06d382c97cccc8d449956236f5709042cacb7 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 26 Jul 2022 11:32:13 -0400
Subject: [PATCH 071/108] Add rendering padding & comments

---
 src/panel.ts | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index bbba259c..b615e77f 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -335,9 +335,9 @@ const text = (label: string, className: string, dispatch: Dispatch, operation?:
 const toMetadata = (operation: Operation | undefined, x: number, y: number): Metadata => {
     const metadata: Metadata = {
         type: GateType.Invalid,
-        x: x + minGateWidth / 2,
+        x: x + 1 + minGateWidth / 2, // offset by 1 for left padding
         controlsY: [],
-        targetsY: [y + gateHeight / 2],
+        targetsY: [y + 1 + gateHeight / 2], // offset by 1 for top padding
         label: '',
         width: -1,
     };
@@ -367,7 +367,8 @@ const toMetadata = (operation: Operation | undefined, x: number, y: number): Met
     } else {
         metadata.type = GateType.Unitary;
         metadata.label = gate;
-        metadata.targetsY = [[y + gateHeight / 2]];
+        metadata.targetsY = [[y + 1 + gateHeight / 2]];
+        // GateType.Unitary wants matrix array. Also, offset by 1 for top padding
     }
 
     if (displayArgs != null) metadata.displayArgs = displayArgs;

From cbc1a5f48d8f0f31e3b2675b91167d2168490a91 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 27 Jul 2022 08:57:27 -0400
Subject: [PATCH 072/108] Let usePanel accepts user options

---
 src/panel.ts | 45 ++++++++++++++++++++++++++-------------------
 src/sqore.ts | 10 +++++-----
 2 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index b615e77f..fc0223f9 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -25,23 +25,30 @@ const context: Context = {
     container: undefined,
 };
 
-const extensionPanel = (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
-    const dispatch = (action: Action) => {
-        update(action, context, useRefresh);
+interface PanelOptions {
+    gateDictionary?: GateDictionary;
+    displaySize?: number;
+}
 
-        const panelElem = panel(dispatch, context);
-        const prevPanelElem = container.querySelector('.panel');
+const extensionPanel =
+    (options?: PanelOptions) =>
+    (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
+        const dispatch = (action: Action) => {
+            update(action, context, useRefresh);
 
-        prevPanelElem && container.replaceChild(panelElem, prevPanelElem);
-    };
+            const panelElem = panel(dispatch, context, options);
+            const prevPanelElem = container.querySelector('.panel');
+
+            prevPanelElem && container.replaceChild(panelElem, prevPanelElem);
+        };
 
-    addEvents(dispatch, container, sqore);
+        addEvents(dispatch, container, sqore);
 
-    const panelElem = panel(dispatch, context);
-    const prevPanelElem = container.querySelector('.panel');
+        const panelElem = panel(dispatch, context, options);
+        const prevPanelElem = container.querySelector('.panel');
 
-    prevPanelElem == null && container.prepend(panelElem);
-};
+        prevPanelElem == null && container.prepend(panelElem);
+    };
 
 const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) => {
     const elems = container.querySelectorAll<SVGElement>('[data-id]');
@@ -151,18 +158,18 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
     }
 };
 
-const panel = (dispatch: Dispatch, context: Context) => {
+const panel = (dispatch: Dispatch, context: Context, options?: PanelOptions) => {
     const panelElem = elem('div');
     panelElem.className = 'panel';
     children(panelElem, [
         context.addMode //
-            ? addPanel(dispatch, context)
+            ? addPanel(dispatch, context, options)
             : editPanel(dispatch, context),
     ]);
     return panelElem;
 };
 
-const addPanel = (dispatch: Dispatch, context: Context) => {
+const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions) => {
     const addPanelElem = elem('div', 'add-panel');
     const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
     svgElem.setAttribute('width', '144px');
@@ -383,7 +390,7 @@ const toMetadata = (operation: Operation | undefined, x: number, y: number): Met
  * @param type i.e. 'H' or 'X'
  */
 const gate = (dispatch: Dispatch, type: string, x: number, y: number) => {
-    const operation = defaultGates[type];
+    const operation = gateDictionary[type];
     if (operation == null) throw new Error(`Gate ${type} not available`);
     const metadata = toMetadata(operation, x, y);
     const gateElem = _formatGate(metadata).cloneNode(true);
@@ -396,11 +403,11 @@ const gate = (dispatch: Dispatch, type: string, x: number, y: number) => {
     return gateElem;
 };
 
-interface DefaultGates {
+interface GateDictionary {
     [index: string]: Operation;
 }
 
-const defaultGates: DefaultGates = {
+const gateDictionary: GateDictionary = {
     H: {
         gate: 'H',
         targets: [{ qId: 0 }],
@@ -417,4 +424,4 @@ const defaultGates: DefaultGates = {
     },
 };
 
-export { extensionPanel };
+export { extensionPanel, PanelOptions };
diff --git a/src/sqore.ts b/src/sqore.ts
index 15d89f96..1a850113 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -4,7 +4,7 @@
 import { Circuit, ConditionalRender, Operation } from './circuit';
 import { svgNS } from './constants';
 import { extensionDraggable } from './draggable';
-import { extensionPanel } from './panel';
+import { extensionPanel, PanelOptions } from './panel';
 import { formatGates } from './formatters/gateFormatter';
 import { formatInputs } from './formatters/inputFormatter';
 import { formatRegisters } from './formatters/registerFormatter';
@@ -373,12 +373,12 @@ export class Sqore {
     }
 
     public useDraggable(): Sqore {
-        this.extensions.push(extensionDraggable);
+        this.extensions = [...this.extensions, extensionDraggable];
         return this;
     }
 
-    public usePanel(): Sqore {
-        this.extensions.push(extensionPanel);
+    public usePanel(options?: PanelOptions): Sqore {
+        this.extensions = [...this.extensions, extensionPanel(options)];
         return this;
     }
 
@@ -388,7 +388,7 @@ export class Sqore {
             _sqore: Sqore,
             _useRender: () => void,
         ) => callback(this.circuit);
-        this.extensions.push(extensionOnCircuitChange);
+        this.extensions = [...this.extensions, extensionOnCircuitChange];
         return this;
     }
 }

From c82fd00e92bad74247dd3feec74339b59eaad1d8 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 27 Jul 2022 09:22:59 -0400
Subject: [PATCH 073/108] Rename useRender to useRefresh

---
 src/draggable.ts | 4 ++--
 src/sqore.ts     | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index 10ece347..46cae392 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -26,7 +26,7 @@ interface Context {
  * @param sqore             Sqore object
  * @param onCircuitChange   User-provided callback function triggered when circuit is changed
  */
-const extensionDraggable = (container: HTMLElement, sqore: Sqore, useRender: () => void): void => {
+const extensionDraggable = (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
     const svg = container.querySelector('svg[id]') as SVGElement;
 
     const context: Context = {
@@ -34,7 +34,7 @@ const extensionDraggable = (container: HTMLElement, sqore: Sqore, useRender: ()
         svg,
         operations: sqore.circuit.operations,
         wireData: _wireData(container),
-        renderFn: useRender,
+        renderFn: useRefresh,
         paddingY: 20,
         selectedId: null,
         selectedWire: null,
diff --git a/src/sqore.ts b/src/sqore.ts
index 1a850113..600a9b27 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -34,7 +34,7 @@ type GateRegistry = {
 };
 
 type Extension = {
-    (container: HTMLElement, sqore: Sqore, useRender: () => void): void;
+    (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void;
 };
 
 /**
@@ -130,7 +130,7 @@ export class Sqore {
             : container.replaceChild(svg, previousSvg);
         this.addGateClickHandlers(container, _circuit);
 
-        // Run extensions after every render or re-render
+        // Run extensions after every render or refresh
         const extensions = this.extensions;
         extensions != null &&
             extensions.map((extension) => extension(container, this, () => this.renderCircuit(container)));
@@ -386,7 +386,7 @@ export class Sqore {
         const extensionOnCircuitChange = (
             _container: HTMLElement, //
             _sqore: Sqore,
-            _useRender: () => void,
+            _useRefresh: () => void,
         ) => callback(this.circuit);
         this.extensions = [...this.extensions, extensionOnCircuitChange];
         return this;

From 094fe7b95e71a5abd530eabaa2f8a9813d4188ed Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Wed, 27 Jul 2022 10:41:38 -0400
Subject: [PATCH 074/108] Enable ghost element when dragging

---
 src/panel.ts  | 56 ++++++++++++++++++++++++++++++++++++++++++++++++---
 src/styles.ts | 10 +++++++++
 2 files changed, 63 insertions(+), 3 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index fc0223f9..e3688f5e 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -89,6 +89,10 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
             }
         }),
     );
+
+    document.addEventListener('mouseup', () => {
+        dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
+    });
 };
 
 interface Action {
@@ -154,6 +158,44 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
         case 'DISPLAY_CURSOR_MOVING': {
             const { container } = context;
             container && container.classList.add('moving');
+            break;
+        }
+        case 'DISPLAY_GHOST_ELEMENT': {
+            const handleMouseMove = (ev: MouseEvent) => {
+                divElem.style.left = `${ev.clientX - minGateWidth / 2}px`;
+                divElem.style.top = `${ev.clientY - gateHeight / 2}px`;
+            };
+
+            const { container } = context;
+            const { ghostElem, initX, initY } = action.payload as {
+                ghostElem: SVGGraphicsElement;
+                initX: number;
+                initY: number;
+            };
+
+            // Generate svg element to wrap around ghost element
+            const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+            svgElem.append(ghostElem);
+
+            // Generate div element to wrap around svg element
+            const divElem = elem('div', 'ghost');
+            divElem.style.left = `${initX - minGateWidth / 2}px`;
+            divElem.style.top = `${initY - gateHeight / 2}px`;
+            divElem.appendChild(svgElem);
+
+            if (container) {
+                container.appendChild(divElem);
+                container.addEventListener('mousemove', handleMouseMove);
+            }
+
+            break;
+        }
+        case 'REMOVE_GHOST_ELEMENT': {
+            const { container } = context;
+            if (container) {
+                const ghostElem = container.querySelector('.ghost');
+                ghostElem && container.removeChild(ghostElem);
+            }
         }
     }
 };
@@ -393,13 +435,21 @@ const gate = (dispatch: Dispatch, type: string, x: number, y: number) => {
     const operation = gateDictionary[type];
     if (operation == null) throw new Error(`Gate ${type} not available`);
     const metadata = toMetadata(operation, x, y);
-    const gateElem = _formatGate(metadata).cloneNode(true);
-    gateElem.addEventListener('mousedown', () => {
-        // dispatch({ type: 'ADD_OPERATION', payload: operation });
+    const gateElem = _formatGate(metadata).cloneNode(true) as SVGElement;
+    gateElem.addEventListener('mousedown', (ev: MouseEvent) => {
+        // Generate equivalent ghost element with x and y at 0
+        const ghostMetadata = toMetadata(operation, 0, 0);
+        const ghostElem = _formatGate(ghostMetadata).cloneNode(true);
+        // Get initial x and y position from 'mousedown' event
+        const { clientX: initX, clientY: initY } = ev;
+
+        // Dispatch relevant events
         dispatch({ type: 'OPERATION', payload: cloneDeep(operation) });
         dispatch({ type: 'DISPLAY_DROPZONE_LAYER' });
         dispatch({ type: 'DISPLAY_CURSOR_MOVING', payload: true });
+        dispatch({ type: 'DISPLAY_GHOST_ELEMENT', payload: { ghostElem, initX, initY } });
     });
+
     return gateElem;
 };
 
diff --git a/src/styles.ts b/src/styles.ts
index 82e00cf5..86a341d3 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -1,6 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
+import { gateHeight, minGateWidth } from './constants';
+
 /**
  * Provides configuration for CSS styles of visualization.
  */
@@ -280,6 +282,7 @@ const _panel = `
     .title {
         margin: 0 0 4px;
         font-size: 1.2rem;
+        user-select: none;
     }
     .block {
         display: block;
@@ -292,4 +295,11 @@ const _panel = `
         height: 40px;
         border: 1px solid black;
     }
+    .ghost {
+        width: ${minGateWidth}px;
+        height: ${gateHeight}px;
+        position: absolute;
+        z-index: 1;
+        pointer-events: none;
+    }
 `;

From e1f0d2134380a7b4d587f44dfe947a0886af1bca Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 28 Jul 2022 10:45:33 -0400
Subject: [PATCH 075/108] Add `options` functionalities in Add Panel

---
 example/script.js |  6 +----
 src/constants.ts  |  6 +++++
 src/panel.ts      | 65 +++++++++++++++++++++++++++++++++--------------
 src/styles.ts     |  8 ++++--
 4 files changed, 59 insertions(+), 26 deletions(-)

diff --git a/example/script.js b/example/script.js
index d65eb0d7..69b5639a 100644
--- a/example/script.js
+++ b/example/script.js
@@ -9,16 +9,12 @@ if (typeof qviz != 'undefined') {
     if (entangleDiv != null) {
         qviz.create(entangle) //
             .useDraggable()
-            .usePanel()
+            .usePanel({ displaySize: 3 })
             .draw(entangleDiv);
     }
 
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
-        /* Pass in isEditable = true to allow circuit to be editable */
-        /* Pass in onCircuitChange to trigger callback function
-           whenever there is a change in circuit */
-        // qviz.draw(sample, sampleDiv, qviz.STYLES['Default'], 0, isEditable, onCircuitChange);
         qviz.create(sample) //
             .useDraggable()
             .usePanel()
diff --git a/src/constants.ts b/src/constants.ts
index 2ccb9e33..3cfb3579 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -37,3 +37,9 @@ export const labelFontSize = 14;
 export const argsFontSize = 12;
 /** Starting x coord for each register wire. */
 export const regLineStart = 40;
+
+// Panel
+/** Panel width */
+export const panelWidth = 144;
+/** Gap between gates in Add Panel */
+export const gateGap = 50;
diff --git a/src/panel.ts b/src/panel.ts
index e3688f5e..70a25038 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,7 +1,7 @@
 import cloneDeep from 'lodash/cloneDeep';
 import range from 'lodash/range';
 import { Operation } from './circuit';
-import { gateHeight, minGateWidth } from './constants';
+import { gateGap, gateHeight, minGateWidth, panelWidth } from './constants';
 import { _equivOperation, _equivParentArray, _lastIndex } from './draggable';
 import { _formatGate } from './formatters/gateFormatter';
 import { GateType, Metadata } from './metadata';
@@ -26,8 +26,8 @@ const context: Context = {
 };
 
 interface PanelOptions {
-    gateDictionary?: GateDictionary;
     displaySize?: number;
+    gateDictionary?: GateDictionary;
 }
 
 const extensionPanel =
@@ -90,9 +90,10 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
         }),
     );
 
-    document.addEventListener('mouseup', () => {
-        dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
-    });
+    svgElem &&
+        svgElem.addEventListener('mouseup', () => {
+            dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
+        });
 };
 
 interface Action {
@@ -212,18 +213,37 @@ const panel = (dispatch: Dispatch, context: Context, options?: PanelOptions) =>
 };
 
 const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions) => {
-    const addPanelElem = elem('div', 'add-panel');
+    let gateDictionary = defaultGateDictionary;
+    let objectKeys = Object.keys(gateDictionary);
+    if (options != null) {
+        const { displaySize, gateDictionary: optionGateDictionary } = options;
+        displaySize && (objectKeys = objectKeys.slice(0, displaySize));
+        optionGateDictionary && (gateDictionary = optionGateDictionary);
+    }
+
+    let prefixX = 0;
+    let prefixY = 0;
+    const gateElems = objectKeys.map((key) => {
+        const gateElem = gate(dispatch, gateDictionary, key.toString(), prefixX, prefixY);
+        if (prefixX + gateGap > panelWidth) {
+            prefixX = 0;
+            prefixY += gateGap;
+        } else {
+            prefixX += gateGap;
+        }
+        return gateElem;
+    });
+
+    // Generate svg container to store gate elements
     const svgElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
-    svgElem.setAttribute('width', '144px');
-    svgElem.setAttribute('height', '100%');
-    const hGate = gate(dispatch, 'H', 0, 0);
-    const xGate = gate(dispatch, 'X', 50, 0);
-    const x2Gate = gate(dispatch, 'X', 100, 0);
-    svgElem.appendChild(hGate);
-    svgElem.appendChild(xGate);
-    svgElem.appendChild(x2Gate);
+    svgElem.classList.add('add-panel-svg');
+    childrenSvg(svgElem, gateElems);
+
+    // Generate add panel
+    const addPanelElem = elem('div', 'add-panel');
     children(addPanelElem, [title('ADD')]);
     addPanelElem.appendChild(svgElem);
+
     return addPanelElem;
 };
 
@@ -257,6 +277,11 @@ const children = (parentElem: HTMLElement, childElems: HTMLElement[]) => {
     return parentElem;
 };
 
+const childrenSvg = (parentElem: SVGElement, childElems: SVGElement[]) => {
+    childElems.map((elem) => parentElem.appendChild(elem));
+    return parentElem;
+};
+
 const title = (text: string) => {
     const titleElem = elem('h2');
     titleElem.className = 'title';
@@ -431,7 +456,7 @@ const toMetadata = (operation: Operation | undefined, x: number, y: number): Met
  * @param dispatch
  * @param type i.e. 'H' or 'X'
  */
-const gate = (dispatch: Dispatch, type: string, x: number, y: number) => {
+const gate = (dispatch: Dispatch, gateDictionary: GateDictionary, type: string, x: number, y: number) => {
     const operation = gateDictionary[type];
     if (operation == null) throw new Error(`Gate ${type} not available`);
     const metadata = toMetadata(operation, x, y);
@@ -457,20 +482,22 @@ interface GateDictionary {
     [index: string]: Operation;
 }
 
-const gateDictionary: GateDictionary = {
+const defaultGateDictionary: GateDictionary = {
+    RX: {
+        gate: 'RX',
+        targets: [{ qId: 0 }],
+    },
     H: {
         gate: 'H',
         targets: [{ qId: 0 }],
     },
     X: {
         gate: 'X',
-        isControlled: true,
         targets: [{ qId: 0 }],
     },
     ZZ: {
         gate: 'ZZ',
-        targets: [{ qId: 0 }, { qId: 1 }],
-        isControlled: true,
+        targets: [{ qId: 0 }],
     },
 };
 
diff --git a/src/styles.ts b/src/styles.ts
index 86a341d3..68ee107e 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import { gateHeight, minGateWidth } from './constants';
+import { gateHeight, minGateWidth, panelWidth } from './constants';
 
 /**
  * Provides configuration for CSS styles of visualization.
@@ -271,7 +271,7 @@ const _panel = `
         display: flex;
     }
     .panel {
-        width: 144px;
+        width: ${panelWidth};
         border: solid 1px black;
         padding: 8px;
     }
@@ -295,6 +295,10 @@ const _panel = `
         height: 40px;
         border: 1px solid black;
     }
+    .add-panel-svg {
+        width: ${panelWidth}px;
+        height: 100%;
+    }
     .ghost {
         width: ${minGateWidth}px;
         height: ${gateHeight}px;

From 8fa5804f14c458cba2bb97530b22412bb1b8d18c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 28 Jul 2022 11:12:24 -0400
Subject: [PATCH 076/108] Refactor payload for addMode & cancel event when
 dragging

---
 src/panel.ts | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 70a25038..1bbcdb05 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -58,7 +58,7 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
             const dataId = elem.getAttribute('data-id');
             const operation = _equivOperation(dataId, sqore.circuit.operations);
             dispatch({ type: 'OPERATION', payload: operation });
-            dispatch({ type: 'ADD_MODE', payload: false });
+            dispatch({ type: 'EDIT_MODE' });
         }),
     );
 
@@ -69,10 +69,9 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
     });
 
     const svgElem = container.querySelector('svg[id]');
-    svgElem &&
-        svgElem.addEventListener('mousedown', () => {
-            dispatch({ type: 'ADD_MODE', payload: true });
-        });
+    svgElem?.addEventListener('mousedown', () => {
+        dispatch({ type: 'ADD_MODE' });
+    });
 
     const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
     const dropzoneElems = dropzoneLayer.querySelectorAll<SVGRectElement>('.dropzone');
@@ -90,10 +89,12 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
         }),
     );
 
-    svgElem &&
-        svgElem.addEventListener('mouseup', () => {
-            dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
-        });
+    svgElem?.addEventListener('mouseup', () => {
+        dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
+    });
+    container.querySelector('.add-panel')?.addEventListener('mouseup', () => {
+        dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
+    });
 };
 
 interface Action {
@@ -104,7 +105,11 @@ interface Action {
 const update = (action: Action, context: Context, useRefresh: () => void) => {
     switch (action.type) {
         case 'ADD_MODE': {
-            context.addMode = action.payload as boolean;
+            context.addMode = true;
+            break;
+        }
+        case 'EDIT_MODE': {
+            context.addMode = false;
             break;
         }
         case 'OPERATION': {

From 56285c3a5758864cf129ece0ee3f934353f2a3dd Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 28 Jul 2022 11:32:26 -0400
Subject: [PATCH 077/108] Add more gates into defaultGateDictionary

---
 src/panel.ts | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/src/panel.ts b/src/panel.ts
index 1bbcdb05..5fb255bd 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -492,6 +492,14 @@ const defaultGateDictionary: GateDictionary = {
         gate: 'RX',
         targets: [{ qId: 0 }],
     },
+    RY: {
+        gate: 'RY',
+        targets: [{ qId: 0 }],
+    },
+    RZ: {
+        gate: 'RZ',
+        targets: [{ qId: 0 }],
+    },
     H: {
         gate: 'H',
         targets: [{ qId: 0 }],
@@ -500,6 +508,22 @@ const defaultGateDictionary: GateDictionary = {
         gate: 'X',
         targets: [{ qId: 0 }],
     },
+    S: {
+        gate: 'S',
+        targets: [{ qId: 0 }],
+    },
+    T: {
+        gate: 'T',
+        targets: [{ qId: 0 }],
+    },
+    Y: {
+        gate: 'Y',
+        targets: [{ qId: 0 }],
+    },
+    Z: {
+        gate: 'Z',
+        targets: [{ qId: 0 }],
+    },
     ZZ: {
         gate: 'ZZ',
         targets: [{ qId: 0 }],

From b4d5127749b7b2c35f30c819970b0773e5d75140 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 28 Jul 2022 14:55:21 -0400
Subject: [PATCH 078/108] Support Entangle in Add Panel

---
 src/constants.ts |  3 ++-
 src/panel.ts     | 18 ++++++++++++------
 2 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/src/constants.ts b/src/constants.ts
index 3cfb3579..71123456 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -42,4 +42,5 @@ export const regLineStart = 40;
 /** Panel width */
 export const panelWidth = 144;
 /** Gap between gates in Add Panel */
-export const gateGap = 50;
+export const horizontalGap = 10;
+export const verticalGap = 10;
diff --git a/src/panel.ts b/src/panel.ts
index 5fb255bd..13956692 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,7 +1,7 @@
 import cloneDeep from 'lodash/cloneDeep';
 import range from 'lodash/range';
 import { Operation } from './circuit';
-import { gateGap, gateHeight, minGateWidth, panelWidth } from './constants';
+import { gateHeight, horizontalGap, minGateWidth, panelWidth, verticalGap } from './constants';
 import { _equivOperation, _equivParentArray, _lastIndex } from './draggable';
 import { _formatGate } from './formatters/gateFormatter';
 import { GateType, Metadata } from './metadata';
@@ -229,13 +229,13 @@ const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions)
     let prefixX = 0;
     let prefixY = 0;
     const gateElems = objectKeys.map((key) => {
-        const gateElem = gate(dispatch, gateDictionary, key.toString(), prefixX, prefixY);
-        if (prefixX + gateGap > panelWidth) {
+        const { width: gateWidth } = toMetadata(gateDictionary[key], 0, 0);
+        if (prefixX + gateWidth + horizontalGap > panelWidth) {
             prefixX = 0;
-            prefixY += gateGap;
-        } else {
-            prefixX += gateGap;
+            prefixY += gateHeight + verticalGap;
         }
+        const gateElem = gate(dispatch, gateDictionary, key.toString(), prefixX, prefixY);
+        prefixX += gateWidth + horizontalGap;
         return gateElem;
     });
 
@@ -451,7 +451,9 @@ const toMetadata = (operation: Operation | undefined, x: number, y: number): Met
     }
 
     if (displayArgs != null) metadata.displayArgs = displayArgs;
+
     metadata.width = getGateWidth(metadata);
+    metadata.x = x + 1 + metadata.width / 2; // offset by 1 for left padding
 
     return metadata;
 };
@@ -488,6 +490,10 @@ interface GateDictionary {
 }
 
 const defaultGateDictionary: GateDictionary = {
+    Entangle: {
+        gate: 'Entangle',
+        targets: [{ qId: 0 }],
+    },
     RX: {
         gate: 'RX',
         targets: [{ qId: 0 }],

From 961963c93d491dc5800f6a78e6d957f83488025e Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 28 Jul 2022 16:57:30 -0400
Subject: [PATCH 079/108] Fix selector bug

---
 src/draggable.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index 46cae392..2b678be1 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -187,7 +187,7 @@ const _dropzoneLayer = (context: Context) => {
  */
 const _wireData = (container: HTMLElement): number[] => {
     // elems include qubit wires and lines of measure gates
-    const elems = container.querySelectorAll<SVGGElement>('svg > g:nth-child(3) > g');
+    const elems = container.querySelectorAll<SVGGElement>('svg[id] > g:nth-child(3) > g');
     // filter out <g> elements having more than 2 elements because
     // qubit wires contain only 2 elements: <line> and <text>
     // lines of measure gates contain 4 <line> elements

From 54b66fdaa1ba84aa692d167f2a87aa8f7efb36ac Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Sat, 30 Jul 2022 14:21:58 -0400
Subject: [PATCH 080/108] Rename editable to draggable tests

---
 __tests__/{editable.test.ts => draggable.test.ts} | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)
 rename __tests__/{editable.test.ts => draggable.test.ts} (98%)

diff --git a/__tests__/editable.test.ts b/__tests__/draggable.test.ts
similarity index 98%
rename from __tests__/editable.test.ts
rename to __tests__/draggable.test.ts
index 0fde4705..a923d8cb 100644
--- a/__tests__/editable.test.ts
+++ b/__tests__/draggable.test.ts
@@ -1,8 +1,8 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import { exportedForTesting } from '../src/editable';
-import { Circuit, draw, Operation, STYLES } from '../src/index';
+import { exportedForTesting } from '../src/draggable';
+import { Circuit, create, Operation } from '../src/index';
 
 const {
     _wireYs,
@@ -49,7 +49,7 @@ describe('Test _hostElems', () => {
                 },
             ],
         };
-        draw(circuit, container, STYLES['default']);
+        create(circuit).draw(container);
     });
     test('should return 4 elements', () => {
         expect(_hostElems(container)).toMatchSnapshot();
@@ -142,7 +142,7 @@ describe('Test _wireData', () => {
                 },
             ],
         };
-        draw(circuit, container, STYLES['default']);
+        create(circuit).draw(container);
         expect(_wireData(container)).toStrictEqual([40, 100]);
     });
     test('3 wires should return [40,100, 180]', () => {
@@ -168,7 +168,7 @@ describe('Test _wireData', () => {
                 },
             ],
         };
-        draw(circuit, container, STYLES['default']);
+        create(circuit).draw(container);
         expect(_wireData(container)).toStrictEqual([40, 100, 180]);
     });
 });
@@ -198,7 +198,7 @@ describe('Test _equivGateElem', () => {
                 },
             ],
         };
-        draw(circuit, container, STYLES['default']);
+        create(circuit).draw(container);
     });
     test('should return gate H', () => {
         const elem = container.querySelector('[class^="gate-"]') as SVGElement;

From f2d8cd5e4f5e3772ead151db57ccd3911b0301b0 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Sat, 30 Jul 2022 14:22:31 -0400
Subject: [PATCH 081/108] Add return signatures

---
 src/panel.ts | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 13956692..681c60d8 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -206,7 +206,7 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
     }
 };
 
-const panel = (dispatch: Dispatch, context: Context, options?: PanelOptions) => {
+const panel = (dispatch: Dispatch, context: Context, options?: PanelOptions): HTMLElement => {
     const panelElem = elem('div');
     panelElem.className = 'panel';
     children(panelElem, [
@@ -217,7 +217,7 @@ const panel = (dispatch: Dispatch, context: Context, options?: PanelOptions) =>
     return panelElem;
 };
 
-const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions) => {
+const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions): HTMLElement => {
     let gateDictionary = defaultGateDictionary;
     let objectKeys = Object.keys(gateDictionary);
     if (options != null) {
@@ -252,7 +252,7 @@ const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions)
     return addPanelElem;
 };
 
-const editPanel = (dispatch: Dispatch, context: Context) => {
+const editPanel = (dispatch: Dispatch, context: Context): HTMLElement => {
     const { operation, registerSize } = context;
     const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
     const target = operation?.targets[0].qId;
@@ -277,17 +277,17 @@ const elem = (tag: string, className?: string): HTMLElement => {
 /**
  * Append all child elements to a parent element
  */
-const children = (parentElem: HTMLElement, childElems: HTMLElement[]) => {
+const children = (parentElem: HTMLElement, childElems: HTMLElement[]): HTMLElement => {
     childElems.map((elem) => parentElem.appendChild(elem));
     return parentElem;
 };
 
-const childrenSvg = (parentElem: SVGElement, childElems: SVGElement[]) => {
+const childrenSvg = (parentElem: SVGElement, childElems: SVGElement[]): SVGElement => {
     childElems.map((elem) => parentElem.appendChild(elem));
     return parentElem;
 };
 
-const title = (text: string) => {
+const title = (text: string): HTMLElement => {
     const titleElem = elem('h2');
     titleElem.className = 'title';
     titleElem.innerText = text;
@@ -332,7 +332,7 @@ const select = (
     return divElem;
 };
 
-const option = (value: string, text: string) => {
+const option = (value: string, text: string): HTMLOptionElement => {
     const optionElem = elem('option') as HTMLOptionElement;
     optionElem.value = value;
     optionElem.textContent = text;
@@ -346,7 +346,7 @@ const checkboxes = (
     selectedIndexes: number[],
     dispatch: Dispatch,
     operation?: Operation,
-) => {
+): HTMLDivElement => {
     const checkboxElems = options.map((option, index) => {
         const elem = checkbox(option.value, option.text);
         const inputElem = elem.querySelector('input') as HTMLInputElement;
@@ -378,7 +378,7 @@ const checkboxes = (
     return divElem;
 };
 
-const checkbox = (value: string, text: string) => {
+const checkbox = (value: string, text: string): HTMLLabelElement => {
     const inputElem = elem('input') as HTMLInputElement;
     inputElem.type = 'checkbox';
     inputElem.value = value;
@@ -389,7 +389,7 @@ const checkbox = (value: string, text: string) => {
     return labelElem;
 };
 
-const text = (label: string, className: string, dispatch: Dispatch, operation?: Operation) => {
+const text = (label: string, className: string, dispatch: Dispatch, operation?: Operation): HTMLElement => {
     const labelElem = elem('label') as HTMLLabelElement;
     labelElem.className = 'block';
     labelElem.textContent = label;
@@ -463,7 +463,7 @@ const toMetadata = (operation: Operation | undefined, x: number, y: number): Met
  * @param dispatch
  * @param type i.e. 'H' or 'X'
  */
-const gate = (dispatch: Dispatch, gateDictionary: GateDictionary, type: string, x: number, y: number) => {
+const gate = (dispatch: Dispatch, gateDictionary: GateDictionary, type: string, x: number, y: number): SVGElement => {
     const operation = gateDictionary[type];
     if (operation == null) throw new Error(`Gate ${type} not available`);
     const metadata = toMetadata(operation, x, y);

From 7d2e05a7f9ea8301436b8e04ad6daade921c1102 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Sat, 30 Jul 2022 14:34:36 -0400
Subject: [PATCH 082/108] Update draggable snapshot

---
 .../{editable.test.ts.snap => draggable.test.ts.snap}             | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename __tests__/__snapshots__/{editable.test.ts.snap => draggable.test.ts.snap} (100%)

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/draggable.test.ts.snap
similarity index 100%
rename from __tests__/__snapshots__/editable.test.ts.snap
rename to __tests__/__snapshots__/draggable.test.ts.snap

From aef2eb01ea024d974ab23699cec24d230748a16a Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Sun, 31 Jul 2022 11:54:42 -0400
Subject: [PATCH 083/108] Add tests

---
 __tests__/__snapshots__/panel.test.ts.snap | 403 +++++++++++++++++++++
 __tests__/panel.test.ts                    | 300 +++++++++++++++
 src/panel.ts                               |  27 +-
 3 files changed, 727 insertions(+), 3 deletions(-)
 create mode 100644 __tests__/__snapshots__/panel.test.ts.snap
 create mode 100644 __tests__/panel.test.ts

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
new file mode 100644
index 00000000..7b1a87e1
--- /dev/null
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -0,0 +1,403 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Test checkbox Should return checkbox q0 1`] = `
+<label>
+  <input
+    type="checkbox"
+    value="0"
+  />
+  q0
+</label>
+`;
+
+exports[`Test checkbox Should return checkbox q1 1`] = `
+<label>
+  <input
+    type="checkbox"
+    value="1"
+  />
+  q1
+</label>
+`;
+
+exports[`Test checkbox Should return checkbox q2 1`] = `
+<label>
+  <input
+    type="checkbox"
+    value="2"
+  />
+  q2
+</label>
+`;
+
+exports[`Test checkboxes Test with gate X with 1 control 1`] = `
+<div
+  class="controls-input"
+>
+  <label
+    class="block"
+  >
+    Controls
+  </label>
+  <label>
+    <input
+      type="checkbox"
+      value="0"
+    />
+    q0
+  </label>
+  <label>
+    <input
+      checked="true"
+      type="checkbox"
+      value="1"
+    />
+    q1
+  </label>
+</div>
+`;
+
+exports[`Test checkboxes Test with gate X with 2 controls 1`] = `
+<div
+  class="controls-input"
+>
+  <label
+    class="block"
+  >
+    Controls
+  </label>
+  <label>
+    <input
+      checked="true"
+      type="checkbox"
+      value="0"
+    />
+    q0
+  </label>
+  <label>
+    <input
+      type="checkbox"
+      value="1"
+    />
+    q1
+  </label>
+  <label>
+    <input
+      checked="true"
+      type="checkbox"
+      value="2"
+    />
+    q2
+  </label>
+</div>
+`;
+
+exports[`Test children Add 0 with class to parent 1`] = `<div />`;
+
+exports[`Test children Add 1 child to parent 1`] = `
+<div>
+  <p />
+</div>
+`;
+
+exports[`Test children Add 2 children to parent 1`] = `
+<div>
+  <p />
+  <div />
+</div>
+`;
+
+exports[`Test childrenSvg Add 0 child to parent 1`] = `<svg />`;
+
+exports[`Test childrenSvg Add 1 child to parent 1`] = `
+<svg>
+  <rect />
+</svg>
+`;
+
+exports[`Test childrenSvg Add 2 children to parent 1`] = `
+<svg>
+  <rect />
+  <circle />
+</svg>
+`;
+
+exports[`Test editPanel  1`] = `
+<div
+  class="edit-panel"
+>
+  <h2
+    class="title"
+  >
+    EDIT
+  </h2>
+  <div
+    class="target-input"
+  >
+    <label
+      class="block"
+    >
+      Target
+    </label>
+    <select>
+      <option
+        value="0"
+      >
+        q0
+      </option>
+      <option
+        value="1"
+      >
+        q1
+      </option>
+    </select>
+  </div>
+  <div
+    class="controls-input"
+  >
+    <label
+      class="block"
+    >
+      Controls
+    </label>
+    <label>
+      <input
+        type="checkbox"
+        value="0"
+      />
+      q0
+    </label>
+    <label>
+      <input
+        type="checkbox"
+        value="1"
+      />
+      q1
+    </label>
+  </div>
+  <div
+    class="display-input"
+  >
+    <label
+      class="block"
+    >
+      Display
+    </label>
+    <input
+      autofocus="true"
+      type="text"
+    />
+  </div>
+</div>
+`;
+
+exports[`Test elem Should return <div> 1`] = `<div />`;
+
+exports[`Test elem Should return <div> with className "panel" 1`] = `
+<div
+  class="panel"
+/>
+`;
+
+exports[`Test elem Should return <p> 1`] = `<p />`;
+
+exports[`Test gate Should return gate H 1`] = `
+<g
+  class="gate"
+>
+  <g>
+    <g>
+      <rect
+        class="gate-unitary"
+        height="40"
+        width="40"
+        x="1"
+        y="1"
+      />
+      <text
+        font-size="14"
+        x="21"
+        y="21"
+      >
+        H
+      </text>
+    </g>
+  </g>
+</g>
+`;
+
+exports[`Test gate Should return gate RX 1`] = `
+<g
+  class="gate"
+>
+  <g>
+    <g>
+      <rect
+        class="gate-unitary"
+        height="40"
+        width="40"
+        x="1"
+        y="1"
+      />
+      <text
+        font-size="14"
+        x="21"
+        y="21"
+      >
+        RX
+      </text>
+    </g>
+  </g>
+</g>
+`;
+
+exports[`Test gate Should return gate X 1`] = `
+<g
+  class="gate"
+>
+  <g
+    class="oplus"
+  >
+    <circle
+      cx="21"
+      cy="21"
+      r="15"
+    />
+    <line
+      x1="21"
+      x2="21"
+      y1="6"
+      y2="36"
+    />
+    <line
+      x1="6"
+      x2="36"
+      y1="21"
+      y2="21"
+    />
+  </g>
+</g>
+`;
+
+exports[`Test option Should return option q0 1`] = `
+<option
+  value="0"
+>
+  q0
+</option>
+`;
+
+exports[`Test option Should return option q1 1`] = `
+<option
+  value="1"
+>
+  q1
+</option>
+`;
+
+exports[`Test option Should return option q2 1`] = `
+<option
+  value="2"
+>
+  q2
+</option>
+`;
+
+exports[`Test text Should return gate H without display-args 1`] = `
+<div
+  class="display-input"
+>
+  <label
+    class="block"
+  >
+    Display
+  </label>
+  <input
+    autofocus="true"
+    type="text"
+  />
+</div>
+`;
+
+exports[`Test text Should return gate RX with display-args 1`] = `
+<div
+  class="display-input"
+>
+  <label
+    class="block"
+  >
+    Display
+  </label>
+  <input
+    autofocus="true"
+    type="text"
+  />
+</div>
+`;
+
+exports[`Test title Should return title "ADD" 1`] = `
+<h2
+  class="title"
+>
+  ADD
+</h2>
+`;
+
+exports[`Test title Should return title "EDIT" 1`] = `
+<h2
+  class="title"
+>
+  EDIT
+</h2>
+`;
+
+exports[`Test title Should return title "PANEL" 1`] = `
+<h2
+  class="title"
+>
+  PANEL
+</h2>
+`;
+
+exports[`Test toMetadata Should return metadata of gate H 1`] = `
+Object {
+  "controlsY": Array [],
+  "label": "H",
+  "targetsY": Array [
+    Array [
+      21,
+    ],
+  ],
+  "type": 4,
+  "width": 40,
+  "x": 21,
+}
+`;
+
+exports[`Test toMetadata Should return metadata of gate RX 1`] = `
+Object {
+  "controlsY": Array [],
+  "label": "H",
+  "targetsY": Array [
+    Array [
+      21,
+    ],
+  ],
+  "type": 4,
+  "width": 40,
+  "x": 21,
+}
+`;
+
+exports[`Test toMetadata Should return metadata of gate X 1`] = `
+Object {
+  "controlsY": Array [],
+  "label": "X",
+  "targetsY": Array [
+    21,
+  ],
+  "type": 3,
+  "width": 40,
+  "x": 21,
+}
+`;
diff --git a/__tests__/panel.test.ts b/__tests__/panel.test.ts
new file mode 100644
index 00000000..d18c1d01
--- /dev/null
+++ b/__tests__/panel.test.ts
@@ -0,0 +1,300 @@
+import range from 'lodash/range';
+import { PanelContext } from '../src/panel';
+import { Action, Dispatch, exportedForTesting } from '../src/panel';
+
+const {
+    editPanel,
+    elem,
+    children,
+    childrenSvg,
+    title,
+    select,
+    option,
+    checkboxes,
+    checkbox,
+    text,
+    toMetadata,
+    gate,
+    defaultGateDictionary,
+} = exportedForTesting;
+
+describe('Test elem', () => {
+    test('Should return <div>', () => {
+        expect(elem('div')).toMatchSnapshot();
+    });
+    test('Should return <p>', () => {
+        expect(elem('p')).toMatchSnapshot();
+    });
+    test('Should return <div> with className "panel"', () => {
+        expect(elem('div', 'panel')).toMatchSnapshot();
+    });
+});
+
+describe('Test children', () => {
+    let parentElem: HTMLDivElement;
+    beforeEach(() => {
+        parentElem = document.createElement('div');
+    });
+    test('Add 1 child to parent', () => {
+        const childElems = [document.createElement('p')] as HTMLElement[];
+        expect(children(parentElem, childElems)).toMatchSnapshot();
+    });
+    test('Add 2 children to parent', () => {
+        const childElems = [document.createElement('p'), document.createElement('div')] as HTMLElement[];
+        expect(children(parentElem, childElems)).toMatchSnapshot();
+    });
+    test('Add 0 with class to parent', () => {
+        const childElems = [] as HTMLElement[];
+        expect(children(parentElem, childElems)).toMatchSnapshot();
+    });
+});
+
+describe('Test childrenSvg', () => {
+    let parentElem: SVGElement;
+    beforeEach(() => {
+        parentElem = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+    });
+    test('Add 0 child to parent', () => {
+        const childElems = [] as SVGElement[];
+        expect(childrenSvg(parentElem, childElems)).toMatchSnapshot();
+    });
+    test('Add 1 child to parent', () => {
+        const childElems = [document.createElementNS('http://www.w3.org/2000/svg', 'rect')] as SVGElement[];
+        expect(childrenSvg(parentElem, childElems)).toMatchSnapshot();
+    });
+    test('Add 2 children to parent', () => {
+        const childElems = [
+            document.createElementNS('http://www.w3.org/2000/svg', 'rect'), //
+            document.createElementNS('http://www.w3.org/2000/svg', 'circle'), //
+        ] as SVGElement[];
+        expect(childrenSvg(parentElem, childElems)).toMatchSnapshot();
+    });
+});
+
+describe('Test title', () => {
+    test('Should return title "PANEL"', () => {
+        expect(title('PANEL')).toMatchSnapshot();
+    });
+    test('Should return title "ADD"', () => {
+        expect(title('ADD')).toMatchSnapshot();
+    });
+    test('Should return title "EDIT"', () => {
+        expect(title('EDIT')).toMatchSnapshot();
+    });
+});
+
+describe('Test text', () => {
+    const emptyDispatch: Dispatch = (action: Action) => {
+        action;
+    };
+    test('Should return gate H without display-args', () => {
+        const operation = {
+            gate: 'H',
+            targets: [{ qId: 0 }],
+        };
+        expect(text('Display', 'display-input', emptyDispatch, operation)).toMatchSnapshot();
+    });
+    test('Should return gate RX with display-args', () => {
+        const operation = {
+            gate: 'RX',
+            displayArgs: '(0.25)',
+            isControlled: true,
+            controls: [{ qId: 1 }],
+            targets: [{ qId: 0 }],
+        };
+        expect(text('Display', 'display-input', emptyDispatch, operation)).toMatchSnapshot();
+    });
+});
+
+describe('Test toMetadata', () => {
+    test('Should return metadata of gate H', () => {
+        const operation = {
+            gate: 'H',
+            targets: [{ qId: 0 }],
+        };
+        expect(toMetadata(operation, 0, 0)).toMatchSnapshot();
+    });
+    test('Should return metadata of gate RX', () => {
+        const operation = {
+            gate: 'H',
+            targets: [{ qId: 0 }],
+        };
+        expect(toMetadata(operation, 0, 0)).toMatchSnapshot();
+    });
+    test('Should return metadata of gate X', () => {
+        const operation = {
+            gate: 'X',
+            targets: [{ qId: 3 }],
+        };
+        expect(toMetadata(operation, 0, 0)).toMatchSnapshot();
+    });
+});
+
+describe('Test select', () => {
+    const emptyDispatch: Dispatch = (action: Action) => {
+        action;
+    };
+    test('Test with gate X', () => {
+        const operation = {
+            gate: 'X',
+            targets: [{ qId: 0 }],
+            controls: [{ qId: 1 }],
+        };
+        const registerSize = 2;
+        const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
+        const target = operation?.targets[0].qId;
+        select('Target', 'target-input', options, target || 0, emptyDispatch, operation);
+    });
+    test('Test with gate H', () => {
+        const operation = {
+            gate: 'H',
+            targets: [{ qId: 0 }],
+        };
+        const registerSize = 2;
+        const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
+        const target = operation?.targets[0].qId;
+        select('Target', 'target-input', options, target || 0, emptyDispatch, operation);
+    });
+});
+
+describe('Test option', () => {
+    test('Should return option q0', () => {
+        expect(option('0', 'q0')).toMatchSnapshot();
+    });
+    test('Should return option q1', () => {
+        expect(option('1', 'q1')).toMatchSnapshot();
+    });
+    test('Should return option q2', () => {
+        expect(option('2', 'q2')).toMatchSnapshot();
+    });
+});
+
+describe('Test checkboxes', () => {
+    const emptyDispatch: Dispatch = (action: Action) => {
+        action;
+    };
+    test('Test with gate X with 1 control', () => {
+        const operation = {
+            gate: 'X',
+            targets: [{ qId: 0 }],
+            controls: [{ qId: 1 }],
+        };
+        const registerSize = 2;
+        const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
+        const controls = operation.controls?.map((control) => control.qId);
+        expect(
+            checkboxes('Controls', 'controls-input', options, controls || [], emptyDispatch, operation),
+        ).toMatchSnapshot();
+    });
+    test('Test with gate X with 2 controls', () => {
+        const operation = {
+            gate: 'X',
+            targets: [{ qId: 1 }],
+            controls: [{ qId: 0 }, { qId: 2 }],
+        };
+        const registerSize = 3;
+        const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
+        const controls = operation.controls?.map((control) => control.qId);
+        expect(
+            checkboxes('Controls', 'controls-input', options, controls || [], emptyDispatch, operation),
+        ).toMatchSnapshot();
+    });
+});
+
+describe('Test checkbox', () => {
+    test('Should return checkbox q0', () => {
+        expect(checkbox('0', 'q0')).toMatchSnapshot();
+    });
+    test('Should return checkbox q1', () => {
+        expect(checkbox('1', 'q1')).toMatchSnapshot();
+    });
+    test('Should return checkbox q2', () => {
+        expect(checkbox('2', 'q2')).toMatchSnapshot();
+    });
+});
+
+describe('Test gate', () => {
+    const emptyDispatch: Dispatch = (action: Action) => {
+        action;
+    };
+    test('Should return gate H', () => {
+        expect(gate(emptyDispatch, defaultGateDictionary, 'H', 0, 0)).toMatchSnapshot();
+    });
+    test('Should return gate X', () => {
+        expect(gate(emptyDispatch, defaultGateDictionary, 'X', 0, 0)).toMatchSnapshot();
+    });
+    test('Should return gate RX', () => {
+        expect(gate(emptyDispatch, defaultGateDictionary, 'RX', 0, 0)).toMatchSnapshot();
+    });
+});
+
+describe('Test defaulGateDictionary', () => {
+    test('Verify defaultGateDictionary', () => {
+        const expected = {
+            Entangle: {
+                gate: 'Entangle',
+                targets: [{ qId: 0 }],
+            },
+            RX: {
+                gate: 'RX',
+                targets: [{ qId: 0 }],
+            },
+            RY: {
+                gate: 'RY',
+                targets: [{ qId: 0 }],
+            },
+            RZ: {
+                gate: 'RZ',
+                targets: [{ qId: 0 }],
+            },
+            H: {
+                gate: 'H',
+                targets: [{ qId: 0 }],
+            },
+            X: {
+                gate: 'X',
+                targets: [{ qId: 0 }],
+            },
+            S: {
+                gate: 'S',
+                targets: [{ qId: 0 }],
+            },
+            T: {
+                gate: 'T',
+                targets: [{ qId: 0 }],
+            },
+            Y: {
+                gate: 'Y',
+                targets: [{ qId: 0 }],
+            },
+            Z: {
+                gate: 'Z',
+                targets: [{ qId: 0 }],
+            },
+            ZZ: {
+                gate: 'ZZ',
+                targets: [{ qId: 0 }],
+            },
+        };
+        expect(defaultGateDictionary).toMatchObject(expected);
+    });
+});
+
+describe('Test editPanel', () => {
+    const emptyDispatch: Dispatch = (action: Action) => {
+        action;
+    };
+    test('', () => {
+        const context: PanelContext = {
+            addMode: false,
+            operations: [],
+            operation: {
+                gate: 'X',
+                targets: [{ qId: 0 }],
+            },
+            registerSize: 2,
+            container: undefined,
+        };
+        expect(editPanel(emptyDispatch, context)).toMatchSnapshot();
+    });
+});
diff --git a/src/panel.ts b/src/panel.ts
index 681c60d8..f2d2e4dd 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -270,7 +270,7 @@ const editPanel = (dispatch: Dispatch, context: Context): HTMLElement => {
 
 const elem = (tag: string, className?: string): HTMLElement => {
     const _elem = document.createElement(tag);
-    _elem.className = className || '';
+    className && (_elem.className = className);
     return _elem;
 };
 
@@ -290,7 +290,7 @@ const childrenSvg = (parentElem: SVGElement, childElems: SVGElement[]): SVGEleme
 const title = (text: string): HTMLElement => {
     const titleElem = elem('h2');
     titleElem.className = 'title';
-    titleElem.innerText = text;
+    titleElem.textContent = text;
     return titleElem;
 };
 
@@ -536,4 +536,25 @@ const defaultGateDictionary: GateDictionary = {
     },
 };
 
-export { extensionPanel, PanelOptions };
+/**
+ * Object exported for unit testing
+ */
+const exportedForTesting = {
+    panel,
+    addPanel,
+    editPanel,
+    elem,
+    children,
+    childrenSvg,
+    title,
+    select,
+    option,
+    checkboxes,
+    checkbox,
+    text,
+    toMetadata,
+    gate,
+    defaultGateDictionary,
+};
+
+export { extensionPanel, PanelOptions, exportedForTesting, Dispatch, Action, Context as PanelContext };

From 141f4752af927d7e10131a8b465f9f055a429243 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Sun, 31 Jul 2022 12:00:46 -0400
Subject: [PATCH 084/108] Add tests for addPanel

---
 __tests__/__snapshots__/panel.test.ts.snap | 323 ++++++++++++++++++++-
 __tests__/panel.test.ts                    |  38 ++-
 2 files changed, 358 insertions(+), 3 deletions(-)

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
index 7b1a87e1..04caf3ac 100644
--- a/__tests__/__snapshots__/panel.test.ts.snap
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -1,5 +1,326 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`Test addPanel Should return default addPanel 1`] = `
+<div
+  class="add-panel"
+>
+  <h2
+    class="title"
+  >
+    ADD
+  </h2>
+  <svg
+    class="add-panel-svg"
+  >
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="76"
+            x="1"
+            y="1"
+          />
+          <text
+            font-size="14"
+            x="39"
+            y="21"
+          >
+            Entangle
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="87"
+            y="1"
+          />
+          <text
+            font-size="14"
+            x="107"
+            y="21"
+          >
+            RX
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="1"
+            y="51"
+          />
+          <text
+            font-size="14"
+            x="21"
+            y="71"
+          >
+            RY
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="51"
+            y="51"
+          />
+          <text
+            font-size="14"
+            x="71"
+            y="71"
+          >
+            RZ
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="1"
+            y="101"
+          />
+          <text
+            font-size="14"
+            x="21"
+            y="121"
+          >
+            H
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g
+        class="oplus"
+      >
+        <circle
+          cx="71"
+          cy="121"
+          r="15"
+        />
+        <line
+          x1="71"
+          x2="71"
+          y1="106"
+          y2="136"
+        />
+        <line
+          x1="56"
+          x2="86"
+          y1="121"
+          y2="121"
+        />
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="1"
+            y="151"
+          />
+          <text
+            font-size="14"
+            x="21"
+            y="171"
+          >
+            S
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="51"
+            y="151"
+          />
+          <text
+            font-size="14"
+            x="71"
+            y="171"
+          >
+            T
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="1"
+            y="201"
+          />
+          <text
+            font-size="14"
+            x="21"
+            y="221"
+          >
+            Y
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="51"
+            y="201"
+          />
+          <text
+            font-size="14"
+            x="71"
+            y="221"
+          >
+            Z
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="1"
+            y="251"
+          />
+          <text
+            font-size="14"
+            x="21"
+            y="271"
+          >
+            ZZ
+          </text>
+        </g>
+      </g>
+    </g>
+  </svg>
+</div>
+`;
+
+exports[`Test addPanel Should return default addPanel with displaySize 2 1`] = `
+<div
+  class="add-panel"
+>
+  <h2
+    class="title"
+  >
+    ADD
+  </h2>
+  <svg
+    class="add-panel-svg"
+  >
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="76"
+            x="1"
+            y="1"
+          />
+          <text
+            font-size="14"
+            x="39"
+            y="21"
+          >
+            Entangle
+          </text>
+        </g>
+      </g>
+    </g>
+    <g
+      class="gate"
+    >
+      <g>
+        <g>
+          <rect
+            class="gate-unitary"
+            height="40"
+            width="40"
+            x="87"
+            y="1"
+          />
+          <text
+            font-size="14"
+            x="107"
+            y="21"
+          >
+            RX
+          </text>
+        </g>
+      </g>
+    </g>
+  </svg>
+</div>
+`;
+
 exports[`Test checkbox Should return checkbox q0 1`] = `
 <label>
   <input
@@ -122,7 +443,7 @@ exports[`Test childrenSvg Add 2 children to parent 1`] = `
 </svg>
 `;
 
-exports[`Test editPanel  1`] = `
+exports[`Test editPanel Should return editPanel editing X gate 1`] = `
 <div
   class="edit-panel"
 >
diff --git a/__tests__/panel.test.ts b/__tests__/panel.test.ts
index d18c1d01..0d34fb1d 100644
--- a/__tests__/panel.test.ts
+++ b/__tests__/panel.test.ts
@@ -1,8 +1,9 @@
 import range from 'lodash/range';
-import { PanelContext } from '../src/panel';
+import { PanelContext, PanelOptions } from '../src/panel';
 import { Action, Dispatch, exportedForTesting } from '../src/panel';
 
 const {
+    addPanel,
     editPanel,
     elem,
     children,
@@ -284,7 +285,7 @@ describe('Test editPanel', () => {
     const emptyDispatch: Dispatch = (action: Action) => {
         action;
     };
-    test('', () => {
+    test('Should return editPanel editing X gate', () => {
         const context: PanelContext = {
             addMode: false,
             operations: [],
@@ -298,3 +299,36 @@ describe('Test editPanel', () => {
         expect(editPanel(emptyDispatch, context)).toMatchSnapshot();
     });
 });
+
+describe('Test addPanel', () => {
+    const emptyDispatch: Dispatch = (action: Action) => {
+        action;
+    };
+    test('Should return default addPanel', () => {
+        const context: PanelContext = {
+            addMode: true,
+            operations: [],
+            operation: {
+                gate: 'X',
+                targets: [{ qId: 0 }],
+            },
+            registerSize: 2,
+            container: undefined,
+        };
+        expect(addPanel(emptyDispatch, context)).toMatchSnapshot();
+    });
+    test('Should return default addPanel with displaySize 2', () => {
+        const context: PanelContext = {
+            addMode: true,
+            operations: [],
+            operation: {
+                gate: 'X',
+                targets: [{ qId: 0 }],
+            },
+            registerSize: 2,
+            container: undefined,
+        };
+        const options: PanelOptions = { displaySize: 2 };
+        expect(addPanel(emptyDispatch, context, options)).toMatchSnapshot();
+    });
+});

From 0ec4100caf1bd3ae08247095258aba5a6434fc69 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Sun, 31 Jul 2022 12:03:50 -0400
Subject: [PATCH 085/108] Add tests for panel

---
 __tests__/__snapshots__/panel.test.ts.snap | 338 +++++++++++++++++++++
 __tests__/panel.test.ts                    |  33 ++
 2 files changed, 371 insertions(+)

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
index 04caf3ac..3892b303 100644
--- a/__tests__/__snapshots__/panel.test.ts.snap
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -624,6 +624,344 @@ exports[`Test option Should return option q2 1`] = `
 </option>
 `;
 
+exports[`Test panel Should return panel with addPanel visible 1`] = `
+<div
+  class="panel"
+>
+  <div
+    class="add-panel"
+  >
+    <h2
+      class="title"
+    >
+      ADD
+    </h2>
+    <svg
+      class="add-panel-svg"
+    >
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="76"
+              x="1"
+              y="1"
+            />
+            <text
+              font-size="14"
+              x="39"
+              y="21"
+            >
+              Entangle
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="87"
+              y="1"
+            />
+            <text
+              font-size="14"
+              x="107"
+              y="21"
+            >
+              RX
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="1"
+              y="51"
+            />
+            <text
+              font-size="14"
+              x="21"
+              y="71"
+            >
+              RY
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="51"
+              y="51"
+            />
+            <text
+              font-size="14"
+              x="71"
+              y="71"
+            >
+              RZ
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="1"
+              y="101"
+            />
+            <text
+              font-size="14"
+              x="21"
+              y="121"
+            >
+              H
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g
+          class="oplus"
+        >
+          <circle
+            cx="71"
+            cy="121"
+            r="15"
+          />
+          <line
+            x1="71"
+            x2="71"
+            y1="106"
+            y2="136"
+          />
+          <line
+            x1="56"
+            x2="86"
+            y1="121"
+            y2="121"
+          />
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="1"
+              y="151"
+            />
+            <text
+              font-size="14"
+              x="21"
+              y="171"
+            >
+              S
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="51"
+              y="151"
+            />
+            <text
+              font-size="14"
+              x="71"
+              y="171"
+            >
+              T
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="1"
+              y="201"
+            />
+            <text
+              font-size="14"
+              x="21"
+              y="221"
+            >
+              Y
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="51"
+              y="201"
+            />
+            <text
+              font-size="14"
+              x="71"
+              y="221"
+            >
+              Z
+            </text>
+          </g>
+        </g>
+      </g>
+      <g
+        class="gate"
+      >
+        <g>
+          <g>
+            <rect
+              class="gate-unitary"
+              height="40"
+              width="40"
+              x="1"
+              y="251"
+            />
+            <text
+              font-size="14"
+              x="21"
+              y="271"
+            >
+              ZZ
+            </text>
+          </g>
+        </g>
+      </g>
+    </svg>
+  </div>
+</div>
+`;
+
+exports[`Test panel Should return panel with editPanel visible 1`] = `
+<div
+  class="panel"
+>
+  <div
+    class="edit-panel"
+  >
+    <h2
+      class="title"
+    >
+      EDIT
+    </h2>
+    <div
+      class="target-input"
+    >
+      <label
+        class="block"
+      >
+        Target
+      </label>
+      <select>
+        <option
+          value="0"
+        >
+          q0
+        </option>
+        <option
+          value="1"
+        >
+          q1
+        </option>
+      </select>
+    </div>
+    <div
+      class="controls-input"
+    >
+      <label
+        class="block"
+      >
+        Controls
+      </label>
+      <label>
+        <input
+          type="checkbox"
+          value="0"
+        />
+        q0
+      </label>
+      <label>
+        <input
+          type="checkbox"
+          value="1"
+        />
+        q1
+      </label>
+    </div>
+    <div
+      class="display-input"
+    >
+      <label
+        class="block"
+      >
+        Display
+      </label>
+      <input
+        autofocus="true"
+        type="text"
+      />
+    </div>
+  </div>
+</div>
+`;
+
 exports[`Test text Should return gate H without display-args 1`] = `
 <div
   class="display-input"
diff --git a/__tests__/panel.test.ts b/__tests__/panel.test.ts
index 0d34fb1d..2dafcee7 100644
--- a/__tests__/panel.test.ts
+++ b/__tests__/panel.test.ts
@@ -3,6 +3,7 @@ import { PanelContext, PanelOptions } from '../src/panel';
 import { Action, Dispatch, exportedForTesting } from '../src/panel';
 
 const {
+    panel,
     addPanel,
     editPanel,
     elem,
@@ -332,3 +333,35 @@ describe('Test addPanel', () => {
         expect(addPanel(emptyDispatch, context, options)).toMatchSnapshot();
     });
 });
+
+describe('Test panel', () => {
+    const emptyDispatch: Dispatch = (action: Action) => {
+        action;
+    };
+    test('Should return panel with addPanel visible', () => {
+        const context: PanelContext = {
+            addMode: true,
+            operations: [],
+            operation: {
+                gate: 'X',
+                targets: [{ qId: 0 }],
+            },
+            registerSize: 2,
+            container: undefined,
+        };
+        expect(panel(emptyDispatch, context)).toMatchSnapshot();
+    });
+    test('Should return panel with editPanel visible', () => {
+        const context: PanelContext = {
+            addMode: false,
+            operations: [],
+            operation: {
+                gate: 'X',
+                targets: [{ qId: 0 }],
+            },
+            registerSize: 2,
+            container: undefined,
+        };
+        expect(panel(emptyDispatch, context)).toMatchSnapshot();
+    });
+});

From ec1c6b6b82be0ea5f901228244b03a765cc2dec1 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Sun, 31 Jul 2022 12:05:56 -0400
Subject: [PATCH 086/108] Reorganize imports

---
 __tests__/panel.test.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/__tests__/panel.test.ts b/__tests__/panel.test.ts
index 2dafcee7..f5073f14 100644
--- a/__tests__/panel.test.ts
+++ b/__tests__/panel.test.ts
@@ -1,6 +1,5 @@
 import range from 'lodash/range';
-import { PanelContext, PanelOptions } from '../src/panel';
-import { Action, Dispatch, exportedForTesting } from '../src/panel';
+import { Action, Dispatch, exportedForTesting, PanelContext, PanelOptions } from '../src/panel';
 
 const {
     panel,

From 3fd1fb463fdba7e449af038ccf9af6e24ee85a3f Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 1 Aug 2022 10:24:23 -0400
Subject: [PATCH 087/108] Add license

---
 __tests__/panel.test.ts | 3 +++
 src/panel.ts            | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/__tests__/panel.test.ts b/__tests__/panel.test.ts
index f5073f14..10256e60 100644
--- a/__tests__/panel.test.ts
+++ b/__tests__/panel.test.ts
@@ -1,3 +1,6 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
 import range from 'lodash/range';
 import { Action, Dispatch, exportedForTesting, PanelContext, PanelOptions } from '../src/panel';
 
diff --git a/src/panel.ts b/src/panel.ts
index f2d2e4dd..5e96f652 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -1,3 +1,6 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
 import cloneDeep from 'lodash/cloneDeep';
 import range from 'lodash/range';
 import { Operation } from './circuit';

From 8224df63a8aa857b61c3f123cf85d51961f5d54c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 1 Aug 2022 11:24:29 -0400
Subject: [PATCH 088/108] Add documentation

---
 src/panel.ts | 159 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 150 insertions(+), 9 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 5e96f652..d22b4f02 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -12,6 +12,9 @@ import { Register } from './register';
 import { Sqore } from './sqore';
 import { getGateWidth } from './utils';
 
+/**
+ * Interface for context
+ */
 interface Context {
     addMode: boolean;
     operations: Operation[];
@@ -20,6 +23,9 @@ interface Context {
     container: HTMLElement | undefined;
 }
 
+/**
+ * Object to maintain global state of extensionPanel
+ */
 const context: Context = {
     addMode: true,
     operations: [],
@@ -28,13 +34,34 @@ const context: Context = {
     container: undefined,
 };
 
+/**
+ * Interface for options provided through usePanel()
+ */
 interface PanelOptions {
     displaySize?: number;
     gateDictionary?: GateDictionary;
 }
 
+/**
+ * Interface for dispatch
+ */
+interface Dispatch {
+    (action: Action): void;
+}
+
+/**
+ * Entry point to run extensionPanel
+ * @param options   User-provided object to customize extensionPanel
+ * @returns         Curried function of entry point to run extensionPanel
+ */
 const extensionPanel =
     (options?: PanelOptions) =>
+    /**
+     * Curried function of entry point to run extensionPanel
+     * @param container     HTML element for rendering visualization into
+     * @param sqore         Sqore object
+     * @param useRefresh    Function to trigger circuit re-rendering
+     */
     (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
         const dispatch = (action: Action) => {
             update(action, context, useRefresh);
@@ -53,7 +80,14 @@ const extensionPanel =
         prevPanelElem == null && container.prepend(panelElem);
     };
 
-const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) => {
+/**
+ * Function to handle all event listeners
+ * @param dispatch      Function to update state and trigger panel re-rendering
+ * @param container     HTML element for rendering visualization into
+ * @param sqore         Sqore object
+ */
+const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore): void => {
+    // Gates in SVG circuit are selectable
     const elems = container.querySelectorAll<SVGElement>('[data-id]');
     elems.forEach((elem) =>
         elem.addEventListener('mousedown', (ev: MouseEvent) => {
@@ -65,17 +99,20 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
         }),
     );
 
+    // Context is updated when mouse is over container
     container.addEventListener('mouseover', () => {
         context.registerSize = sqore.circuit.qubits.length;
         context.operations = sqore.circuit.operations;
         context.container = container;
     });
 
+    // addMode triggers
     const svgElem = container.querySelector('svg[id]');
     svgElem?.addEventListener('mousedown', () => {
         dispatch({ type: 'ADD_MODE' });
     });
 
+    // Drag and drop
     const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
     const dropzoneElems = dropzoneLayer.querySelectorAll<SVGRectElement>('.dropzone');
     dropzoneElems.forEach((dropzoneElem) =>
@@ -92,20 +129,32 @@ const addEvents = (dispatch: Dispatch, container: HTMLElement, sqore: Sqore) =>
         }),
     );
 
+    // Remove ghost element if drops gate in svgElement
     svgElem?.addEventListener('mouseup', () => {
         dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
     });
+
+    // Remove ghost element if drops gate in addPanel
     container.querySelector('.add-panel')?.addEventListener('mouseup', () => {
         dispatch({ type: 'REMOVE_GHOST_ELEMENT' });
     });
 };
 
+/**
+ * Interface for action
+ */
 interface Action {
     type: string;
     payload?: unknown;
 }
 
-const update = (action: Action, context: Context, useRefresh: () => void) => {
+/**
+ * Primary function for state management
+ * @param action        Object to have type and payload
+ * @param context       Context object to manage extension state
+ * @param useRefresh    Function to trigger circuit re-rendering
+ */
+const update = (action: Action, context: Context, useRefresh: () => void): void => {
     switch (action.type) {
         case 'ADD_MODE': {
             context.addMode = true;
@@ -209,6 +258,13 @@ const update = (action: Action, context: Context, useRefresh: () => void) => {
     }
 };
 
+/**
+ * Function to produce panel element
+ * @param dispatch      Function to update state and trigger panel re-rendering
+ * @param context       Context object to manage extension state
+ * @param options       User-provided object to customize extensionPanel
+ * @returns             HTML element for panel
+ */
 const panel = (dispatch: Dispatch, context: Context, options?: PanelOptions): HTMLElement => {
     const panelElem = elem('div');
     panelElem.className = 'panel';
@@ -220,6 +276,13 @@ const panel = (dispatch: Dispatch, context: Context, options?: PanelOptions): HT
     return panelElem;
 };
 
+/**
+ * Function to produce addPanel element
+ * @param dispatch      Function to update state and trigger panel re-rendering
+ * @param context       Context object to manage extension state
+ * @param options       User-provided object to customize extensionPanel
+ * @returns             HTML element for addPanel
+ */
 const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions): HTMLElement => {
     let gateDictionary = defaultGateDictionary;
     let objectKeys = Object.keys(gateDictionary);
@@ -255,6 +318,12 @@ const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions):
     return addPanelElem;
 };
 
+/**
+ * Function to produce editPanel element
+ * @param dispatch      Function to update state and trigger panel re-rendering
+ * @param context       Context object to manage extension state
+ * @returns             HTML element for editPanel
+ */
 const editPanel = (dispatch: Dispatch, context: Context): HTMLElement => {
     const { operation, registerSize } = context;
     const options = range(registerSize).map((i) => ({ value: `${i}`, text: `q${i}` }));
@@ -271,6 +340,12 @@ const editPanel = (dispatch: Dispatch, context: Context): HTMLElement => {
     return editPanelElem;
 };
 
+/**
+ * Factory function to produce HTML element
+ * @param tag       Tag name
+ * @param className Class name
+ * @returns         HTML element
+ */
 const elem = (tag: string, className?: string): HTMLElement => {
     const _elem = document.createElement(tag);
     className && (_elem.className = className);
@@ -278,18 +353,32 @@ const elem = (tag: string, className?: string): HTMLElement => {
 };
 
 /**
- * Append all child elements to a parent element
+ * Append all child elements to a parent HTML element
+ * @param parentElem    Parent HTML element
+ * @param childElems    Array of HTML child elements
+ * @returns             Parent HTML element with all children appended
  */
 const children = (parentElem: HTMLElement, childElems: HTMLElement[]): HTMLElement => {
     childElems.map((elem) => parentElem.appendChild(elem));
     return parentElem;
 };
 
+/**
+ * Append all child elements to a parent SVG element
+ * @param parentElem    Parent SVG element
+ * @param childElems    Array of SVG child elements
+ * @returns             Parent SVG element with all children appended
+ */
 const childrenSvg = (parentElem: SVGElement, childElems: SVGElement[]): SVGElement => {
     childElems.map((elem) => parentElem.appendChild(elem));
     return parentElem;
 };
 
+/**
+ * Function to produce title element
+ * @param text  Text
+ * @returns     Title element
+ */
 const title = (text: string): HTMLElement => {
     const titleElem = elem('h2');
     titleElem.className = 'title';
@@ -297,15 +386,24 @@ const title = (text: string): HTMLElement => {
     return titleElem;
 };
 
+/**
+ * Interface for option element
+ */
 interface Option {
     value: string;
     text: string;
 }
 
-interface Dispatch {
-    (action: Action): void;
-}
-
+/**
+ * Function to produce select element
+ * @param label         Label
+ * @param className     Class name
+ * @param options       Array of Option objects contain value and text
+ * @param selectedIndex Index of current selected option
+ * @param dispatch      Function to update state and trigger panel re-rendering
+ * @param operation     Optional Operation object
+ * @returns             Select element
+ */
 const select = (
     label: string,
     className: string,
@@ -335,6 +433,12 @@ const select = (
     return divElem;
 };
 
+/**
+ * Function to produce option element
+ * @param value Value
+ * @param text  Text
+ * @returns     Option element
+ */
 const option = (value: string, text: string): HTMLOptionElement => {
     const optionElem = elem('option') as HTMLOptionElement;
     optionElem.value = value;
@@ -342,6 +446,16 @@ const option = (value: string, text: string): HTMLOptionElement => {
     return optionElem;
 };
 
+/**
+ * Function to produce checkbox elements
+ * @param label             Label
+ * @param className         Class name
+ * @param options           Array of Option objects contain value and text
+ * @param selectedIndexes   Array of indexes of current selected options
+ * @param dispatch          Function to update state and trigger panel re-rendering
+ * @param operation         Optional Operation object
+ * @returns                 Parent div containing checkbox elements
+ */
 const checkboxes = (
     label: string,
     className: string,
@@ -381,6 +495,12 @@ const checkboxes = (
     return divElem;
 };
 
+/**
+ * Function to produce checkbox element
+ * @param value Value
+ * @param text  Text
+ * @returns     Checkbox element
+ */
 const checkbox = (value: string, text: string): HTMLLabelElement => {
     const inputElem = elem('input') as HTMLInputElement;
     inputElem.type = 'checkbox';
@@ -392,6 +512,14 @@ const checkbox = (value: string, text: string): HTMLLabelElement => {
     return labelElem;
 };
 
+/**
+ * Function to produce input text element
+ * @param label     Label
+ * @param className Class name
+ * @param dispatch  Function to update state and trigger panel re-rendering
+ * @param operation Optional Operation object
+ * @returns         Parent div containing input text element
+ */
 const text = (label: string, className: string, dispatch: Dispatch, operation?: Operation): HTMLElement => {
     const labelElem = elem('label') as HTMLLabelElement;
     labelElem.className = 'block';
@@ -414,6 +542,13 @@ const text = (label: string, className: string, dispatch: Dispatch, operation?:
     return divElem;
 };
 
+/**
+ * Wrapper to generate metadata based on _opToMetadata with mock registers and limited support
+ * @param operation     Operation object
+ * @param x             x coordinate at starting point from the left
+ * @param y             y coordinate at starting point from the top
+ * @returns             Metata object
+ */
 const toMetadata = (operation: Operation | undefined, x: number, y: number): Metadata => {
     const metadata: Metadata = {
         type: GateType.Invalid,
@@ -463,8 +598,8 @@ const toMetadata = (operation: Operation | undefined, x: number, y: number): Met
 
 /**
  * Generate gate element for Add Panel based on type of gate
- * @param dispatch
- * @param type i.e. 'H' or 'X'
+ * @param dispatch  Function to update state and trigger panel re-rendering
+ * @param type      Type of gate. Example: 'H' or 'X'
  */
 const gate = (dispatch: Dispatch, gateDictionary: GateDictionary, type: string, x: number, y: number): SVGElement => {
     const operation = gateDictionary[type];
@@ -488,10 +623,16 @@ const gate = (dispatch: Dispatch, gateDictionary: GateDictionary, type: string,
     return gateElem;
 };
 
+/**
+ * Interface for gate dictionary
+ */
 interface GateDictionary {
     [index: string]: Operation;
 }
 
+/**
+ * Object for default gate dictionary
+ */
 const defaultGateDictionary: GateDictionary = {
     Entangle: {
         gate: 'Entangle',

From 11646302692c75b692a5482dcf31b4461185f4d5 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 1 Aug 2022 12:09:50 -0400
Subject: [PATCH 089/108] Fix up comments

---
 src/draggable.ts |   6 +-
 src/editable.ts  | 559 -----------------------------------------------
 src/sqore.ts     |  20 +-
 3 files changed, 7 insertions(+), 578 deletions(-)
 delete mode 100644 src/editable.ts

diff --git a/src/draggable.ts b/src/draggable.ts
index 2b678be1..6c05f32c 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -22,9 +22,9 @@ interface Context {
 /**
  * Add editable elements and events.
  *
- * @param container         HTML element for rendering visualization into.
- * @param sqore             Sqore object
- * @param onCircuitChange   User-provided callback function triggered when circuit is changed
+ * @param Container     HTML element for rendering visualization into.
+ * @param sqore         Sqore object
+ * @param useRefresh    Function to trigger circuit re-rendering
  */
 const extensionDraggable = (container: HTMLElement, sqore: Sqore, useRefresh: () => void): void => {
     const svg = container.querySelector('svg[id]') as SVGElement;
diff --git a/src/editable.ts b/src/editable.ts
deleted file mode 100644
index 4c16c9cf..00000000
--- a/src/editable.ts
+++ /dev/null
@@ -1,559 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-import { Circuit, Operation } from './circuit';
-import { box } from './formatters/formatUtils';
-import { Register } from './register';
-import { Sqore } from './sqore';
-
-interface Context {
-    container: HTMLElement;
-    svg: SVGElement;
-    operations: Operation[];
-    wireData: number[];
-    renderFn: () => void;
-    paddingY: number;
-    selectedId: string | null;
-    selectedWire: string | null;
-}
-
-/**
- * Add editable elements and events.
- *
- * @param container         HTML element for rendering visualization into.
- * @param sqore             Sqore object
- * @param onCircuitChange   User-provided callback function triggered when circuit is changed
- */
-const addEditable = (container: HTMLElement, sqore: Sqore, onCircuitChange?: (circuit: Circuit) => void): void => {
-    const svg = container.querySelector('svg') as SVGElement;
-
-    const context: Context = {
-        container: container,
-        svg,
-        operations: sqore.circuit.operations,
-        wireData: _wireData(container),
-        renderFn: _renderFn(container, sqore, onCircuitChange),
-        paddingY: 20,
-        selectedId: null,
-        selectedWire: null,
-    };
-
-    _addStyles(container, _wireData(container));
-    _addDataWires(container);
-    svg.appendChild(_dropzoneLayer(context));
-    _addEvents(context);
-};
-
-/**
- * Add data-wire to all host elements
- */
-const _addDataWires = (container: HTMLElement) => {
-    const elems = _hostElems(container);
-    elems.forEach((elem) => {
-        const { cY } = _center(elem);
-        // i.e. cY = 40, wireData returns [40, 100, 140, 180]
-        // dataWire will return 0, which is the index of 40 in wireData
-        const dataWire = _wireData(container).findIndex((y) => y === cY);
-        if (dataWire !== -1) {
-            elem.setAttribute('data-wire', `${dataWire}`);
-        } else {
-            const { y, height } = elem.getBBox();
-            const wireData = _wireData(container);
-            const groupDataWire = wireData.findIndex((wireY) => wireY > y && wireY < y + height);
-            elem.setAttribute('data-wire', `${groupDataWire}`);
-        }
-    });
-};
-
-/**
- * Create a list of wires that element is spanning on
- * i.e. Gate 'Foo' spans on wire 0 (y=40), 1 (y=100), and 2 (y=140)
- *      Function returns [40, 100, 140]
- */
-const _wireYs = (elem: SVGGraphicsElement, wireData: number[]): number[] => {
-    const { y, height } = elem.getBBox();
-    return wireData.filter((wireY) => wireY > y && wireY < y + height);
-};
-
-/**
- * Get list of host elements that dropzones can be attached to
- */
-const _hostElems = (container: HTMLElement): SVGGraphicsElement[] => {
-    return Array.from(
-        container.querySelectorAll<SVGGraphicsElement>(
-            '[class^="gate-"]:not(.gate-control, .gate-swap), .control-dot, .oplus, .cross',
-        ),
-    );
-};
-
-/**
- * Add custom styles specific to this module
- */
-const _addStyles = (container: HTMLElement, wireData: number[]): void => {
-    const elems = _hostElems(container);
-    elems.forEach((elem) => {
-        if (_wireYs(elem, wireData).length < 2) elem.style.cursor = 'grab';
-    });
-};
-
-/**
- * Generate an array of wire prefixes from wire data
- */
-const _wirePrefixes = (wireData: number[]): { index: number; wireY: number; prefixX: number }[] =>
-    wireData.map((wireY, index) => ({ index, wireY, prefixX: 40 }));
-
-/**
- * Find center point of element
- */
-const _center = (elem: SVGGraphicsElement): { cX: number; cY: number } => {
-    const { x, y, width, height } = elem.getBBox();
-    return { cX: x + width / 2, cY: y + height / 2 };
-};
-
-/**
- * Create dropzone layer with all dropzones popullated
- */
-const _dropzoneLayer = (context: Context) => {
-    const dropzoneLayer = document.createElementNS('http://www.w3.org/2000/svg', 'g');
-    dropzoneLayer.classList.add('dropzone-layer');
-
-    const { container, svg, wireData, operations, paddingY } = context;
-    const elems = _hostElems(container);
-
-    const wirePrefixes = _wirePrefixes(wireData);
-
-    // Sort host elements by its x property
-    const sortedElems = Array.from(elems).sort((first, second) => {
-        const { x: x1 } = first.getBBox();
-        const { x: x2 } = second.getBBox();
-        return x1 - x2;
-    });
-
-    // Add dropzones for each host elements
-    sortedElems.map((elem) => {
-        const { cX, cY } = _center(elem);
-        const wirePrefix = wirePrefixes.find((item) => item.wireY === cY);
-
-        // Check to prevent group gates creating dropzones between wires
-        if (wirePrefix) {
-            const prefixX = wirePrefix.prefixX;
-            const elemDropzone = box(prefixX, cY - paddingY, cX - prefixX, paddingY * 2, 'dropzone');
-            elemDropzone.setAttribute('data-dropzone-id', _equivDataId(elem) || '');
-            elemDropzone.setAttribute('data-dropzone-wire', `${wirePrefix.index}`);
-
-            wirePrefix.prefixX = cX;
-
-            dropzoneLayer.appendChild(elemDropzone);
-        } else {
-            // Let group gates creating dropzones for each wire
-            const { x } = elem.getBBox();
-            const wireYs = _wireYs(elem, wireData);
-
-            wireYs.map((wireY) => {
-                const wirePrefix = wirePrefixes.find((item) => item.wireY === wireY);
-                if (wirePrefix) {
-                    const prefixX = wirePrefix.prefixX;
-                    const elemDropzone = box(prefixX, wireY - paddingY, x - prefixX, paddingY * 2, 'dropzone');
-                    elemDropzone.setAttribute('data-dropzone-id', _equivDataId(elem) || '');
-                    elemDropzone.setAttribute('data-dropzone-wire', `${wirePrefix.index}`);
-
-                    wirePrefix.prefixX = x;
-
-                    dropzoneLayer.appendChild(elemDropzone);
-                }
-            });
-        }
-    });
-
-    // Add remaining dropzones to fit max-width of the circuit
-    wirePrefixes.map(({ wireY, prefixX }) => {
-        const maxWidth = Number(svg.getAttribute('width'));
-        const elemDropzone = box(prefixX, wireY - paddingY, maxWidth - prefixX, paddingY * 2, 'dropzone');
-        elemDropzone.setAttribute('data-dropzone-id', `${operations.length}`);
-        const index = wireData.findIndex((item) => item === wireY);
-        elemDropzone.setAttribute('data-dropzone-wire', `${index}`);
-        dropzoneLayer.appendChild(elemDropzone);
-    });
-
-    return dropzoneLayer;
-};
-
-/**
- * Generate an array of y values based on circuit wires
- */
-const _wireData = (container: HTMLElement): number[] => {
-    // elems include qubit wires and lines of measure gates
-    const elems = container.querySelectorAll<SVGGElement>('svg > g:nth-child(3) > g');
-    // filter out <g> elements having more than 2 elements because
-    // qubit wires contain only 2 elements: <line> and <text>
-    // lines of measure gates contain 4 <line> elements
-    const wireElems = Array.from(elems).filter((elem) => elem.childElementCount < 3);
-    const wireData = wireElems.map((wireElem) => {
-        const lineElem = wireElem.children[0] as SVGLineElement;
-        return Number(lineElem.getAttribute('y1'));
-    });
-    return wireData;
-};
-
-/**
- * Find equivalent gate element of host element
- */
-const _equivGateElem = (elem: SVGElement): SVGElement | null => {
-    return elem.closest<SVGElement>('[data-id]');
-};
-
-/**
- * Find data-id of host element
- */
-const _equivDataId = (elem: SVGElement) => {
-    const gateElem = _equivGateElem(elem);
-    return gateElem != null ? gateElem.getAttribute('data-id') : null;
-};
-
-/**
- * Disable contextmenu default behaviors
- */
-const _addContextMenuEvents = (container: HTMLElement) => {
-    container.addEventListener('contextmenu', (ev: MouseEvent) => {
-        ev.preventDefault();
-    });
-};
-
-/**
- * Add events specifically for dropzoneLayer
- */
-const _addDropzoneLayerEvents = (container: HTMLElement, dropzoneLayer: SVGGElement) => {
-    container.addEventListener('mouseup', () => (dropzoneLayer.style.display = 'none'));
-};
-
-/**
- * Add events for document
- */
-const _addDocumentEvents = (context: Context) => {
-    const { container } = context;
-
-    document.addEventListener('keydown', (ev: KeyboardEvent) => {
-        if (ev.ctrlKey && context.selectedId) {
-            container.classList.remove('moving');
-            container.classList.add('copying');
-        }
-    });
-
-    document.addEventListener('keyup', () => {
-        if (context.selectedId) {
-            container.classList.remove('copying');
-            container.classList.add('moving');
-        }
-    });
-
-    document.addEventListener('mouseup', () => {
-        container.classList.remove('moving', 'copying');
-        context.selectedId = null;
-        context.selectedWire = null;
-    });
-};
-
-/**
- * Add all events
- */
-const _addEvents = (context: Context) => {
-    const { container, operations, renderFn } = context;
-    const dropzoneLayer = container.querySelector('.dropzone-layer') as SVGGElement;
-
-    _addContextMenuEvents(container);
-    _addDropzoneLayerEvents(container, dropzoneLayer);
-    _addDocumentEvents(context);
-
-    // Host element events
-    const elems = _hostElems(container);
-    elems.forEach((elem) => {
-        elem.addEventListener('mousedown', () => {
-            context.selectedWire = elem.getAttribute('data-wire');
-            container.classList.add('moving');
-        });
-
-        const gateElem = _equivGateElem(elem);
-        gateElem?.addEventListener('mousedown', (ev: MouseEvent) => {
-            ev.stopPropagation();
-            context.selectedId = _equivDataId(elem);
-            dropzoneLayer.style.display = 'block';
-        });
-    });
-
-    // Dropzone element events
-    const dropzoneElems = dropzoneLayer.querySelectorAll<SVGRectElement>('.dropzone');
-    dropzoneElems.forEach((dropzoneElem) => {
-        dropzoneElem.addEventListener('mouseup', (ev: MouseEvent) => {
-            const targetId = dropzoneElem.getAttribute('data-dropzone-id');
-            const targetWire = dropzoneElem.getAttribute('data-dropzone-wire');
-            if (
-                targetId == null || //
-                targetWire == null ||
-                context.selectedId == null ||
-                context.selectedWire == null
-            )
-                return;
-
-            const newSourceOperation = ev.ctrlKey
-                ? _copyX(context.selectedId, targetId, operations)
-                : _moveX(context.selectedId, targetId, operations);
-
-            if (newSourceOperation != null) {
-                _moveY(context.selectedWire, targetWire, newSourceOperation, context.wireData.length);
-                const parentOperation = _equivParentOperation(context.selectedId, operations);
-                if (parentOperation) {
-                    parentOperation.targets = _targets(parentOperation);
-                }
-            }
-
-            renderFn();
-        });
-    });
-};
-
-const _equivParentOperation = (dataId: string | null, operations: Operation[]): Operation | null => {
-    if (!dataId) return null;
-
-    const indexes = _indexes(dataId);
-    indexes.pop();
-    const lastIndex = indexes.pop();
-
-    if (lastIndex == null) return null;
-
-    let parentOperation = operations;
-    for (const index of indexes) {
-        parentOperation = parentOperation[index].children || parentOperation;
-    }
-    return parentOperation[lastIndex];
-};
-
-/**
- * Find equivalent parent array of an operation
- */
-const _equivParentArray = (dataId: string | null, operations: Operation[]): Operation[] | null => {
-    if (!dataId) return null;
-
-    const indexes = _indexes(dataId);
-    indexes.pop();
-
-    let parentArray = operations;
-    for (const index of indexes) {
-        parentArray = parentArray[index].children || parentArray;
-    }
-    return parentArray;
-};
-
-/**
- * Find an equivalent operation of an element based on its data-id
- */
-const _equivOperation = (dataId: string | null, operations: Operation[]): Operation | null => {
-    if (!dataId) return null;
-
-    const index = _lastIndex(dataId);
-    const operationParent = _equivParentArray(dataId, operations);
-
-    if (
-        operationParent == null || //
-        index == null
-    )
-        return null;
-
-    return operationParent[index];
-};
-
-/**
- * Move an operation horizontally
- */
-const _moveX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
-    if (sourceId === targetId) return _equivOperation(sourceId, operations);
-    const sourceOperation = _equivOperation(sourceId, operations);
-    const sourceOperationParent = _equivParentArray(sourceId, operations);
-    const targetOperationParent = _equivParentArray(targetId, operations);
-    const targetLastIndex = _lastIndex(targetId);
-
-    if (
-        targetOperationParent == null || //
-        targetLastIndex == null ||
-        sourceOperation == null ||
-        sourceOperationParent == null
-    )
-        return null;
-
-    // Insert sourceOperation to target last index
-    const newSourceOperation: Operation = JSON.parse(JSON.stringify(sourceOperation));
-    targetOperationParent.splice(targetLastIndex, 0, newSourceOperation);
-
-    // Delete sourceOperation
-    sourceOperation.gate = 'removed';
-    const indexToRemove = sourceOperationParent.findIndex((operation) => operation.gate === 'removed');
-    sourceOperationParent.splice(indexToRemove, 1);
-
-    return newSourceOperation;
-};
-
-/**
- * Copy an operation horizontally
- */
-const _copyX = (sourceId: string, targetId: string, operations: Operation[]): Operation | null => {
-    const sourceOperation = _equivOperation(sourceId, operations);
-    const sourceOperationParent = _equivParentArray(sourceId, operations);
-    const targetOperationParent = _equivParentArray(targetId, operations);
-    const targetLastIndex = _lastIndex(targetId);
-
-    if (
-        targetOperationParent == null || //
-        targetLastIndex == null ||
-        sourceOperation == null ||
-        sourceOperationParent == null
-    )
-        return null;
-
-    // Insert sourceOperation to target last index
-    const newSourceOperation: Operation = JSON.parse(JSON.stringify(sourceOperation));
-    targetOperationParent.splice(targetLastIndex, 0, newSourceOperation);
-
-    return newSourceOperation;
-};
-
-/**
- * Move an operation vertically by changing its controls and targets
- */
-const _moveY = (sourceWire: string, targetWire: string, operation: Operation, totalWires: number): Operation => {
-    if (operation.gate !== 'measure') {
-        const offset = parseInt(targetWire) - parseInt(sourceWire);
-        _offsetRecursively(operation, offset, totalWires);
-    }
-    return operation;
-};
-
-/**
- * Recursively change object controls and targets
- */
-const _offsetRecursively = (operation: Operation, wireOffset: number, totalWires: number): Operation => {
-    // Offset all targets by offsetY value
-    if (operation.targets != null) {
-        operation.targets.forEach((target) => {
-            target.qId = _circularMod(target.qId, wireOffset, totalWires);
-            if (target.cId) target.cId = _circularMod(target.cId, wireOffset, totalWires);
-        });
-    }
-
-    // Offset all controls by offsetY value
-    if (operation.controls != null) {
-        operation.controls.forEach((control) => {
-            control.qId = _circularMod(control.qId, wireOffset, totalWires);
-            if (control.cId) control.cId = _circularMod(control.qId, wireOffset, totalWires);
-        });
-    }
-
-    // Offset recursively through all children
-    if (operation.children != null) {
-        operation.children.forEach((child) => _offsetRecursively(child, wireOffset, totalWires));
-    }
-
-    return operation;
-};
-
-/**
- * Find targets of an operation by recursively walkthrough all of its children controls and targets
- * i.e. Gate Foo contains gate H and gate RX.
- *      qIds of Gate H is 1
- *      qIds of Gate RX is 1, 2
- *      This should return [{qId: 1}, {qId: 2}]
- */
-const _targets = (operation: Operation): Register[] | [] => {
-    const _recurse = (operation: Operation) => {
-        registers.push(...operation.targets);
-        if (operation.controls) {
-            registers.push(...operation.controls);
-            // If there is more children, keep adding more to registers
-            if (operation.children) {
-                for (const child of operation.children) {
-                    _recurse(child);
-                }
-            }
-        }
-    };
-
-    const registers: Register[] = [];
-    if (operation.children == null) return [];
-
-    // Recursively walkthrough all children to populate registers
-    for (const child of operation.children) {
-        _recurse(child);
-    }
-
-    // Extract qIds from array of object
-    // i.e. [{qId: 0}, {qId: 1}, {qId: 1}] -> [0, 1, 1]
-    const qIds = registers.map((register) => register.qId);
-    const uniqueQIds = Array.from(new Set(qIds));
-
-    // Transform array of numbers into array of qId object
-    // i.e. [0, 1] -> [{qId: 0}, {qId: 1}]
-    return uniqueQIds.map((qId) => ({
-        qId,
-    }));
-};
-
-/**
- * This modulo function always returns positive value based on total
- * i.e: value=0, offset=-1, total=4 returns 3 instead of -1
- */
-const _circularMod = (value: number, offset: number, total: number): number => {
-    return (((value + offset) % total) + total) % total;
-};
-
-/**
- * Split data-id into an array of indexes
- */
-const _indexes = (dataId: string): number[] =>
-    dataId !== '' //
-        ? dataId.split('-').map((segment) => parseInt(segment))
-        : [];
-
-/**
- * Get the last index of data-id
- * i.e: data-id = "0-1-2", _lastIndex will return 2
- */
-const _lastIndex = (dataId: string): number | undefined => {
-    return _indexes(dataId).pop();
-};
-
-/**
- * Return a render function with the onCircuitChange callback attached to it
- */
-const _renderFn = (
-    container: HTMLElement,
-    sqore: Sqore,
-    onCircuitChange?: (circuit: Circuit) => void,
-): (() => void) => {
-    return () => {
-        sqore.draw(container, 0, true, onCircuitChange);
-        if (onCircuitChange) onCircuitChange(sqore.circuit);
-    };
-};
-
-/**
- * Object exported for unit testing
- */
-const exportedForTesting = {
-    _wireYs,
-    _hostElems,
-    _wirePrefixes,
-    _center,
-    _wireData,
-    _equivGateElem,
-    _equivOperation,
-    _equivParentOperation,
-    _equivParentArray,
-    _moveX,
-    _copyX,
-    _moveY,
-    _offsetRecursively,
-    _targets,
-    _circularMod,
-    _indexes,
-    _lastIndex,
-};
-
-export { addEditable, exportedForTesting };
diff --git a/src/sqore.ts b/src/sqore.ts
index caa7c9f7..f188d1a2 100644
--- a/src/sqore.ts
+++ b/src/sqore.ts
@@ -4,11 +4,11 @@
 import { Circuit, ConditionalRender, Operation } from './circuit';
 import { svgNS } from './constants';
 import { extensionDraggable } from './draggable';
-import { extensionPanel, PanelOptions } from './panel';
 import { formatGates } from './formatters/gateFormatter';
 import { formatInputs } from './formatters/inputFormatter';
 import { formatRegisters } from './formatters/registerFormatter';
 import { GateType, Metadata } from './metadata';
+import { extensionPanel, PanelOptions } from './panel';
 import { processOperations } from './process';
 import { style, StyleConfig, STYLES } from './styles';
 import { createUUID } from './utils';
@@ -249,18 +249,11 @@ export class Sqore {
      *
      * @param container HTML element containing visualized circuit.
      * @param circuit Circuit to be visualized.
-     * @param isEditable Optional value enabling/disabling editable feature
-     * @param onCircuitChange Optional function to trigger when changing elements in circuit
      *
      */
-    private addGateClickHandlers(
-        container: HTMLElement,
-        circuit: Circuit,
-        isEditable?: boolean,
-        onCircuitChange?: (circuit: Circuit) => void,
-    ): void {
+    private addGateClickHandlers(container: HTMLElement, circuit: Circuit): void {
         this.addClassicalControlHandlers(container);
-        this.addZoomHandlers(container, circuit, isEditable, onCircuitChange);
+        this.addZoomHandlers(container, circuit);
     }
 
     /**
@@ -320,12 +313,7 @@ export class Sqore {
      * @param onCircuitChange Optional function to trigger when changing elements in circuit
      *
      */
-    private addZoomHandlers(
-        container: HTMLElement,
-        circuit: Circuit,
-        isEditable?: boolean,
-        onCircuitChange?: (circuit: Circuit) => void,
-    ): void {
+    private addZoomHandlers(container: HTMLElement, circuit: Circuit): void {
         container.querySelectorAll('.gate .gate-control').forEach((ctrl) => {
             // Zoom in on clicked gate
             ctrl.addEventListener('click', (ev: Event) => {

From d82dedabc1257df0113fa54ba1202f3688d5325c Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 1 Aug 2022 12:11:45 -0400
Subject: [PATCH 090/108] Remove old tests

---
 __tests__/editable.test.ts | 817 -------------------------------------
 1 file changed, 817 deletions(-)
 delete mode 100644 __tests__/editable.test.ts

diff --git a/__tests__/editable.test.ts b/__tests__/editable.test.ts
deleted file mode 100644
index 0fde4705..00000000
--- a/__tests__/editable.test.ts
+++ /dev/null
@@ -1,817 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT license.
-
-import { exportedForTesting } from '../src/editable';
-import { Circuit, draw, Operation, STYLES } from '../src/index';
-
-const {
-    _wireYs,
-    _hostElems,
-    _wirePrefixes,
-    _center,
-    _wireData,
-    _equivGateElem,
-    _equivOperation,
-    _equivParentOperation,
-    _equivParentArray,
-    _moveX,
-    _copyX,
-    _moveY,
-    _offsetRecursively,
-    _targets,
-    _circularMod,
-    _indexes,
-    _lastIndex,
-} = exportedForTesting;
-
-describe('Test _hostElems', () => {
-    let container: HTMLElement;
-    beforeAll(() => {
-        container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-    });
-    test('should return 4 elements', () => {
-        expect(_hostElems(container)).toMatchSnapshot();
-        expect(_hostElems(container)).toHaveLength(4);
-    });
-});
-
-describe('Test _wireYs', () => {
-    test('should return [40,100]', () => {
-        Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
-            writable: true,
-            value: () => ({
-                x: 0,
-                y: 20,
-                width: 0,
-                height: 120,
-            }),
-        });
-        const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        expect(_wireYs(elem, [40, 100, 140])).toStrictEqual([40, 100]);
-    });
-});
-
-describe('Test _wirePrefixes', () => {
-    test('2 wires', () => {
-        expect(_wirePrefixes([40, 100])).toStrictEqual([
-            { index: 0, prefixX: 40, wireY: 40 },
-            { index: 1, prefixX: 40, wireY: 100 },
-        ]);
-    });
-    test('3 wires', () => {
-        expect(_wirePrefixes([40, 100, 140])).toStrictEqual([
-            { index: 0, prefixX: 40, wireY: 40 },
-            { index: 1, prefixX: 40, wireY: 100 },
-            { index: 2, prefixX: 40, wireY: 140 },
-        ]);
-    });
-});
-
-describe('Test _center', () => {
-    test('should return {25,50}', () => {
-        Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
-            writable: true,
-            value: () => ({
-                x: 0,
-                y: 0,
-                width: 50,
-                height: 100,
-            }),
-        });
-        const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        expect(_center(elem)).toStrictEqual({ cX: 25, cY: 50 });
-    });
-    test('should return {105,210}', () => {
-        Object.defineProperty(window.SVGElement.prototype, 'getBBox', {
-            writable: true,
-            value: () => ({
-                x: 100,
-                y: 200,
-                width: 10,
-                height: 20,
-            }),
-        });
-        const elem = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
-        expect(_center(elem)).toStrictEqual({ cX: 105, cY: 210 });
-    });
-});
-
-describe('Test _wireData', () => {
-    test('2 wires should return [40,100]', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-        expect(_wireData(container)).toStrictEqual([40, 100]);
-    });
-    test('3 wires should return [40,100, 180]', () => {
-        const container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }, { id: 2 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 2 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-        expect(_wireData(container)).toStrictEqual([40, 100, 180]);
-    });
-});
-
-describe('Test _equivGateElem', () => {
-    let container: HTMLElement;
-    beforeAll(() => {
-        container = document.createElement('div');
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        draw(circuit, container, STYLES['default']);
-    });
-    test('should return gate H', () => {
-        const elem = container.querySelector('[class^="gate-"]') as SVGElement;
-        expect(_equivGateElem(elem)).toMatchSnapshot();
-    });
-});
-
-describe('Test _equivOperation', () => {
-    const circuit = {
-        qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-        operations: [
-            {
-                gate: 'H',
-                targets: [{ qId: 0 }],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [{ qId: 0 }],
-                targets: [{ qId: 1 }],
-            },
-            {
-                gate: 'Measure',
-                isMeasurement: true,
-                controls: [{ qId: 1 }],
-                targets: [{ type: 1, qId: 1, cId: 0 }],
-            },
-        ],
-    };
-    test('should return H gate', () => {
-        expect(_equivOperation('0', circuit.operations)).toMatchSnapshot();
-    });
-    test('should return X gate', () => {
-        expect(_equivOperation('1', circuit.operations)).toMatchSnapshot();
-    });
-});
-
-describe('Test _equivParentOperation', () => {
-    test('should return Foo', () => {
-        const circuit = {
-            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
-            operations: [
-                {
-                    gate: 'Foo',
-                    conditionalRender: 3,
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                        },
-                        {
-                            gate: 'RX',
-                            displayArgs: '(0.25)',
-                            isControlled: true,
-                            controls: [{ qId: 1 }],
-                            targets: [{ qId: 0 }],
-                        },
-                    ],
-                },
-                {
-                    gate: 'X',
-                    targets: [{ qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ qId: 2 }, { qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }, { qId: 3 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }, { qId: 3 }],
-                    targets: [{ qId: 2 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ type: 1, qId: 0, cId: 0 }],
-                },
-                {
-                    gate: 'ApplyIfElseR',
-                    isConditional: true,
-                    controls: [{ type: 1, qId: 0, cId: 0 }],
-                    targets: [],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            isControlled: true,
-                            controls: [{ qId: 0 }],
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 2,
-                        },
-                        {
-                            gate: 'Foo',
-                            targets: [{ qId: 3 }],
-                            conditionalRender: 2,
-                        },
-                    ],
-                },
-                {
-                    gate: 'SWAP',
-                    targets: [{ qId: 0 }, { qId: 2 }],
-                    children: [
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                    ],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }, { qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-            ],
-        };
-        expect(_equivParentOperation('0-1', circuit.operations)).toMatchSnapshot();
-    });
-});
-
-describe('Test _equivParentArray', () => {
-    test('should return Foo', () => {
-        const circuit = {
-            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
-            operations: [
-                {
-                    gate: 'Foo',
-                    conditionalRender: 3,
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                        },
-                        {
-                            gate: 'RX',
-                            displayArgs: '(0.25)',
-                            isControlled: true,
-                            controls: [{ qId: 1 }],
-                            targets: [{ qId: 0 }],
-                        },
-                    ],
-                },
-                {
-                    gate: 'X',
-                    targets: [{ qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ qId: 2 }, { qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }, { qId: 3 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }, { qId: 3 }],
-                    targets: [{ qId: 2 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ type: 1, qId: 0, cId: 0 }],
-                },
-                {
-                    gate: 'ApplyIfElseR',
-                    isConditional: true,
-                    controls: [{ type: 1, qId: 0, cId: 0 }],
-                    targets: [],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            isControlled: true,
-                            controls: [{ qId: 0 }],
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 2,
-                        },
-                        {
-                            gate: 'Foo',
-                            targets: [{ qId: 3 }],
-                            conditionalRender: 2,
-                        },
-                    ],
-                },
-                {
-                    gate: 'SWAP',
-                    targets: [{ qId: 0 }, { qId: 2 }],
-                    children: [
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                    ],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }, { qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-            ],
-        };
-        expect(_equivParentArray('0-1', circuit.operations)).toMatchSnapshot();
-    });
-    test('should return all operations', () => {
-        const circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-        expect(_equivParentArray('0', circuit.operations)).toMatchSnapshot();
-    });
-});
-
-describe('Test _moveX', () => {
-    let circuit: Circuit;
-    beforeEach(() => {
-        circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-    });
-    test('move elem from index 0 to index 1', () => {
-        _moveX('0', '2', circuit.operations);
-        expect(circuit.operations).toMatchSnapshot();
-    });
-    test('move elem from index 0 to last', () => {
-        _moveX('0', '3', circuit.operations);
-        expect(circuit.operations).toMatchSnapshot();
-    });
-});
-
-describe('Test _copyX', () => {
-    let circuit: Circuit;
-    beforeEach(() => {
-        circuit = {
-            qubits: [{ id: 0 }, { id: 1, numChildren: 1 }],
-            operations: [
-                {
-                    gate: 'H',
-                    targets: [{ qId: 0 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'Measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ type: 1, qId: 1, cId: 0 }],
-                },
-            ],
-        };
-    });
-    test('copy elem from index 0 to index 1', () => {
-        _copyX('0', '2', circuit.operations);
-        expect(circuit.operations).toMatchSnapshot();
-    });
-    test('copy elem from index 0 to last', () => {
-        _copyX('0', '3', circuit.operations);
-        expect(circuit.operations).toMatchSnapshot();
-    });
-});
-
-describe('Test _moveY', () => {
-    let operation: Operation;
-    beforeEach(() => {
-        operation = {
-            gate: 'ZZ',
-            targets: [{ qId: 1 }, { qId: 3 }],
-        };
-    });
-    test('offset by 1', () => {
-        _moveY('1', '2', operation, 4);
-        expect(operation).toStrictEqual({
-            gate: 'ZZ',
-            targets: [{ qId: 2 }, { qId: 0 }],
-        });
-    });
-    test('offset by -3', () => {
-        _moveY('3', '0', operation, 4);
-        expect(operation).toStrictEqual({
-            gate: 'ZZ',
-            targets: [{ qId: 2 }, { qId: 0 }],
-        });
-    });
-});
-
-describe('Test _offsetRecursively', () => {
-    let operation: Operation;
-    beforeEach(() => {
-        operation = {
-            gate: 'ZZ',
-            targets: [{ qId: 1 }, { qId: 3 }],
-        };
-    });
-    test('offset by 1', () => {
-        _offsetRecursively(operation, 1, 4);
-        expect(operation).toStrictEqual({
-            gate: 'ZZ',
-            targets: [{ qId: 2 }, { qId: 0 }],
-        });
-    });
-    test('offset by 2', () => {
-        _offsetRecursively(operation, 2, 4);
-        expect(operation).toStrictEqual({
-            gate: 'ZZ',
-            targets: [{ qId: 3 }, { qId: 1 }],
-        });
-    });
-});
-
-describe('Test _targets', () => {
-    let circuit: Circuit;
-    beforeEach(() => {
-        circuit = {
-            qubits: [{ id: 0, numChildren: 1 }, { id: 1 }, { id: 2 }, { id: 3 }],
-            operations: [
-                {
-                    gate: 'Foo',
-                    conditionalRender: 3,
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                        },
-                        {
-                            gate: 'RX',
-                            displayArgs: '(0.25)',
-                            isControlled: true,
-                            controls: [{ qId: 1 }],
-                            targets: [{ qId: 0 }],
-                        },
-                    ],
-                },
-                {
-                    gate: 'X',
-                    targets: [{ qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }],
-                    targets: [{ qId: 2 }, { qId: 3 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }, { qId: 3 }],
-                    targets: [{ qId: 1 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 1 }, { qId: 3 }],
-                    targets: [{ qId: 2 }],
-                },
-                {
-                    gate: 'X',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'measure',
-                    isMeasurement: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ type: 1, qId: 0, cId: 0 }],
-                },
-                {
-                    gate: 'ApplyIfElseR',
-                    isConditional: true,
-                    controls: [{ type: 1, qId: 0, cId: 0 }],
-                    targets: [],
-                    children: [
-                        {
-                            gate: 'H',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 1,
-                        },
-                        {
-                            gate: 'X',
-                            isControlled: true,
-                            controls: [{ qId: 0 }],
-                            targets: [{ qId: 1 }],
-                            conditionalRender: 2,
-                        },
-                        {
-                            gate: 'Foo',
-                            targets: [{ qId: 3 }],
-                            conditionalRender: 2,
-                        },
-                    ],
-                },
-                {
-                    gate: 'SWAP',
-                    targets: [{ qId: 0 }, { qId: 2 }],
-                    children: [
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 2 }], targets: [{ qId: 0 }] },
-                        { gate: 'X', isControlled: true, controls: [{ qId: 0 }], targets: [{ qId: 2 }] },
-                    ],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'ZZ',
-                    targets: [{ qId: 0 }, { qId: 1 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-                {
-                    gate: 'XX',
-                    isControlled: true,
-                    controls: [{ qId: 0 }, { qId: 2 }],
-                    targets: [{ qId: 1 }, { qId: 3 }],
-                },
-            ],
-        };
-    });
-    test('move RX down 1, should return [{qId:1}, {qId:2}]', () => {
-        const parentOperation = circuit.operations[0];
-        const parentArray = parentOperation.children;
-        if (parentArray) {
-            const operation = parentArray[1];
-            _moveY('0', '1', operation, 4);
-        }
-        expect(_targets(parentOperation)).toStrictEqual([{ qId: 1 }, { qId: 2 }]);
-    });
-    test('move RX down 2, should return [{qId:1}, {qId:2}, {qId:3}]', () => {
-        const parentOperation = circuit.operations[0];
-        const parentArray = parentOperation.children;
-        if (parentArray) {
-            const operation = parentArray[1];
-            _moveY('0', '2', operation, 4);
-        }
-        expect(_targets(parentOperation)).toStrictEqual([{ qId: 1 }, { qId: 2 }, { qId: 3 }]);
-    });
-});
-
-describe('Test _circularMod', () => {
-    test('should return 2', () => {
-        expect(_circularMod(5, 1, 4)).toEqual(2);
-    });
-    test('should return 1', () => {
-        expect(_circularMod(100, 1, 2)).toEqual(1);
-    });
-    test('should return 3', () => {
-        expect(_circularMod(3, 0, 4)).toEqual(3);
-    });
-});
-
-describe('Test _lastIndex', () => {
-    test('"" should return undefined', () => {
-        expect(_lastIndex('')).toBeUndefined();
-    });
-    test('"0-0-1" should return 1', () => {
-        expect(_lastIndex('0-0-1')).toEqual(1);
-    });
-    test('"1-0-5" should return [1,0,5]', () => {
-        expect(_lastIndex('1-0-5')).toEqual(5);
-    });
-});
-
-describe('Test _indexes', () => {
-    test('"" should return []', () => {
-        expect(_indexes('')).toStrictEqual([]);
-    });
-    test('"0-0-1" should return [0,0,1]', () => {
-        expect(_indexes('0-0-1')).toStrictEqual([0, 0, 1]);
-    });
-    test('"1-0-1" should return [1,0,1]', () => {
-        expect(_indexes('1-0-1')).toStrictEqual([1, 0, 1]);
-    });
-});

From b79b88b2a3acbfe07c7a35db29b5ca5ef0a7093b Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Mon, 1 Aug 2022 12:14:15 -0400
Subject: [PATCH 091/108] Remove old snapshots

---
 __tests__/__snapshots__/editable.test.ts.snap | 402 ------------------
 1 file changed, 402 deletions(-)
 delete mode 100644 __tests__/__snapshots__/editable.test.ts.snap

diff --git a/__tests__/__snapshots__/editable.test.ts.snap b/__tests__/__snapshots__/editable.test.ts.snap
deleted file mode 100644
index c29c1bad..00000000
--- a/__tests__/__snapshots__/editable.test.ts.snap
+++ /dev/null
@@ -1,402 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Test _copyX copy elem from index 0 to index 1 1`] = `
-Array [
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-    "gate": "X",
-    "isControlled": true,
-    "targets": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-  },
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-    "gate": "Measure",
-    "isMeasurement": true,
-    "targets": Array [
-      Object {
-        "cId": 0,
-        "qId": 1,
-        "type": 1,
-      },
-    ],
-  },
-]
-`;
-
-exports[`Test _copyX copy elem from index 0 to last 1`] = `
-Array [
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-    "gate": "X",
-    "isControlled": true,
-    "targets": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-    "gate": "Measure",
-    "isMeasurement": true,
-    "targets": Array [
-      Object {
-        "cId": 0,
-        "qId": 1,
-        "type": 1,
-      },
-    ],
-  },
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-]
-`;
-
-exports[`Test _equivGateElem should return gate H 1`] = `
-<g
-  class="gate"
-  data-id="0"
-  data-zoom-in="false"
-  data-zoom-out="false"
->
-  <g>
-    <g>
-      <rect
-        class="gate-unitary"
-        height="40"
-        width="40"
-        x="80"
-        y="20"
-      />
-      <text
-        font-size="14"
-        x="100"
-        y="40"
-      >
-        H
-      </text>
-    </g>
-  </g>
-</g>
-`;
-
-exports[`Test _equivOperation should return H gate 1`] = `
-Object {
-  "gate": "H",
-  "targets": Array [
-    Object {
-      "qId": 0,
-    },
-  ],
-}
-`;
-
-exports[`Test _equivOperation should return X gate 1`] = `
-Object {
-  "controls": Array [
-    Object {
-      "qId": 0,
-    },
-  ],
-  "gate": "X",
-  "isControlled": true,
-  "targets": Array [
-    Object {
-      "qId": 1,
-    },
-  ],
-}
-`;
-
-exports[`Test _equivParentArray should return Foo 1`] = `
-Array [
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-    "displayArgs": "(0.25)",
-    "gate": "RX",
-    "isControlled": true,
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-]
-`;
-
-exports[`Test _equivParentArray should return all operations 1`] = `
-Array [
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-    "gate": "X",
-    "isControlled": true,
-    "targets": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-    "gate": "Measure",
-    "isMeasurement": true,
-    "targets": Array [
-      Object {
-        "cId": 0,
-        "qId": 1,
-        "type": 1,
-      },
-    ],
-  },
-]
-`;
-
-exports[`Test _equivParentOperation should return Foo 1`] = `
-Object {
-  "children": Array [
-    Object {
-      "gate": "H",
-      "targets": Array [
-        Object {
-          "qId": 1,
-        },
-      ],
-    },
-    Object {
-      "controls": Array [
-        Object {
-          "qId": 1,
-        },
-      ],
-      "displayArgs": "(0.25)",
-      "gate": "RX",
-      "isControlled": true,
-      "targets": Array [
-        Object {
-          "qId": 0,
-        },
-      ],
-    },
-  ],
-  "conditionalRender": 3,
-  "gate": "Foo",
-  "targets": Array [
-    Object {
-      "qId": 0,
-    },
-    Object {
-      "qId": 1,
-    },
-  ],
-}
-`;
-
-exports[`Test _hostElems should return 4 elements 1`] = `
-Array [
-  <rect
-    class="gate-unitary"
-    height="40"
-    width="40"
-    x="80"
-    y="20"
-  />,
-  <circle
-    class="control-dot"
-    cx="160"
-    cy="40"
-    r="5"
-  />,
-  <g
-    class="oplus"
-  >
-    <circle
-      cx="160"
-      cy="100"
-      r="15"
-    />
-    <line
-      x1="160"
-      x2="160"
-      y1="85"
-      y2="115"
-    />
-    <line
-      x1="145"
-      x2="175"
-      y1="100"
-      y2="100"
-    />
-  </g>,
-  <rect
-    class="gate-measure"
-    height="40"
-    width="40"
-    x="200"
-    y="80"
-  />,
-]
-`;
-
-exports[`Test _moveX move elem from index 0 to index 1 1`] = `
-Array [
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-    "gate": "X",
-    "isControlled": true,
-    "targets": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-  },
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-    "gate": "Measure",
-    "isMeasurement": true,
-    "targets": Array [
-      Object {
-        "cId": 0,
-        "qId": 1,
-        "type": 1,
-      },
-    ],
-  },
-]
-`;
-
-exports[`Test _moveX move elem from index 0 to last 1`] = `
-Array [
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-    "gate": "X",
-    "isControlled": true,
-    "targets": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-  },
-  Object {
-    "controls": Array [
-      Object {
-        "qId": 1,
-      },
-    ],
-    "gate": "Measure",
-    "isMeasurement": true,
-    "targets": Array [
-      Object {
-        "cId": 0,
-        "qId": 1,
-        "type": 1,
-      },
-    ],
-  },
-  Object {
-    "gate": "H",
-    "targets": Array [
-      Object {
-        "qId": 0,
-      },
-    ],
-  },
-]
-`;

From 5e7ec222cb4c95912a784f92d383d6beb1dcdd9f Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 2 Aug 2022 17:06:30 -0400
Subject: [PATCH 092/108] Increase panelWidth from 144 to 150

---
 src/constants.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/constants.ts b/src/constants.ts
index 71123456..d98bbb66 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -40,7 +40,7 @@ export const regLineStart = 40;
 
 // Panel
 /** Panel width */
-export const panelWidth = 144;
+export const panelWidth = 150;
 /** Gap between gates in Add Panel */
 export const horizontalGap = 10;
 export const verticalGap = 10;

From e36cb96f29d8107d75414e6887918caf4f737c53 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Tue, 2 Aug 2022 21:22:29 -0400
Subject: [PATCH 093/108] Update tests

---
 __tests__/__snapshots__/panel.test.ts.snap | 100 ++++++++++-----------
 1 file changed, 50 insertions(+), 50 deletions(-)

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
index 3892b303..d6848421 100644
--- a/__tests__/__snapshots__/panel.test.ts.snap
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -109,13 +109,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="1"
-            y="101"
+            x="101"
+            y="51"
           />
           <text
             font-size="14"
-            x="21"
-            y="121"
+            x="121"
+            y="71"
           >
             H
           </text>
@@ -129,19 +129,19 @@ exports[`Test addPanel Should return default addPanel 1`] = `
         class="oplus"
       >
         <circle
-          cx="71"
+          cx="21"
           cy="121"
           r="15"
         />
         <line
-          x1="71"
-          x2="71"
+          x1="21"
+          x2="21"
           y1="106"
           y2="136"
         />
         <line
-          x1="56"
-          x2="86"
+          x1="6"
+          x2="36"
           y1="121"
           y2="121"
         />
@@ -156,13 +156,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="1"
-            y="151"
+            x="51"
+            y="101"
           />
           <text
             font-size="14"
-            x="21"
-            y="171"
+            x="71"
+            y="121"
           >
             S
           </text>
@@ -178,13 +178,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="51"
-            y="151"
+            x="101"
+            y="101"
           />
           <text
             font-size="14"
-            x="71"
-            y="171"
+            x="121"
+            y="121"
           >
             T
           </text>
@@ -201,12 +201,12 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             height="40"
             width="40"
             x="1"
-            y="201"
+            y="151"
           />
           <text
             font-size="14"
             x="21"
-            y="221"
+            y="171"
           >
             Y
           </text>
@@ -223,12 +223,12 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             height="40"
             width="40"
             x="51"
-            y="201"
+            y="151"
           />
           <text
             font-size="14"
             x="71"
-            y="221"
+            y="171"
           >
             Z
           </text>
@@ -244,13 +244,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="1"
-            y="251"
+            x="101"
+            y="151"
           />
           <text
             font-size="14"
-            x="21"
-            y="271"
+            x="121"
+            y="171"
           >
             ZZ
           </text>
@@ -736,13 +736,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="1"
-              y="101"
+              x="101"
+              y="51"
             />
             <text
               font-size="14"
-              x="21"
-              y="121"
+              x="121"
+              y="71"
             >
               H
             </text>
@@ -756,19 +756,19 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
           class="oplus"
         >
           <circle
-            cx="71"
+            cx="21"
             cy="121"
             r="15"
           />
           <line
-            x1="71"
-            x2="71"
+            x1="21"
+            x2="21"
             y1="106"
             y2="136"
           />
           <line
-            x1="56"
-            x2="86"
+            x1="6"
+            x2="36"
             y1="121"
             y2="121"
           />
@@ -783,13 +783,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="1"
-              y="151"
+              x="51"
+              y="101"
             />
             <text
               font-size="14"
-              x="21"
-              y="171"
+              x="71"
+              y="121"
             >
               S
             </text>
@@ -805,13 +805,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="51"
-              y="151"
+              x="101"
+              y="101"
             />
             <text
               font-size="14"
-              x="71"
-              y="171"
+              x="121"
+              y="121"
             >
               T
             </text>
@@ -828,12 +828,12 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               height="40"
               width="40"
               x="1"
-              y="201"
+              y="151"
             />
             <text
               font-size="14"
               x="21"
-              y="221"
+              y="171"
             >
               Y
             </text>
@@ -850,12 +850,12 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               height="40"
               width="40"
               x="51"
-              y="201"
+              y="151"
             />
             <text
               font-size="14"
               x="71"
-              y="221"
+              y="171"
             >
               Z
             </text>
@@ -871,13 +871,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="1"
-              y="251"
+              x="101"
+              y="151"
             />
             <text
               font-size="14"
-              x="21"
-              y="271"
+              x="121"
+              y="171"
             >
               ZZ
             </text>

From 4e4ff7cc87f04561139300c0d596154e05db5343 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 09:59:23 -0400
Subject: [PATCH 094/108] Rename Display to Parameters

---
 __tests__/__snapshots__/panel.test.ts.snap | 8 ++++----
 src/panel.ts                               | 2 +-
 src/styles.ts                              | 2 +-
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
index d6848421..888eebab 100644
--- a/__tests__/__snapshots__/panel.test.ts.snap
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -497,12 +497,12 @@ exports[`Test editPanel Should return editPanel editing X gate 1`] = `
     </label>
   </div>
   <div
-    class="display-input"
+    class="parameters-input"
   >
     <label
       class="block"
     >
-      Display
+      Parameters
     </label>
     <input
       autofocus="true"
@@ -946,12 +946,12 @@ exports[`Test panel Should return panel with editPanel visible 1`] = `
       </label>
     </div>
     <div
-      class="display-input"
+      class="parameters-input"
     >
       <label
         class="block"
       >
-        Display
+        Parameters
       </label>
       <input
         autofocus="true"
diff --git a/src/panel.ts b/src/panel.ts
index d22b4f02..491b3772 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -335,7 +335,7 @@ const editPanel = (dispatch: Dispatch, context: Context): HTMLElement => {
         title('EDIT'),
         select('Target', 'target-input', options, target || 0, dispatch, operation),
         checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
-        text('Display', 'display-input', dispatch, operation),
+        text('Parameters', 'parameters-input', dispatch, operation),
     ]);
     return editPanelElem;
 };
diff --git a/src/styles.ts b/src/styles.ts
index 68ee107e..ee7ee3d9 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -287,7 +287,7 @@ const _panel = `
     .block {
         display: block;
     }
-    .target-input, .controls-input, .display-input {
+    .target-input, .controls-input, .parameters-input {
         margin-bottom: 8px;
     }
     .add-panel-gate {

From 651efc28cfd2f24fee0aa5c9832e675071773681 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 10:15:49 -0400
Subject: [PATCH 095/108] Fix missing px in width

---
 src/styles.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/styles.ts b/src/styles.ts
index ee7ee3d9..3e9b8440 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -271,7 +271,7 @@ const _panel = `
         display: flex;
     }
     .panel {
-        width: ${panelWidth};
+        width: ${panelWidth}px;
         border: solid 1px black;
         padding: 8px;
     }

From b77fe2de10abaa3672750b28c5d41f174c7eca80 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 10:18:32 -0400
Subject: [PATCH 096/108] Not display Parameters on X (Not) gate

---
 __tests__/__snapshots__/panel.test.ts.snap | 26 ----------------------
 src/panel.ts                               |  5 ++++-
 2 files changed, 4 insertions(+), 27 deletions(-)

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
index 888eebab..53447d6f 100644
--- a/__tests__/__snapshots__/panel.test.ts.snap
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -496,19 +496,6 @@ exports[`Test editPanel Should return editPanel editing X gate 1`] = `
       q1
     </label>
   </div>
-  <div
-    class="parameters-input"
-  >
-    <label
-      class="block"
-    >
-      Parameters
-    </label>
-    <input
-      autofocus="true"
-      type="text"
-    />
-  </div>
 </div>
 `;
 
@@ -945,19 +932,6 @@ exports[`Test panel Should return panel with editPanel visible 1`] = `
         q1
       </label>
     </div>
-    <div
-      class="parameters-input"
-    >
-      <label
-        class="block"
-      >
-        Parameters
-      </label>
-      <input
-        autofocus="true"
-        type="text"
-      />
-    </div>
   </div>
 </div>
 `;
diff --git a/src/panel.ts b/src/panel.ts
index 491b3772..92f3a6d2 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -330,13 +330,16 @@ const editPanel = (dispatch: Dispatch, context: Context): HTMLElement => {
     const target = operation?.targets[0].qId;
     const controls = operation?.controls?.map((control) => control.qId);
 
+    const shouldDisplayParameters = operation?.gate !== 'X';
+
     const editPanelElem = elem('div', 'edit-panel');
     children(editPanelElem, [
         title('EDIT'),
         select('Target', 'target-input', options, target || 0, dispatch, operation),
         checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
-        text('Parameters', 'parameters-input', dispatch, operation),
     ]);
+    shouldDisplayParameters && editPanelElem.appendChild(text('Parameters', 'parameters-input', dispatch, operation));
+
     return editPanelElem;
 };
 

From 943ce1c94a82487d461135aab788a9de4d8f358f Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 10:36:15 -0400
Subject: [PATCH 097/108] Add conditions to element renders

---
 src/panel.ts | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 92f3a6d2..8af2e50b 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -330,14 +330,18 @@ const editPanel = (dispatch: Dispatch, context: Context): HTMLElement => {
     const target = operation?.targets[0].qId;
     const controls = operation?.controls?.map((control) => control.qId);
 
-    const shouldDisplayParameters = operation?.gate !== 'X';
+    const shouldDisplayTarget = operation?.gate.toLowerCase() !== 'measure';
+    const shouldDisplayControls = operation?.gate.toLowerCase() !== 'measure';
+    const shouldDisplayParameters = operation?.gate !== 'X' && operation?.gate.toLowerCase() !== 'measure';
 
     const editPanelElem = elem('div', 'edit-panel');
-    children(editPanelElem, [
-        title('EDIT'),
-        select('Target', 'target-input', options, target || 0, dispatch, operation),
-        checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
-    ]);
+    children(editPanelElem, [title('EDIT')]);
+    shouldDisplayTarget &&
+        editPanelElem.appendChild(select('Target', 'target-input', options, target || 0, dispatch, operation));
+    shouldDisplayControls &&
+        editPanelElem.appendChild(
+            checkboxes('Controls', 'controls-input', options, controls || [], dispatch, operation),
+        );
     shouldDisplayParameters && editPanelElem.appendChild(text('Parameters', 'parameters-input', dispatch, operation));
 
     return editPanelElem;

From 6af8e755d242f4e0115c322da3ddde9fa154cd30 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 11:18:21 -0400
Subject: [PATCH 098/108] Fix composed gates disappearing

---
 src/draggable.ts | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index 6c05f32c..a1abcba7 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -271,14 +271,16 @@ const _addEvents = (context: Context) => {
     elems.forEach((elem) => {
         elem.addEventListener('mousedown', () => {
             context.selectedWire = elem.getAttribute('data-wire');
-            container.classList.add('moving');
         });
 
         const gateElem = _equivGateElem(elem);
         gateElem?.addEventListener('mousedown', (ev: MouseEvent) => {
             ev.stopPropagation();
-            context.selectedId = _equivDataId(elem);
-            dropzoneLayer.style.display = 'block';
+            if (gateElem.getAttribute('data-expanded') !== 'true') {
+                context.selectedId = _equivDataId(elem);
+                container.classList.add('moving');
+                dropzoneLayer.style.display = 'block';
+            }
         });
     });
 

From 4c3ec4d97412b7cd5ff18783d320a45d8fe4ed57 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 11:41:22 -0400
Subject: [PATCH 099/108] Add Panel supports composed gates

---
 __tests__/panel.test.ts | 26 +++++++++++++++++++++++++-
 src/panel.ts            | 36 ++++++++++++++++++++++++++++++++++--
 2 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/__tests__/panel.test.ts b/__tests__/panel.test.ts
index 10256e60..1a28d0bf 100644
--- a/__tests__/panel.test.ts
+++ b/__tests__/panel.test.ts
@@ -237,7 +237,31 @@ describe('Test defaulGateDictionary', () => {
         const expected = {
             Entangle: {
                 gate: 'Entangle',
-                targets: [{ qId: 0 }],
+                targets: [{ qId: 0 }, { qId: 1 }],
+                children: [
+                    {
+                        gate: 'H',
+                        targets: [
+                            {
+                                qId: 0,
+                            },
+                        ],
+                    },
+                    {
+                        gate: 'X',
+                        isControlled: true,
+                        controls: [
+                            {
+                                qId: 0,
+                            },
+                        ],
+                        targets: [
+                            {
+                                qId: 1,
+                            },
+                        ],
+                    },
+                ],
             },
             RX: {
                 gate: 'RX',
diff --git a/src/panel.ts b/src/panel.ts
index 8af2e50b..1e28c42d 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -171,7 +171,15 @@ const update = (action: Action, context: Context, useRefresh: () => void): void
         case 'TARGET': {
             const { operation } = context;
             const payload = action.payload as Register[];
-            operation && (operation.targets = payload);
+            if (operation) {
+                const difference = payload[0].qId - operation.targets[0].qId;
+                const newTargets = operation.targets.map((target) => {
+                    const { qId } = target;
+                    const newQId = qId + difference;
+                    return { qId: newQId };
+                });
+                operation.targets = newTargets;
+            }
             useRefresh();
             break;
         }
@@ -643,7 +651,31 @@ interface GateDictionary {
 const defaultGateDictionary: GateDictionary = {
     Entangle: {
         gate: 'Entangle',
-        targets: [{ qId: 0 }],
+        targets: [{ qId: 0 }, { qId: 1 }],
+        children: [
+            {
+                gate: 'H',
+                targets: [
+                    {
+                        qId: 0,
+                    },
+                ],
+            },
+            {
+                gate: 'X',
+                isControlled: true,
+                controls: [
+                    {
+                        qId: 0,
+                    },
+                ],
+                targets: [
+                    {
+                        qId: 1,
+                    },
+                ],
+            },
+        ],
     },
     RX: {
         gate: 'RX',

From 89a91f17c1003b9aba77dbd20b01b42763650426 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 12:00:56 -0400
Subject: [PATCH 100/108] Fix composed gate children don't change targets

---
 src/draggable.ts | 1 +
 src/panel.ts     | 9 ++-------
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/src/draggable.ts b/src/draggable.ts
index a1abcba7..290ce6ab 100644
--- a/src/draggable.ts
+++ b/src/draggable.ts
@@ -553,5 +553,6 @@ export {
     _equivGateElem,
     _equivParentArray,
     _lastIndex,
+    _offsetRecursively,
     exportedForTesting,
 };
diff --git a/src/panel.ts b/src/panel.ts
index 1e28c42d..fe3c0b79 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -5,7 +5,7 @@ import cloneDeep from 'lodash/cloneDeep';
 import range from 'lodash/range';
 import { Operation } from './circuit';
 import { gateHeight, horizontalGap, minGateWidth, panelWidth, verticalGap } from './constants';
-import { _equivOperation, _equivParentArray, _lastIndex } from './draggable';
+import { _equivOperation, _equivParentArray, _lastIndex, _offsetRecursively } from './draggable';
 import { _formatGate } from './formatters/gateFormatter';
 import { GateType, Metadata } from './metadata';
 import { Register } from './register';
@@ -173,12 +173,7 @@ const update = (action: Action, context: Context, useRefresh: () => void): void
             const payload = action.payload as Register[];
             if (operation) {
                 const difference = payload[0].qId - operation.targets[0].qId;
-                const newTargets = operation.targets.map((target) => {
-                    const { qId } = target;
-                    const newQId = qId + difference;
-                    return { qId: newQId };
-                });
-                operation.targets = newTargets;
+                _offsetRecursively(operation, difference, context.registerSize);
             }
             useRefresh();
             break;

From 197fa8995efe42a7063322751e54d3db24e451d7 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 14:40:32 -0400
Subject: [PATCH 101/108] Set panelMinHeight

---
 src/constants.ts | 2 ++
 src/styles.ts    | 4 ++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/constants.ts b/src/constants.ts
index d98bbb66..a2415ffa 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -41,6 +41,8 @@ export const regLineStart = 40;
 // Panel
 /** Panel width */
 export const panelWidth = 150;
+/** Panel minimum height */
+export const panelMinHeight = 300;
 /** Gap between gates in Add Panel */
 export const horizontalGap = 10;
 export const verticalGap = 10;
diff --git a/src/styles.ts b/src/styles.ts
index 3e9b8440..9bd7d4e9 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -1,7 +1,7 @@
 // Copyright (c) Microsoft Corporation.
 // Licensed under the MIT license.
 
-import { gateHeight, minGateWidth, panelWidth } from './constants';
+import { gateHeight, minGateWidth, panelMinHeight, panelWidth } from './constants';
 
 /**
  * Provides configuration for CSS styles of visualization.
@@ -297,7 +297,7 @@ const _panel = `
     }
     .add-panel-svg {
         width: ${panelWidth}px;
-        height: 100%;
+        min-height: ${panelMinHeight}px;
     }
     .ghost {
         width: ${minGateWidth}px;

From 0f637be81795085a174218bbbca415ba022ab57f Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 14:41:02 -0400
Subject: [PATCH 102/108] Make customDictionary extended instead of replaced

---
 src/panel.ts | 33 ++++-----------------------------
 1 file changed, 4 insertions(+), 29 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index fe3c0b79..37be7a12 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -292,7 +292,10 @@ const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions):
     if (options != null) {
         const { displaySize, gateDictionary: optionGateDictionary } = options;
         displaySize && (objectKeys = objectKeys.slice(0, displaySize));
-        optionGateDictionary && (gateDictionary = optionGateDictionary);
+        if (optionGateDictionary) {
+            gateDictionary = { ...optionGateDictionary, ...defaultGateDictionary };
+            objectKeys = Object.keys(gateDictionary);
+        }
     }
 
     let prefixX = 0;
@@ -644,34 +647,6 @@ interface GateDictionary {
  * Object for default gate dictionary
  */
 const defaultGateDictionary: GateDictionary = {
-    Entangle: {
-        gate: 'Entangle',
-        targets: [{ qId: 0 }, { qId: 1 }],
-        children: [
-            {
-                gate: 'H',
-                targets: [
-                    {
-                        qId: 0,
-                    },
-                ],
-            },
-            {
-                gate: 'X',
-                isControlled: true,
-                controls: [
-                    {
-                        qId: 0,
-                    },
-                ],
-                targets: [
-                    {
-                        qId: 1,
-                    },
-                ],
-            },
-        ],
-    },
     RX: {
         gate: 'RX',
         targets: [{ qId: 0 }],

From 05f8af4323886a5ffefaed0235178a61cdaa64f2 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 14:46:33 -0400
Subject: [PATCH 103/108] Remove unnecessary useRefresh

---
 src/panel.ts | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/panel.ts b/src/panel.ts
index 37be7a12..52477d4b 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -205,7 +205,6 @@ const update = (action: Action, context: Context, useRefresh: () => void): void
             ) {
                 targetOperationParent.splice(targetLastIndex, 0, context.operation);
             }
-            useRefresh();
             break;
         }
         case 'DISPLAY_DROPZONE_LAYER': {
@@ -290,10 +289,10 @@ const addPanel = (dispatch: Dispatch, context: Context, options?: PanelOptions):
     let gateDictionary = defaultGateDictionary;
     let objectKeys = Object.keys(gateDictionary);
     if (options != null) {
-        const { displaySize, gateDictionary: optionGateDictionary } = options;
+        const { displaySize, gateDictionary: customGateDictionary } = options;
         displaySize && (objectKeys = objectKeys.slice(0, displaySize));
-        if (optionGateDictionary) {
-            gateDictionary = { ...optionGateDictionary, ...defaultGateDictionary };
+        if (customGateDictionary) {
+            gateDictionary = { ...defaultGateDictionary, ...customGateDictionary };
             objectKeys = Object.keys(gateDictionary);
         }
     }

From ae819ed48e7f1768f44fdeb60dc07cb3493cfd15 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 4 Aug 2022 14:48:20 -0400
Subject: [PATCH 104/108] Update tests

---
 __tests__/__snapshots__/panel.test.ts.snap | 204 ++++++++-------------
 __tests__/panel.test.ts                    |  28 ---
 2 files changed, 80 insertions(+), 152 deletions(-)

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
index 53447d6f..b598866f 100644
--- a/__tests__/__snapshots__/panel.test.ts.snap
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -20,16 +20,16 @@ exports[`Test addPanel Should return default addPanel 1`] = `
           <rect
             class="gate-unitary"
             height="40"
-            width="76"
+            width="40"
             x="1"
             y="1"
           />
           <text
             font-size="14"
-            x="39"
+            x="21"
             y="21"
           >
-            Entangle
+            RX
           </text>
         </g>
       </g>
@@ -43,35 +43,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="87"
+            x="51"
             y="1"
           />
           <text
             font-size="14"
-            x="107"
+            x="71"
             y="21"
-          >
-            RX
-          </text>
-        </g>
-      </g>
-    </g>
-    <g
-      class="gate"
-    >
-      <g>
-        <g>
-          <rect
-            class="gate-unitary"
-            height="40"
-            width="40"
-            x="1"
-            y="51"
-          />
-          <text
-            font-size="14"
-            x="21"
-            y="71"
           >
             RY
           </text>
@@ -87,13 +65,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="51"
-            y="51"
+            x="101"
+            y="1"
           />
           <text
             font-size="14"
-            x="71"
-            y="71"
+            x="121"
+            y="21"
           >
             RZ
           </text>
@@ -109,12 +87,12 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="101"
+            x="1"
             y="51"
           />
           <text
             font-size="14"
-            x="121"
+            x="21"
             y="71"
           >
             H
@@ -129,21 +107,21 @@ exports[`Test addPanel Should return default addPanel 1`] = `
         class="oplus"
       >
         <circle
-          cx="21"
-          cy="121"
+          cx="71"
+          cy="71"
           r="15"
         />
         <line
-          x1="21"
-          x2="21"
-          y1="106"
-          y2="136"
+          x1="71"
+          x2="71"
+          y1="56"
+          y2="86"
         />
         <line
-          x1="6"
-          x2="36"
-          y1="121"
-          y2="121"
+          x1="56"
+          x2="86"
+          y1="71"
+          y2="71"
         />
       </g>
     </g>
@@ -156,13 +134,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="51"
-            y="101"
+            x="101"
+            y="51"
           />
           <text
             font-size="14"
-            x="71"
-            y="121"
+            x="121"
+            y="71"
           >
             S
           </text>
@@ -178,12 +156,12 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="101"
+            x="1"
             y="101"
           />
           <text
             font-size="14"
-            x="121"
+            x="21"
             y="121"
           >
             T
@@ -200,13 +178,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="1"
-            y="151"
+            x="51"
+            y="101"
           />
           <text
             font-size="14"
-            x="21"
-            y="171"
+            x="71"
+            y="121"
           >
             Y
           </text>
@@ -222,13 +200,13 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="51"
-            y="151"
+            x="101"
+            y="101"
           />
           <text
             font-size="14"
-            x="71"
-            y="171"
+            x="121"
+            y="121"
           >
             Z
           </text>
@@ -244,12 +222,12 @@ exports[`Test addPanel Should return default addPanel 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="101"
+            x="1"
             y="151"
           />
           <text
             font-size="14"
-            x="121"
+            x="21"
             y="171"
           >
             ZZ
@@ -281,16 +259,16 @@ exports[`Test addPanel Should return default addPanel with displaySize 2 1`] = `
           <rect
             class="gate-unitary"
             height="40"
-            width="76"
+            width="40"
             x="1"
             y="1"
           />
           <text
             font-size="14"
-            x="39"
+            x="21"
             y="21"
           >
-            Entangle
+            RX
           </text>
         </g>
       </g>
@@ -304,15 +282,15 @@ exports[`Test addPanel Should return default addPanel with displaySize 2 1`] = `
             class="gate-unitary"
             height="40"
             width="40"
-            x="87"
+            x="51"
             y="1"
           />
           <text
             font-size="14"
-            x="107"
+            x="71"
             y="21"
           >
-            RX
+            RY
           </text>
         </g>
       </g>
@@ -634,16 +612,16 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
             <rect
               class="gate-unitary"
               height="40"
-              width="76"
+              width="40"
               x="1"
               y="1"
             />
             <text
               font-size="14"
-              x="39"
+              x="21"
               y="21"
             >
-              Entangle
+              RX
             </text>
           </g>
         </g>
@@ -657,35 +635,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="87"
+              x="51"
               y="1"
             />
             <text
               font-size="14"
-              x="107"
+              x="71"
               y="21"
-            >
-              RX
-            </text>
-          </g>
-        </g>
-      </g>
-      <g
-        class="gate"
-      >
-        <g>
-          <g>
-            <rect
-              class="gate-unitary"
-              height="40"
-              width="40"
-              x="1"
-              y="51"
-            />
-            <text
-              font-size="14"
-              x="21"
-              y="71"
             >
               RY
             </text>
@@ -701,13 +657,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="51"
-              y="51"
+              x="101"
+              y="1"
             />
             <text
               font-size="14"
-              x="71"
-              y="71"
+              x="121"
+              y="21"
             >
               RZ
             </text>
@@ -723,12 +679,12 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="101"
+              x="1"
               y="51"
             />
             <text
               font-size="14"
-              x="121"
+              x="21"
               y="71"
             >
               H
@@ -743,21 +699,21 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
           class="oplus"
         >
           <circle
-            cx="21"
-            cy="121"
+            cx="71"
+            cy="71"
             r="15"
           />
           <line
-            x1="21"
-            x2="21"
-            y1="106"
-            y2="136"
+            x1="71"
+            x2="71"
+            y1="56"
+            y2="86"
           />
           <line
-            x1="6"
-            x2="36"
-            y1="121"
-            y2="121"
+            x1="56"
+            x2="86"
+            y1="71"
+            y2="71"
           />
         </g>
       </g>
@@ -770,13 +726,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="51"
-              y="101"
+              x="101"
+              y="51"
             />
             <text
               font-size="14"
-              x="71"
-              y="121"
+              x="121"
+              y="71"
             >
               S
             </text>
@@ -792,12 +748,12 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="101"
+              x="1"
               y="101"
             />
             <text
               font-size="14"
-              x="121"
+              x="21"
               y="121"
             >
               T
@@ -814,13 +770,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="1"
-              y="151"
+              x="51"
+              y="101"
             />
             <text
               font-size="14"
-              x="21"
-              y="171"
+              x="71"
+              y="121"
             >
               Y
             </text>
@@ -836,13 +792,13 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="51"
-              y="151"
+              x="101"
+              y="101"
             />
             <text
               font-size="14"
-              x="71"
-              y="171"
+              x="121"
+              y="121"
             >
               Z
             </text>
@@ -858,12 +814,12 @@ exports[`Test panel Should return panel with addPanel visible 1`] = `
               class="gate-unitary"
               height="40"
               width="40"
-              x="101"
+              x="1"
               y="151"
             />
             <text
               font-size="14"
-              x="121"
+              x="21"
               y="171"
             >
               ZZ
diff --git a/__tests__/panel.test.ts b/__tests__/panel.test.ts
index 1a28d0bf..428ede80 100644
--- a/__tests__/panel.test.ts
+++ b/__tests__/panel.test.ts
@@ -235,34 +235,6 @@ describe('Test gate', () => {
 describe('Test defaulGateDictionary', () => {
     test('Verify defaultGateDictionary', () => {
         const expected = {
-            Entangle: {
-                gate: 'Entangle',
-                targets: [{ qId: 0 }, { qId: 1 }],
-                children: [
-                    {
-                        gate: 'H',
-                        targets: [
-                            {
-                                qId: 0,
-                            },
-                        ],
-                    },
-                    {
-                        gate: 'X',
-                        isControlled: true,
-                        controls: [
-                            {
-                                qId: 0,
-                            },
-                        ],
-                        targets: [
-                            {
-                                qId: 1,
-                            },
-                        ],
-                    },
-                ],
-            },
             RX: {
                 gate: 'RX',
                 targets: [{ qId: 0 }],

From 4c03bf020c7bfd4793f0d4b534da91257e975a45 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Fri, 5 Aug 2022 13:10:23 -0400
Subject: [PATCH 105/108] More examples

---
 example/index.html | 12 +++++++++---
 example/script.js  | 27 +++++++++++++++++++--------
 2 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/example/index.html b/example/index.html
index 196a80c5..95bdfd82 100644
--- a/example/index.html
+++ b/example/index.html
@@ -46,11 +46,17 @@ <h1 id="title">quantum-viz.js</h1>
             <h4>1. Entangle</h4>
             <div id="entangle" class="container">
             </div>
-            <h4>2. Sample</h4>
+            <h4>2. Entangle with useDraggable</h4>
+            <div id="entangle-usedraggable" class="container">
+            </div>
+            <h4>3. Entangle with useDraggable and usePanel</h4>
+            <div id="entangle-usedraggable-usepanel" class="container">
+            </div>
+            <h4>4. Sample</h4>
             <div id="sample" class="container"></div>
-            <h4>3. Teleport</h4>
+            <h4>5. Teleport</h4>
             <div id="teleport" class="container"></div>
-            <h4>4. Grover</h4>
+            <h4>6. Grover</h4>
             <div id="grover" class="container"></div>
         </div>
     </main>
diff --git a/example/script.js b/example/script.js
index 69b5639a..7d4ff864 100644
--- a/example/script.js
+++ b/example/script.js
@@ -8,20 +8,31 @@ if (typeof qviz != 'undefined') {
     const entangleDiv = document.getElementById('entangle');
     if (entangleDiv != null) {
         qviz.create(entangle) //
-            .useDraggable()
-            .usePanel({ displaySize: 3 })
+            .useOnCircuitChange((circuit) => {
+                console.log('New circuit ↓');
+                console.log(circuit);
+            })
             .draw(entangleDiv);
     }
 
+    const entangleUDDiv = document.getElementById('entangle-usedraggable');
+    if (entangleUDDiv != null) {
+        qviz.create(entangle) //
+            .useDraggable()
+            .draw(entangleUDDiv);
+    }
+
+    const entangleUDUPDiv = document.getElementById('entangle-usedraggable-usepanel');
+    if (entangleUDUPDiv != null) {
+        qviz.create(entangle) //
+            .useDraggable()
+            .usePanel()
+            .draw(entangleUDUPDiv);
+    }
+
     const sampleDiv = document.getElementById('sample');
     if (sampleDiv != null) {
         qviz.create(sample) //
-            .useDraggable()
-            .usePanel()
-            .useOnCircuitChange((circuit) => {
-                console.log('New circuit ↓');
-                console.log(circuit);
-            })
             .draw(sampleDiv);
     }
 

From c14219c64c0ea550ccf1c5ee59bf56937b5969ed Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Fri, 5 Aug 2022 13:18:10 -0400
Subject: [PATCH 106/108] Add extension usage instruction

---
 README.md | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index f3e9cd22..665c94bf 100644
--- a/README.md
+++ b/README.md
@@ -38,11 +38,22 @@ const sampleCircuit = {
 2. Draw it in a `div`:
 ```js
 const sampleDiv = document.getElementById('sample');
-qviz.draw(sampleCircuit, sampleDiv, qviz.STYLES['Default']);
+qviz.create(sampleCircuit)
+    .draw(sampleDiv)
 ```
 
 Refer to the [`example`](./example) folder for an example on how to use quantum-viz.js. Notice that in order to open the contents of this folder in a browser you will need first to install from source (see [below](#running-from-source)).
 
+3. Enable extensions by:
+```js
+const sampleDiv = document.getElementById('sample');
+qviz.create(sampleCircuit)
+    .useDraggble() // drag and drop
+    .usePanel() // edit and add panel
+    .useOnCircuitChange(circuit => console.log(circuit)) // trigger when circuit changes
+    .draw(sampleDiv)
+```
+
 ## Python usage
 
 To use this package with Python, use [quantum-viz](/quantum-viz).

From 79b849aef0ad2baf53136fb583890f23d3be2b7d Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Fri, 5 Aug 2022 14:36:32 -0400
Subject: [PATCH 107/108] Disable control checkboxes if already in targets

---
 src/panel.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/panel.ts b/src/panel.ts
index 52477d4b..ea39fa00 100755
--- a/src/panel.ts
+++ b/src/panel.ts
@@ -480,7 +480,11 @@ const checkboxes = (
         const elem = checkbox(option.value, option.text);
         const inputElem = elem.querySelector('input') as HTMLInputElement;
         selectedIndexes.includes(index) && inputElem.setAttribute('checked', 'true');
-        operation == undefined && inputElem.setAttribute('disabled', 'true');
+        // Disable control checkboxes if already in targets
+        if (operation) {
+            const targetIndexes = operation.targets.map((register) => register.qId);
+            targetIndexes.includes(index) && inputElem.setAttribute('disabled', 'true');
+        }
 
         inputElem.onchange = () => {
             // Get all checked options

From 1e8f11c1ab09172ce7e2208f1391fe4341136825 Mon Sep 17 00:00:00 2001
From: Aaron Nguyen <me@aaronnguyen.dev>
Date: Thu, 11 Aug 2022 11:14:30 -0400
Subject: [PATCH 108/108] Update tests

---
 __tests__/__snapshots__/panel.test.ts.snap | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/__tests__/__snapshots__/panel.test.ts.snap b/__tests__/__snapshots__/panel.test.ts.snap
index b598866f..e2589485 100644
--- a/__tests__/__snapshots__/panel.test.ts.snap
+++ b/__tests__/__snapshots__/panel.test.ts.snap
@@ -340,6 +340,7 @@ exports[`Test checkboxes Test with gate X with 1 control 1`] = `
   </label>
   <label>
     <input
+      disabled="true"
       type="checkbox"
       value="0"
     />
@@ -375,6 +376,7 @@ exports[`Test checkboxes Test with gate X with 2 controls 1`] = `
   </label>
   <label>
     <input
+      disabled="true"
       type="checkbox"
       value="1"
     />
@@ -461,6 +463,7 @@ exports[`Test editPanel Should return editPanel editing X gate 1`] = `
     </label>
     <label>
       <input
+        disabled="true"
         type="checkbox"
         value="0"
       />
@@ -875,6 +878,7 @@ exports[`Test panel Should return panel with editPanel visible 1`] = `
       </label>
       <label>
         <input
+          disabled="true"
           type="checkbox"
           value="0"
         />