The mail server is not configured, please send the following link to {user.name}:
- | null;
-
-type NotificationFunction = (
- message: ArgsProps["message"],
- description?: ArgsProps["description"],
- args?: NotificationConfig
-) => void;
-
-declare const notification: NotificationApi & {
- success: NotificationFunction;
- error: NotificationFunction;
- info: NotificationFunction;
- warning: NotificationFunction;
- warn: NotificationFunction;
-};
-
-export default notification;
diff --git a/client/app/services/parameters/DateParameter.js b/client/app/services/parameters/DateParameter.js
index 18d850aef4..3d386b384d 100644
--- a/client/app/services/parameters/DateParameter.js
+++ b/client/app/services/parameters/DateParameter.js
@@ -61,7 +61,7 @@ class DateParameter extends Parameter {
return value;
}
- const normalizedValue = moment(value, moment.ISO_8601, true);
+ const normalizedValue = moment(value);
return normalizedValue.isValid() ? normalizedValue : null;
}
diff --git a/client/app/services/parameters/DateRangeParameter.js b/client/app/services/parameters/DateRangeParameter.js
index dc284ae29b..6b8b7a1760 100644
--- a/client/app/services/parameters/DateRangeParameter.js
+++ b/client/app/services/parameters/DateRangeParameter.js
@@ -11,14 +11,6 @@ const DATETIME_FORMATS = {
const DYNAMIC_PREFIX = "d_";
-/**
- * Dynamic date range preset value with end set to current time
- * @param from {function(): moment.Moment}
- * @param now {function(): moment.Moment=} moment - defaults to now
- * @returns {function(withNow: boolean): [moment.Moment, moment.Moment|undefined]}
- */
-const untilNow = (from, now = () => moment()) => (withNow = true) => [from(), withNow ? now() : undefined];
-
const DYNAMIC_DATE_RANGES = {
today: {
name: "Today",
@@ -80,77 +72,29 @@ const DYNAMIC_DATE_RANGES = {
.endOf("year"),
],
},
- last_hour: {
- name: "Last hour",
- value: untilNow(() => moment().subtract(1, "hour")),
- },
- last_8_hours: {
- name: "Last 8 hours",
- value: untilNow(() => moment().subtract(8, "hour")),
- },
- last_24_hours: {
- name: "Last 24 hours",
- value: untilNow(() => moment().subtract(24, "hour")),
- },
last_7_days: {
name: "Last 7 days",
- value: untilNow(
- () =>
- moment()
- .subtract(7, "days")
- .startOf("day"),
- () => moment().endOf("day")
- ),
+ value: () => [moment().subtract(7, "days"), moment()],
},
last_14_days: {
name: "Last 14 days",
- value: untilNow(
- () =>
- moment()
- .subtract(14, "days")
- .startOf("day"),
- () => moment().endOf("day")
- ),
+ value: () => [moment().subtract(14, "days"), moment()],
},
last_30_days: {
name: "Last 30 days",
- value: untilNow(
- () =>
- moment()
- .subtract(30, "days")
- .startOf("day"),
- () => moment().endOf("day")
- ),
+ value: () => [moment().subtract(30, "days"), moment()],
},
last_60_days: {
name: "Last 60 days",
- value: untilNow(
- () =>
- moment()
- .subtract(60, "days")
- .startOf("day"),
- () => moment().endOf("day")
- ),
+ value: () => [moment().subtract(60, "days"), moment()],
},
last_90_days: {
name: "Last 90 days",
- value: untilNow(
- () =>
- moment()
- .subtract(90, "days")
- .startOf("day"),
- () => moment().endOf("day")
- ),
+ value: () => [moment().subtract(90, "days"), moment()],
},
last_12_months: {
name: "Last 12 months",
- value: untilNow(
- () =>
- moment()
- .subtract(12, "months")
- .startOf("day"),
- () => moment().endOf("day")
- ),
+ value: () => [moment().subtract(12, "months"), moment()],
},
};
@@ -163,11 +107,6 @@ export function isDynamicDateRangeString(value) {
return !!DYNAMIC_DATE_RANGES[value.substring(DYNAMIC_PREFIX.length)];
}
-export function getDynamicDateRangeStringFromName(dynamicRangeName) {
- const key = findKey(DYNAMIC_DATE_RANGES, range => range.name === dynamicRangeName);
- return key ? DYNAMIC_PREFIX + key : undefined;
-}
-
export function isDynamicDateRange(value) {
return includes(DYNAMIC_DATE_RANGES, value);
}
diff --git a/client/app/services/parameters/TextPatternParameter.js b/client/app/services/parameters/TextPatternParameter.js
deleted file mode 100644
index fe84c00ce8..0000000000
--- a/client/app/services/parameters/TextPatternParameter.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { toString, isNull } from "lodash";
-import Parameter from "./Parameter";
-
-class TextPatternParameter extends Parameter {
- constructor(parameter, parentQueryId) {
- super(parameter, parentQueryId);
- this.regex = parameter.regex;
- this.setValue(parameter.value);
- }
-
- // eslint-disable-next-line class-methods-use-this
- normalizeValue(value) {
- const normalizedValue = toString(value);
- if (isNull(normalizedValue)) {
- return null;
- }
-
- var re = new RegExp(this.regex);
-
- if (re !== null) {
- if (re.test(normalizedValue)) {
- return normalizedValue;
- }
- }
- return null;
- }
-}
-
-export default TextPatternParameter;
diff --git a/client/app/services/parameters/index.js b/client/app/services/parameters/index.js
index e34eced08f..9e1b3fffcb 100644
--- a/client/app/services/parameters/index.js
+++ b/client/app/services/parameters/index.js
@@ -5,7 +5,6 @@ import EnumParameter from "./EnumParameter";
import QueryBasedDropdownParameter from "./QueryBasedDropdownParameter";
import DateParameter from "./DateParameter";
import DateRangeParameter from "./DateRangeParameter";
-import TextPatternParameter from "./TextPatternParameter";
function createParameter(param, parentQueryId) {
switch (param.type) {
@@ -23,8 +22,6 @@ function createParameter(param, parentQueryId) {
case "datetime-range":
case "datetime-range-with-seconds":
return new DateRangeParameter(param, parentQueryId);
- case "text-pattern":
- return new TextPatternParameter({ ...param, type: "text-pattern" }, parentQueryId);
default:
return new TextParameter({ ...param, type: "text" }, parentQueryId);
}
@@ -37,7 +34,6 @@ function cloneParameter(param) {
export {
Parameter,
TextParameter,
- TextPatternParameter,
NumberParameter,
EnumParameter,
QueryBasedDropdownParameter,
diff --git a/client/app/services/parameters/tests/Parameter.test.js b/client/app/services/parameters/tests/Parameter.test.js
index 4d504e3165..3ec7ad7b13 100644
--- a/client/app/services/parameters/tests/Parameter.test.js
+++ b/client/app/services/parameters/tests/Parameter.test.js
@@ -1,7 +1,6 @@
import {
createParameter,
TextParameter,
- TextPatternParameter,
NumberParameter,
EnumParameter,
QueryBasedDropdownParameter,
@@ -13,7 +12,6 @@ describe("Parameter", () => {
describe("create", () => {
const parameterTypes = [
["text", TextParameter],
- ["text-pattern", TextPatternParameter],
["number", NumberParameter],
["enum", EnumParameter],
["query", QueryBasedDropdownParameter],
diff --git a/client/app/services/parameters/tests/TextPatternParameter.test.js b/client/app/services/parameters/tests/TextPatternParameter.test.js
deleted file mode 100644
index 699e320d96..0000000000
--- a/client/app/services/parameters/tests/TextPatternParameter.test.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { createParameter } from "..";
-
-describe("TextPatternParameter", () => {
- let param;
-
- beforeEach(() => {
- param = createParameter({ name: "param", title: "Param", type: "text-pattern", regex: "a+" });
- });
-
- describe("noramlizeValue", () => {
- test("converts matching strings", () => {
- const normalizedValue = param.normalizeValue("art");
- expect(normalizedValue).toBe("art");
- });
-
- test("returns null when string does not match pattern", () => {
- const normalizedValue = param.normalizeValue("brt");
- expect(normalizedValue).toBeNull();
- });
- });
-});
diff --git a/client/app/services/policy/DefaultPolicy.js b/client/app/services/policy/DefaultPolicy.js
index 368be23f71..58a594013a 100644
--- a/client/app/services/policy/DefaultPolicy.js
+++ b/client/app/services/policy/DefaultPolicy.js
@@ -1,4 +1,4 @@
-import { get, isArray } from "lodash";
+import { isArray } from "lodash";
import { currentUser, clientConfig } from "@/services/auth";
/* eslint-disable class-methods-use-this */
@@ -57,12 +57,4 @@ export default class DefaultPolicy {
const result = clientConfig.queryRefreshIntervals;
return isArray(result) ? result : null;
}
-
- canEdit(object) {
- return get(object, "can_edit", false);
- }
-
- canRun() {
- return true;
- }
}
diff --git a/client/app/services/query-result.js b/client/app/services/query-result.js
index 03d8de61b6..0f39778c0c 100644
--- a/client/app/services/query-result.js
+++ b/client/app/services/query-result.js
@@ -113,10 +113,6 @@ export function fetchDataFromJob(jobId, interval = 1000) {
});
}
-export function isDateTime(v) {
- return isString(v) && moment(v, moment.ISO_8601, true).isValid() && /^\d{4}-\d{2}-\d{2}T/.test(v);
-}
-
class QueryResult {
constructor(props) {
this.deferred = defer();
@@ -151,7 +147,7 @@ class QueryResult {
let newType = null;
if (isNumber(v)) {
newType = "float";
- } else if (isDateTime(v)) {
+ } else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}T/)) {
row[k] = moment.utc(v);
newType = "datetime";
} else if (isString(v) && v.match(/^\d{4}-\d{2}-\d{2}$/)) {
@@ -275,10 +271,6 @@ class QueryResult {
return this.getColumnNames().map(col => getColumnFriendlyName(col));
}
- getTruncated() {
- return this.query_result.data ? this.query_result.data.truncated : null;
- }
-
getFilters() {
if (!this.getColumns()) {
return [];
@@ -322,9 +314,6 @@ class QueryResult {
}
return v;
});
- if (filter.values.length > 1 && filter.multiple) {
- filter.current = filter.values.slice();
- }
});
return filters;
@@ -446,11 +435,11 @@ class QueryResult {
return `${queryName.replace(/ /g, "_") + moment(this.getUpdatedAt()).format("_YYYY_MM_DD")}.${fileType}`;
}
- static getByQueryId(id, parameters, applyAutoLimit, maxAge) {
+ static getByQueryId(id, parameters, maxAge) {
const queryResult = new QueryResult();
axios
- .post(`api/queries/${id}/results`, { id, parameters, apply_auto_limit: applyAutoLimit, max_age: maxAge })
+ .post(`api/queries/${id}/results`, { id, parameters, max_age: maxAge })
.then(response => {
queryResult.update(response);
@@ -465,14 +454,13 @@ class QueryResult {
return queryResult;
}
- static get(dataSourceId, query, parameters, applyAutoLimit, maxAge, queryId) {
+ static get(dataSourceId, query, parameters, maxAge, queryId) {
const queryResult = new QueryResult();
const params = {
data_source_id: dataSourceId,
parameters,
query,
- apply_auto_limit: applyAutoLimit,
max_age: maxAge,
};
diff --git a/client/app/services/query-result.test.js b/client/app/services/query-result.test.js
deleted file mode 100644
index d31e9e1ea1..0000000000
--- a/client/app/services/query-result.test.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { isDateTime } from "@/services/query-result";
-
-describe("isDateTime", () => {
- it.each([
- ["2022-01-01T00:00:00", true],
- ["2022-01-01T00:00:00+09:00", true],
- ["2021-01-27T00:00:01.733983944+03:00 stderr F {", false],
- ["2021-01-27Z00:00:00+09:00", false],
- ["2021-01-27", false],
- ["foo bar", false],
- [2022, false],
- [null, false],
- ["", false],
- ])("isDateTime('%s'). expected '%s'.", (value, expected) => {
- expect(isDateTime(value)).toBe(expected);
- });
-});
diff --git a/client/app/services/query.js b/client/app/services/query.js
index a8cf624cb8..66ba2be6b8 100644
--- a/client/app/services/query.js
+++ b/client/app/services/query.js
@@ -24,7 +24,6 @@ import location from "@/services/location";
import { Parameter, createParameter } from "./parameters";
import { currentUser } from "./auth";
import QueryResult from "./query-result";
-import localOptions from "@/lib/localOptions";
Mustache.escape = identity; // do not html-escape values
@@ -51,7 +50,6 @@ export class Query {
if (!has(this, "options")) {
this.options = {};
}
- this.options.apply_auto_limit = !!this.options.apply_auto_limit;
if (!isArray(this.options.parameters)) {
this.options.parameters = [];
@@ -132,8 +130,7 @@ export class Query {
}
getQueryResult(maxAge) {
- const execute = () =>
- QueryResult.getByQueryId(this.id, this.getParameters().getExecutionValues(), this.getAutoLimit(), maxAge);
+ const execute = () => QueryResult.getByQueryId(this.id, this.getParameters().getExecutionValues(), maxAge);
return this.prepareQueryResultExecution(execute, maxAge);
}
@@ -144,8 +141,7 @@ export class Query {
}
const parameters = this.getParameters().getExecutionValues({ joinListValues: true });
- const execute = () =>
- QueryResult.get(this.data_source_id, queryText, parameters, this.getAutoLimit(), maxAge, this.id);
+ const execute = () => QueryResult.get(this.data_source_id, queryText, parameters, maxAge, this.id);
return this.prepareQueryResultExecution(execute, maxAge);
}
@@ -188,10 +184,6 @@ export class Query {
return this.$parameters;
}
- getAutoLimit() {
- return this.options.apply_auto_limit;
- }
-
getParametersDefs(update = true) {
return this.getParameters().get(update);
}
@@ -402,10 +394,25 @@ QueryService.newQuery = function newQuery() {
name: "New Query",
schedule: null,
user: currentUser,
- options: { apply_auto_limit: localOptions.get("applyAutoLimit", true) },
+ options: {},
tags: [],
can_edit: true,
});
};
+QueryService.format = function formatQuery(syntax, query) {
+ if (syntax === "json") {
+ try {
+ const formatted = JSON.stringify(JSON.parse(query), " ", 4);
+ return Promise.resolve(formatted);
+ } catch (err) {
+ return Promise.reject(String(err));
+ }
+ } else if (syntax === "sql") {
+ return axios.post("api/queries/format", { query }).then(data => data.query);
+ } else {
+ return Promise.reject("Query formatting is not supported for your data source syntax.");
+ }
+};
+
extend(Query, QueryService);
diff --git a/client/app/services/restoreSession.jsx b/client/app/services/restoreSession.jsx
deleted file mode 100644
index b7773325ed..0000000000
--- a/client/app/services/restoreSession.jsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { map } from "lodash";
-import React from "react";
-import Modal from "antd/lib/modal";
-import { Auth } from "@/services/auth";
-
-const SESSION_RESTORED_MESSAGE = "redash_session_restored";
-
-export function notifySessionRestored() {
- if (window.opener) {
- window.opener.postMessage({ type: SESSION_RESTORED_MESSAGE }, window.location.origin);
- }
-}
-
-function getPopupPosition(width, height) {
- const windowLeft = window.screenX;
- const windowTop = window.screenY;
- const windowWidth = window.innerWidth;
- const windowHeight = window.innerHeight;
-
- return {
- left: Math.floor((windowWidth - width) / 2 + windowLeft),
- top: Math.floor((windowHeight - height) / 2 + windowTop),
- width: Math.floor(width),
- height: Math.floor(height),
- };
-}
-
-function showRestoreSessionPrompt(loginUrl, onSuccess) {
- let popup = null;
-
- Modal.warning({
- content: "Your session has expired. Please login to continue.",
- okText: (
-
- Login
- (opens in a new tab)
-
- ),
- centered: true,
- mask: true,
- maskClosable: false,
- keyboard: false,
- onOk: closeModal => {
- if (popup && !popup.closed) {
- popup.focus();
- return; // popup already shown
- }
-
- const popupOptions = {
- ...getPopupPosition(640, 640),
- menubar: "no",
- toolbar: "no",
- location: "yes",
- resizable: "yes",
- scrollbars: "yes",
- status: "yes",
- };
-
- popup = window.open(loginUrl, "Restore Session", map(popupOptions, (value, key) => `${key}=${value}`).join(","));
-
- const handlePostMessage = event => {
- if (event.data.type === SESSION_RESTORED_MESSAGE) {
- if (popup) {
- popup.close();
- }
- popup = null;
- window.removeEventListener("message", handlePostMessage);
- closeModal();
- onSuccess();
- }
- };
-
- window.addEventListener("message", handlePostMessage, false);
- },
- });
-}
-
-let restoreSessionPromise = null;
-
-export function restoreSession() {
- if (!restoreSessionPromise) {
- restoreSessionPromise = new Promise(resolve => {
- showRestoreSessionPrompt(Auth.getLoginUrl(), () => {
- restoreSessionPromise = null;
- resolve();
- });
- });
- }
-
- return restoreSessionPromise;
-}
diff --git a/client/app/services/routes.js b/client/app/services/routes.js
new file mode 100644
index 0000000000..abb008a0a4
--- /dev/null
+++ b/client/app/services/routes.js
@@ -0,0 +1,42 @@
+import { isString, isObject, filter, sortBy } from "lodash";
+import pathToRegexp from "path-to-regexp";
+
+function getRouteParamsCount(path) {
+ const tokens = pathToRegexp.parse(path);
+ return filter(tokens, isObject).length;
+}
+
+class Routes {
+ _items = [];
+ _sorted = false;
+
+ get items() {
+ if (!this._sorted) {
+ this._items = sortBy(this._items, [
+ item => getRouteParamsCount(item.path), // simple definitions first, with more params - last
+ item => -item.path.length, // longer first
+ item => item.path, // if same type and length - sort alphabetically
+ ]);
+ this._sorted = true;
+ }
+ return this._items;
+ }
+
+ register(id, route) {
+ id = isString(id) ? id : null;
+ this.unregister(id);
+ if (isObject(route)) {
+ this._items = [...this._items, { ...route, id }];
+ this._sorted = false;
+ }
+ }
+
+ unregister(id) {
+ if (isString(id)) {
+ // removing item does not break their order (if already sorted)
+ this._items = filter(this._items, item => item.id !== id);
+ }
+ }
+}
+
+export default new Routes();
diff --git a/client/app/services/routes.ts b/client/app/services/routes.ts
deleted file mode 100644
index eae32f8fcc..0000000000
--- a/client/app/services/routes.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import { isString, isObject, filter, sortBy } from "lodash";
-import React from "react";
-import { Context, Route as UniversalRouterRoute } from "universal-router";
-import pathToRegexp from "path-to-regexp";
-
-export interface CurrentRoute {
- id: string | null;
- key?: string;
- title: string;
- routeParams: P;
-}
-
-export interface RedashRoute
extends UniversalRouterRoute {
- path: string; // we don't use other UniversalRouterRoute options, path should be available and should be a string
- key?: string; // generated in Router.jsx
- title: string;
- render?: (currentRoute: CurrentRoute) => React.ReactNode;
- getApiKey?: () => string;
-}
-
-interface RouteItem extends RedashRoute {
- id: string | null;
-}
-
-function getRouteParamsCount(path: string) {
- const tokens = pathToRegexp.parse(path);
- return filter(tokens, isObject).length;
-}
-
-class Routes {
- _items: RouteItem[] = [];
- _sorted = false;
-
- get items(): RouteItem[] {
- if (!this._sorted) {
- this._items = sortBy(this._items, [
- item => getRouteParamsCount(item.path), // simple definitions first, with more params - last
- item => -item.path.length, // longer first
- item => item.path, // if same type and length - sort alphabetically
- ]);
- this._sorted = true;
- }
- return this._items;
- }
-
- public register(id: string, route: RedashRoute
) {
- const idOrNull = isString(id) ? id : null;
- this.unregister(idOrNull);
- if (isObject(route)) {
- this._items = [...this.items, { ...route, id: idOrNull }];
- this._sorted = false;
- }
- }
-
- public unregister(id: string | null) {
- if (isString(id)) {
- // removing item does not break their order (if already sorted)
- this._items = filter(this.items, item => item.id !== id);
- }
- }
-}
-
-export default new Routes();
diff --git a/client/app/services/sanitize.js b/client/app/services/sanitize.js
index 47521ba408..4e162db3da 100644
--- a/client/app/services/sanitize.js
+++ b/client/app/services/sanitize.js
@@ -18,6 +18,4 @@ DOMPurify.addHook("afterSanitizeAttributes", function(node) {
}
});
-export { DOMPurify };
-
export default DOMPurify.sanitize;
diff --git a/client/app/services/widget.js b/client/app/services/widget.js
index 1fc5d124db..2b21e3b390 100644
--- a/client/app/services/widget.js
+++ b/client/app/services/widget.js
@@ -1,21 +1,6 @@
import moment from "moment";
import { axios } from "@/services/axios";
-import {
- each,
- pick,
- extend,
- isObject,
- truncate,
- keys,
- difference,
- filter,
- map,
- merge,
- sortBy,
- indexOf,
- size,
- includes,
-} from "lodash";
+import { each, pick, extend, isObject, truncate, keys, difference, filter, map, merge } from "lodash";
import location from "@/services/location";
import { cloneParameter } from "@/services/parameters";
import dashboardGridOptions from "@/config/dashboard-grid-options";
@@ -158,24 +143,18 @@ class Widget {
if (maxAge === undefined || force) {
maxAge = force ? 0 : undefined;
}
+ this.queryResult = this.getQuery().getQueryResult(maxAge);
- const queryResult = this.getQuery().getQueryResult(maxAge);
- this.queryResult = queryResult;
-
- queryResult
+ this.queryResult
.toPromise()
.then(result => {
- if (this.queryResult === queryResult) {
- this.loading = false;
- this.data = result;
- }
+ this.loading = false;
+ this.data = result;
return result;
})
.catch(error => {
- if (this.queryResult === queryResult) {
- this.loading = false;
- this.data = error;
- }
+ this.loading = false;
+ this.data = error;
return error;
});
}
@@ -222,7 +201,7 @@ class Widget {
const queryParams = location.search;
const localTypes = [Widget.MappingType.WidgetLevel, Widget.MappingType.StaticValue];
- const localParameters = map(
+ return map(
filter(params, param => localTypes.indexOf(mappings[param.name].type) >= 0),
param => {
const mapping = mappings[param.name];
@@ -238,13 +217,6 @@ class Widget {
return result;
}
);
-
- // order widget params using paramOrder
- return sortBy(localParameters, param =>
- includes(this.options.paramOrder, param.name)
- ? indexOf(this.options.paramOrder, param.name)
- : size(this.options.paramOrder)
- );
}
getParameterMappings() {
diff --git a/client/app/styles/formStyle.less b/client/app/styles/formStyle.less
deleted file mode 100644
index 884b94a127..0000000000
--- a/client/app/styles/formStyle.less
+++ /dev/null
@@ -1,10 +0,0 @@
-.ant-form-horizontal--labels-left {
- .ant-form-item-label {
- text-align: left;
- white-space: normal;
-
- > label::after {
- content: none; // Do not show ":" next to label when they are aligned on left side
- }
- }
-}
diff --git a/client/app/styles/formStyle.ts b/client/app/styles/formStyle.ts
deleted file mode 100644
index 86d4970cd0..0000000000
--- a/client/app/styles/formStyle.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { FormProps } from "antd/lib/form/Form";
-import { FormItemProps } from "antd/lib/form/FormItem";
-import "./formStyle.less";
-
-export function getHorizontalFormProps(): FormProps {
- return {
- labelCol: { xs: { span: 24 }, sm: { span: 6 }, lg: { span: 4 } },
- wrapperCol: { xs: { span: 24 }, sm: { span: 12 }, lg: { span: 10 } },
- layout: "horizontal",
- className: "ant-form-horizontal--labels-left",
- };
-}
-
-export function getHorizontalFormItemWithoutLabelProps(): FormItemProps {
- return {
- wrapperCol: { xs: { span: 24 }, sm: { span: 12, offset: 6 }, lg: { span: 12, offset: 4 } },
- };
-}
diff --git a/client/cypress/cypress.js b/client/cypress/cypress.js
index 71c2a02141..9a8611fecf 100644
--- a/client/cypress/cypress.js
+++ b/client/cypress/cypress.js
@@ -1,74 +1,64 @@
/* eslint-disable import/no-extraneous-dependencies, no-console */
-const { find } = require("lodash");
+const atob = require("atob");
const { execSync } = require("child_process");
-const { get, post } = require("request").defaults({ jar: true });
+const { post } = require("request").defaults({ jar: true });
const { seedData } = require("./seed-data");
-const fs = require("fs");
-var Cookie = require("request-cookies").Cookie;
-let cypressConfigBaseUrl;
-try {
- const cypressConfig = JSON.parse(fs.readFileSync("cypress.json"));
- cypressConfigBaseUrl = cypressConfig.baseUrl;
-} catch (e) {}
-
-const baseUrl = process.env.CYPRESS_baseUrl || cypressConfigBaseUrl || "http://localhost:5001";
+const baseUrl = process.env.CYPRESS_baseUrl || "http://localhost:5000";
function seedDatabase(seedValues) {
- get(baseUrl + "/login", (_, { headers }) => {
- const request = seedValues.shift();
- const data = request.type === "form" ? { formData: request.data } : { json: request.data };
+ const request = seedValues.shift();
+ const data = request.type === "form" ? { formData: request.data } : { json: request.data };
- if (headers["set-cookie"]) {
- const cookies = headers["set-cookie"].map(cookie => new Cookie(cookie));
- const csrfCookie = find(cookies, { key: "csrf_token" });
- if (csrfCookie) {
- if (request.type === "form") {
- data["formData"] = { ...data["formData"], csrf_token: csrfCookie.value };
- } else {
- data["headers"] = { "X-CSRFToken": csrfCookie.value };
- }
- }
+ post(baseUrl + request.route, data, (err, response) => {
+ const result = response ? response.statusCode : err;
+ console.log("POST " + request.route + " - " + result);
+ if (seedValues.length) {
+ seedDatabase(seedValues);
}
-
- post(baseUrl + request.route, data, (err, response) => {
- const result = response ? response.statusCode : err;
- console.log("POST " + request.route + " - " + result);
- if (seedValues.length) {
- seedDatabase(seedValues);
- }
- });
});
}
function buildServer() {
console.log("Building the server...");
- execSync("docker compose -p cypress build", { stdio: "inherit" });
+ execSync("docker-compose -p cypress build --build-arg skip_dev_deps=true --build-arg skip_ds_deps=true", {
+ stdio: "inherit",
+ });
}
function startServer() {
console.log("Starting the server...");
- execSync("docker compose -p cypress up -d", { stdio: "inherit" });
- execSync("docker compose -p cypress run server create_db", { stdio: "inherit" });
+ execSync("docker-compose -p cypress up -d", { stdio: "inherit" });
+ execSync("docker-compose -p cypress run server create_db", { stdio: "inherit" });
}
function stopServer() {
console.log("Stopping the server...");
- execSync("docker compose -p cypress down", { stdio: "inherit" });
+ execSync("docker-compose -p cypress down", { stdio: "inherit" });
}
function runCypressCI() {
const {
- GITHUB_REPOSITORY,
- CYPRESS_OPTIONS, // eslint-disable-line no-unused-vars
+ PERCY_TOKEN_ENCODED,
+ CYPRESS_PROJECT_ID_ENCODED,
+ CYPRESS_RECORD_KEY_ENCODED,
+ CIRCLE_REPOSITORY_URL,
} = process.env;
- if (GITHUB_REPOSITORY === "getredash/redash" && process.env.CYPRESS_RECORD_KEY) {
- process.env.CYPRESS_OPTIONS = "--record";
+ if (CIRCLE_REPOSITORY_URL && CIRCLE_REPOSITORY_URL.includes("getredash/redash")) {
+ if (PERCY_TOKEN_ENCODED) {
+ process.env.PERCY_TOKEN = atob(`${PERCY_TOKEN_ENCODED}`);
+ }
+ if (CYPRESS_PROJECT_ID_ENCODED) {
+ process.env.CYPRESS_PROJECT_ID = atob(`${CYPRESS_PROJECT_ID_ENCODED}`);
+ }
+ if (CYPRESS_RECORD_KEY_ENCODED) {
+ process.env.CYPRESS_RECORD_KEY = atob(`${CYPRESS_RECORD_KEY_ENCODED}`);
+ }
}
execSync(
- "COMMIT_INFO_MESSAGE=$(git show -s --format=%s) docker compose run --name cypress cypress ./node_modules/.bin/percy exec -t 300 -- ./node_modules/.bin/cypress run $CYPRESS_OPTIONS",
+ "COMMIT_INFO_MESSAGE=$(git show -s --format=%s) docker-compose run cypress ./node_modules/.bin/percy exec -t 300 -- ./node_modules/.bin/cypress run --record",
{ stdio: "inherit" }
);
}
@@ -107,6 +97,6 @@ switch (command) {
stopServer();
break;
default:
- console.log("Usage: yarn cypress [build|start|db-seed|open|run|stop]");
+ console.log("Usage: npm run cypress [build|start|db-seed|open|run|stop]");
break;
}
diff --git a/client/cypress/integration/alert/create_alert_spec.js b/client/cypress/integration/alert/create_alert_spec.js
index 092b631cae..1eb78f2aa2 100644
--- a/client/cypress/integration/alert/create_alert_spec.js
+++ b/client/cypress/integration/alert/create_alert_spec.js
@@ -1,3 +1,5 @@
+import { createQuery } from "../../support/redash-api";
+
describe("Create Alert", () => {
beforeEach(() => {
cy.login();
@@ -10,7 +12,7 @@ describe("Create Alert", () => {
});
it("selects query and takes a screenshot", () => {
- cy.createQuery({ name: "Create Alert Query" }).then(({ id: queryId }) => {
+ createQuery({ name: "Create Alert Query" }).then(({ id: queryId }) => {
cy.visit("/alerts/new");
cy.getByTestId("QuerySelector")
.click()
diff --git a/client/cypress/integration/alert/edit_alert_spec.js b/client/cypress/integration/alert/edit_alert_spec.js
index 1a03ccf373..92082edcea 100644
--- a/client/cypress/integration/alert/edit_alert_spec.js
+++ b/client/cypress/integration/alert/edit_alert_spec.js
@@ -1,11 +1,13 @@
+import { createAlert, createQuery } from "../../support/redash-api";
+
describe("Edit Alert", () => {
beforeEach(() => {
cy.login();
});
it("renders the page and takes a screenshot", () => {
- cy.createQuery({ query: "select 1 as col_name" })
- .then(({ id: queryId }) => cy.createAlert(queryId, { column: "col_name" }))
+ createQuery({ query: "select 1 as col_name" })
+ .then(({ id: queryId }) => createAlert(queryId, { column: "col_name" }))
.then(({ id: alertId }) => {
cy.visit(`/alerts/${alertId}/edit`);
cy.getByTestId("Criteria").should("exist");
@@ -14,8 +16,8 @@ describe("Edit Alert", () => {
});
it("edits the notification template and takes a screenshot", () => {
- cy.createQuery()
- .then(({ id: queryId }) => cy.createAlert(queryId, { custom_subject: "FOO", custom_body: "BAR" }))
+ createQuery()
+ .then(({ id: queryId }) => createAlert(queryId, { custom_subject: "FOO", custom_body: "BAR" }))
.then(({ id: alertId }) => {
cy.visit(`/alerts/${alertId}/edit`);
cy.getByTestId("AlertCustomTemplate").should("exist");
@@ -31,8 +33,8 @@ describe("Edit Alert", () => {
custom_body: "{{ ALERT_THRESHOLD }}",
};
- cy.createQuery()
- .then(({ id: queryId }) => cy.createAlert(queryId, options))
+ createQuery()
+ .then(({ id: queryId }) => createAlert(queryId, options))
.then(({ id: alertId }) => {
cy.visit(`/alerts/${alertId}/edit`);
cy.get(".alert-template-preview").click();
diff --git a/client/cypress/integration/alert/view_alert_spec.js b/client/cypress/integration/alert/view_alert_spec.js
index 2042050fd7..04ccbe7daf 100644
--- a/client/cypress/integration/alert/view_alert_spec.js
+++ b/client/cypress/integration/alert/view_alert_spec.js
@@ -1,13 +1,14 @@
+import { createAlert, createQuery, createUser, addDestinationSubscription } from "../../support/redash-api";
+
describe("View Alert", () => {
beforeEach(function() {
- cy.login().then(() => {
- cy.createQuery({ query: "select 1 as col_name" })
- .then(({ id: queryId }) => cy.createAlert(queryId, { column: "col_name" }))
- .then(({ id: alertId }) => {
- this.alertId = alertId;
- this.alertUrl = `/alerts/${alertId}`;
- });
- });
+ cy.login();
+ createQuery({ query: "select 1 as col_name" })
+ .then(({ id: queryId }) => createAlert(queryId, { column: "col_name" }))
+ .then(({ id: alertId }) => {
+ this.alertId = alertId;
+ this.alertUrl = `/alerts/${alertId}`;
+ });
});
it("renders the page and takes a screenshot", function() {
@@ -23,8 +24,8 @@ describe("View Alert", () => {
.should("not.exist");
cy.server();
- cy.route("GET", "**/api/destinations").as("Destinations");
- cy.route("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");
+ cy.route("GET", "api/destinations").as("Destinations");
+ cy.route("GET", "api/alerts/*/subscriptions").as("Subscriptions");
cy.visit(this.alertUrl);
@@ -41,7 +42,7 @@ describe("View Alert", () => {
describe("Alert Destination permissions", () => {
before(() => {
cy.login();
- cy.createUser({
+ createUser({
name: "Example User",
email: "user@redash.io",
password: "password",
@@ -50,11 +51,11 @@ describe("View Alert", () => {
it("hides remove button from non-author", function() {
cy.server();
- cy.route("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");
+ cy.route("GET", "api/alerts/*/subscriptions").as("Subscriptions");
cy.logout()
.then(() => cy.login()) // as admin
- .then(() => cy.addDestinationSubscription(this.alertId, "Test Email Destination"))
+ .then(() => addDestinationSubscription(this.alertId, "Test Email Destination"))
.then(() => {
cy.visit(this.alertUrl);
@@ -82,11 +83,11 @@ describe("View Alert", () => {
it("shows remove button for non-author admin", function() {
cy.server();
- cy.route("GET", "**/api/alerts/*/subscriptions").as("Subscriptions");
+ cy.route("GET", "api/alerts/*/subscriptions").as("Subscriptions");
cy.logout()
.then(() => cy.login("user@redash.io", "password"))
- .then(() => cy.addDestinationSubscription(this.alertId, "Test Email Destination"))
+ .then(() => addDestinationSubscription(this.alertId, "Test Email Destination"))
.then(() => {
cy.visit(this.alertUrl);
diff --git a/client/cypress/integration/dashboard/dashboard_list.js b/client/cypress/integration/dashboard/dashboard_list.js
deleted file mode 100644
index 3573f68dc4..0000000000
--- a/client/cypress/integration/dashboard/dashboard_list.js
+++ /dev/null
@@ -1,24 +0,0 @@
-describe("Dashboard list sort", () => {
- beforeEach(() => {
- cy.login();
- });
-
- it("creates one dashboard", () => {
- cy.visit("/dashboards");
- cy.getByTestId("CreateButton").click();
- cy.getByTestId("CreateDashboardMenuItem").click();
- cy.getByTestId("CreateDashboardDialog").within(() => {
- cy.get("input").type("A Foo Bar");
- cy.getByTestId("DashboardSaveButton").click();
- });
- });
-
- describe("Sorting table does not crash page ", () => {
- it("sorts", () => {
- cy.visit("/dashboards");
- cy.contains("Name").click();
- cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
- cy.getByTestId("ErrorMessage").should("not.exist");
- });
- });
-});
diff --git a/client/cypress/integration/dashboard/dashboard_spec.js b/client/cypress/integration/dashboard/dashboard_spec.js
index 7d5fff9db3..489a07ea5f 100644
--- a/client/cypress/integration/dashboard/dashboard_spec.js
+++ b/client/cypress/integration/dashboard/dashboard_spec.js
@@ -1,5 +1,6 @@
/* global cy, Cypress */
+import { createDashboard, addTextbox } from "../../support/redash-api";
import { getWidgetTestId } from "../../support/dashboard";
const menuWidth = 80;
@@ -15,7 +16,7 @@ describe("Dashboard", () => {
cy.getByTestId("CreateDashboardMenuItem").click();
cy.server();
- cy.route("POST", "**/api/dashboards").as("NewDashboard");
+ cy.route("POST", "api/dashboards").as("NewDashboard");
cy.getByTestId("CreateDashboardDialog").within(() => {
cy.getByTestId("DashboardSaveButton").should("be.disabled");
@@ -24,19 +25,19 @@ describe("Dashboard", () => {
});
cy.wait("@NewDashboard").then(xhr => {
- const id = Cypress._.get(xhr, "response.body.id");
- assert.isDefined(id, "Dashboard api call returns id");
+ const slug = Cypress._.get(xhr, "response.body.slug");
+ assert.isDefined(slug, "Dashboard api call returns slug");
cy.visit("/dashboards");
cy.getByTestId("DashboardLayoutContent").within(() => {
- cy.getByTestId(`DashboardId${id}`).should("exist");
+ cy.getByTestId(slug).should("exist");
});
});
});
it("archives dashboard", () => {
- cy.createDashboard("Foo Bar").then(({ id }) => {
- cy.visit(`/dashboards/${id}`);
+ createDashboard("Foo Bar").then(({ slug }) => {
+ cy.visit(`/dashboard/${slug}`);
cy.getByTestId("DashboardMoreButton").click();
@@ -51,22 +52,7 @@ describe("Dashboard", () => {
cy.visit("/dashboards");
cy.getByTestId("DashboardLayoutContent").within(() => {
- cy.getByTestId(`DashboardId${id}`).should("not.exist");
- });
- });
- });
-
- it("is accessible through multiple urls", () => {
- cy.server();
- cy.route("GET", "**/api/dashboards/*").as("LoadDashboard");
- cy.createDashboard("Dashboard multiple urls").then(({ id, slug }) => {
- [`/dashboards/${id}`, `/dashboards/${id}-anything-here`, `/dashboard/${slug}`].forEach(url => {
- cy.visit(url);
- cy.wait("@LoadDashboard");
- cy.getByTestId(`DashboardId${id}Container`).should("exist");
-
- // assert it always use the "/dashboards/{id}" path
- cy.location("pathname").should("contain", `/dashboards/${id}`);
+ cy.getByTestId(slug).should("not.exist");
});
});
});
@@ -74,11 +60,11 @@ describe("Dashboard", () => {
context("viewport width is at 800px", () => {
before(function() {
cy.login();
- cy.createDashboard("Foo Bar")
- .then(({ id }) => {
- this.dashboardUrl = `/dashboards/${id}`;
- this.dashboardEditUrl = `/dashboards/${id}?edit`;
- return cy.addTextbox(id, "Hello World!").then(getWidgetTestId);
+ createDashboard("Foo Bar")
+ .then(({ slug, id }) => {
+ this.dashboardUrl = `/dashboard/${slug}`;
+ this.dashboardEditUrl = `/dashboard/${slug}?edit`;
+ return addTextbox(id, "Hello World!").then(getWidgetTestId);
})
.then(elTestId => {
cy.visit(this.dashboardUrl);
@@ -87,7 +73,6 @@ describe("Dashboard", () => {
});
beforeEach(function() {
- cy.login();
cy.visit(this.dashboardUrl);
cy.viewport(800 + menuWidth, 800);
});
@@ -132,8 +117,8 @@ describe("Dashboard", () => {
context("viewport width is at 767px", () => {
before(function() {
cy.login();
- cy.createDashboard("Foo Bar").then(({ id }) => {
- this.dashboardUrl = `/dashboards/${id}`;
+ createDashboard("Foo Bar").then(({ slug }) => {
+ this.dashboardUrl = `/dashboard/${slug}`;
});
});
diff --git a/client/cypress/integration/dashboard/dashboard_tags_spec.js b/client/cypress/integration/dashboard/dashboard_tags_spec.js
index b1ef579af9..9d8ef131ae 100644
--- a/client/cypress/integration/dashboard/dashboard_tags_spec.js
+++ b/client/cypress/integration/dashboard/dashboard_tags_spec.js
@@ -1,14 +1,15 @@
+import { createDashboard } from "../../support/redash-api";
import { expectTagsToContain, typeInTagsSelectAndSave } from "../../support/tags";
describe("Dashboard Tags", () => {
beforeEach(function() {
cy.login();
- cy.createDashboard("Foo Bar").then(({ id }) => cy.visit(`/dashboards/${id}`));
+ createDashboard("Foo Bar").then(({ slug }) => cy.visit(`/dashboard/${slug}`));
});
it("is possible to add and edit tags", () => {
cy.server();
- cy.route("POST", "**/api/dashboards/*").as("DashboardSave");
+ cy.route("POST", "api/dashboards/*").as("DashboardSave");
cy.getByTestId("TagsControl").contains(".label", "Unpublished");
@@ -16,13 +17,13 @@ describe("Dashboard Tags", () => {
.should("contain", "Add tag")
.click();
- typeInTagsSelectAndSave("tag1{enter}tag2{enter}tag3{enter}");
+ typeInTagsSelectAndSave("tag1{enter}tag2{enter}tag3{enter}{esc}");
cy.wait("@DashboardSave");
expectTagsToContain(["tag1", "tag2", "tag3"]);
cy.getByTestId("EditTagsButton").click();
- typeInTagsSelectAndSave("tag4{enter}");
+ typeInTagsSelectAndSave("tag4{enter}{esc}");
cy.wait("@DashboardSave");
cy.reload();
diff --git a/client/cypress/integration/dashboard/filters_spec.js b/client/cypress/integration/dashboard/filters_spec.js
index d239332ee6..47091c600a 100644
--- a/client/cypress/integration/dashboard/filters_spec.js
+++ b/client/cypress/integration/dashboard/filters_spec.js
@@ -1,3 +1,4 @@
+import { createDashboard } from "../../support/redash-api";
import { createQueryAndAddWidget, editDashboard } from "../../support/dashboard";
import { expectTableToHaveLength, expectFirstColumnToHaveMembers } from "../../support/visualizations/table";
@@ -23,12 +24,12 @@ describe("Dashboard Filters", () => {
name: "Query Filters",
query: `SELECT stage1 AS "stage1::filter", stage2, value FROM (${SQL}) q`,
};
- cy.createDashboard("Dashboard Filters").then(dashboard => {
+ createDashboard("Dashboard Filters").then(dashboard => {
createQueryAndAddWidget(dashboard.id, queryData)
.as("widget1TestId")
.then(() => createQueryAndAddWidget(dashboard.id, queryData, { position: { col: 4 } }))
.as("widget2TestId")
- .then(() => cy.visit(`/dashboards/${dashboard.id}`));
+ .then(() => cy.visit(`/dashboard/${dashboard.slug}`));
});
});
@@ -39,7 +40,7 @@ describe("Dashboard Filters", () => {
cy.getByTestId("DashboardFilters").within(() => {
cy.getByTestId("FilterName-stage1::filter")
- .find(".ant-select-selection-item")
+ .find(".ant-select-selection-selected-value")
.should("have.text", "a");
});
@@ -52,7 +53,7 @@ describe("Dashboard Filters", () => {
.click();
});
- cy.contains(".ant-select-item-option-content:visible", "b").click();
+ cy.contains("li.ant-select-dropdown-menu-item:visible", "b").click();
cy.getByTestId(this.widget1TestId).within(() => {
expectTableToHaveLength(3);
@@ -74,7 +75,7 @@ describe("Dashboard Filters", () => {
.click();
});
- cy.contains(".ant-select-item-option-content:visible", "c").click();
+ cy.contains("li.ant-select-dropdown-menu-item:visible", "c").click();
[this.widget1TestId, this.widget2TestId].forEach(widgetTestId =>
cy.getByTestId(widgetTestId).within(() => {
diff --git a/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js b/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js
index 19883a0177..31a7361979 100644
--- a/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js
+++ b/client/cypress/integration/dashboard/grid_compliant_widgets_spec.js
@@ -1,5 +1,6 @@
/* global cy */
+import { createDashboard, addTextbox } from "../../support/redash-api";
import { getWidgetTestId, editDashboard, resizeBy } from "../../support/dashboard";
const menuWidth = 80;
@@ -8,10 +9,10 @@ describe("Grid compliant widgets", () => {
beforeEach(function() {
cy.login();
cy.viewport(1215 + menuWidth, 800);
- cy.createDashboard("Foo Bar")
- .then(({ id }) => {
- this.dashboardUrl = `/dashboards/${id}`;
- return cy.addTextbox(id, "Hello World!").then(getWidgetTestId);
+ createDashboard("Foo Bar")
+ .then(({ slug, id }) => {
+ this.dashboardUrl = `/dashboard/${slug}`;
+ return addTextbox(id, "Hello World!").then(getWidgetTestId);
})
.then(elTestId => {
cy.visit(this.dashboardUrl);
@@ -49,7 +50,7 @@ describe("Grid compliant widgets", () => {
it("auto saves after drag", () => {
cy.server();
- cy.route("POST", "**/api/widgets/*").as("WidgetSave");
+ cy.route("POST", "api/widgets/*").as("WidgetSave");
editDashboard();
cy.get("@textboxEl").dragBy(330);
@@ -117,7 +118,7 @@ describe("Grid compliant widgets", () => {
it("auto saves after resize", () => {
cy.server();
- cy.route("POST", "**/api/widgets/*").as("WidgetSave");
+ cy.route("POST", "api/widgets/*").as("WidgetSave");
editDashboard();
resizeBy(cy.get("@textboxEl"), 200);
diff --git a/client/cypress/integration/dashboard/parameter_spec.js b/client/cypress/integration/dashboard/parameter_mapping_spec.js
similarity index 53%
rename from client/cypress/integration/dashboard/parameter_spec.js
rename to client/cypress/integration/dashboard/parameter_mapping_spec.js
index 0d3cc7a621..04dc58a439 100644
--- a/client/cypress/integration/dashboard/parameter_spec.js
+++ b/client/cypress/integration/dashboard/parameter_mapping_spec.js
@@ -1,24 +1,20 @@
+import { createDashboard } from "../../support/redash-api";
import { createQueryAndAddWidget } from "../../support/dashboard";
-describe("Dashboard Parameters", () => {
- const parameters = [
- { name: "param1", title: "Parameter 1", type: "text", value: "example1" },
- { name: "param2", title: "Parameter 2", type: "text", value: "example2" },
- ];
-
+describe("Parameter Mapping", () => {
beforeEach(function() {
cy.login();
- cy.createDashboard("Foo Bar")
- .then(({ id }) => {
+ createDashboard("Foo Bar")
+ .then(({ slug, id }) => {
this.dashboardId = id;
- this.dashboardUrl = `/dashboards/${id}`;
+ this.dashboardUrl = `/dashboard/${slug}`;
})
.then(() => {
const queryData = {
name: "Text Parameter",
- query: "SELECT '{{param1}}', '{{param2}}' AS parameter",
+ query: "SELECT '{{test-parameter}}' AS parameter",
options: {
- parameters,
+ parameters: [{ name: "test-parameter", title: "Test Parameter", type: "text", value: "example" }],
},
};
const widgetOptions = { position: { col: 0, row: 0, sizeX: 3, sizeY: 10, autoHeight: false } };
@@ -29,7 +25,7 @@ describe("Dashboard Parameters", () => {
});
});
- const openMappingOptions = widgetTestId => {
+ const openMappingOptions = (widgetTestId, paramName) => {
cy.getByTestId(widgetTestId).within(() => {
cy.getByTestId("WidgetDropdownButton").click();
});
@@ -37,33 +33,24 @@ describe("Dashboard Parameters", () => {
cy.getByTestId("WidgetDropdownButtonMenu")
.contains("Edit Parameters")
.click();
+
+ cy.getByTestId(`EditParamMappingButon-${paramName}`).click();
};
- const saveMappingOptions = (closeMappingMenu = false) => {
- return cy
- .getByTestId("EditParamMappingPopover")
- .filter(":visible")
- .as("Popover")
- .within(() => {
- // This is needed to grant the element will have finished loading
- // eslint-disable-next-line cypress/no-unnecessary-waiting
- cy.wait(500);
- cy.contains("button", "OK").click();
- })
- .then(() => {
- if (closeMappingMenu) {
- cy.contains("button", "OK").click();
- }
- return cy.get("@Popover").should("not.be.visible");
- });
+ const saveMappingOptions = () => {
+ cy.getByTestId("EditParamMappingPopover").within(() => {
+ cy.contains("button", "OK").click();
+ });
+
+ cy.contains("button", "OK").click();
};
it("supports widget parameters", function() {
// widget parameter mapping is the default for the API
cy.getByTestId(this.widgetTestId).within(() => {
- cy.getByTestId("TableVisualization").should("contain", "example1");
+ cy.getByTestId("TableVisualization").should("contain", "example");
- cy.getByTestId("ParameterName-param1")
+ cy.getByTestId("ParameterName-test-parameter")
.find("input")
.type("{selectall}Redash");
@@ -75,9 +62,32 @@ describe("Dashboard Parameters", () => {
cy.getByTestId("DashboardParameters").should("not.exist");
});
+ it("supports dashboard parameters", function() {
+ openMappingOptions(this.widgetTestId, "test-parameter");
+
+ cy.getByTestId("NewDashboardParameterOption").click();
+
+ saveMappingOptions();
+
+ cy.getByTestId(this.widgetTestId).within(() => {
+ cy.getByTestId("ParameterName-test-parameter").should("not.exist");
+ });
+
+ cy.getByTestId("DashboardParameters").within(() => {
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .type("{selectall}DashboardParam");
+
+ cy.getByTestId("ParameterApplyButton").click();
+ });
+
+ cy.getByTestId(this.widgetTestId).within(() => {
+ cy.getByTestId("TableVisualization").should("contain", "DashboardParam");
+ });
+ });
+
it("supports static values for parameters", function() {
- openMappingOptions(this.widgetTestId);
- cy.getByTestId("EditParamMappingButton-param1").click();
+ openMappingOptions(this.widgetTestId, "test-parameter");
cy.getByTestId("StaticValueOption").click();
@@ -87,10 +97,10 @@ describe("Dashboard Parameters", () => {
.type("{selectall}StaticValue");
});
- saveMappingOptions(true);
+ saveMappingOptions();
cy.getByTestId(this.widgetTestId).within(() => {
- cy.getByTestId("ParameterName-param1").should("not.exist");
+ cy.getByTestId("ParameterName-test-parameter").should("not.exist");
});
cy.getByTestId("DashboardParameters").should("not.exist");
diff --git a/client/cypress/integration/dashboard/sharing_spec.js b/client/cypress/integration/dashboard/sharing_spec.js
index 5e73b54e51..adba653603 100644
--- a/client/cypress/integration/dashboard/sharing_spec.js
+++ b/client/cypress/integration/dashboard/sharing_spec.js
@@ -1,45 +1,15 @@
/* global cy */
+import { createDashboard, createQuery } from "../../support/redash-api";
import { editDashboard, shareDashboard, createQueryAndAddWidget } from "../../support/dashboard";
describe("Dashboard Sharing", () => {
beforeEach(function() {
cy.login();
- cy.createDashboard("Foo Bar").then(({ id }) => {
+ createDashboard("Foo Bar").then(({ slug, id }) => {
this.dashboardId = id;
- this.dashboardUrl = `/dashboards/${id}`;
+ this.dashboardUrl = `/dashboard/${slug}`;
});
- cy.updateOrgSettings({ disable_public_urls: false });
- });
-
- it("is unavailable when public urls feature is disabled", function() {
- const queryData = {
- query: "select 1",
- };
-
- const position = { autoHeight: false, sizeY: 6 };
- createQueryAndAddWidget(this.dashboardId, queryData, { position })
- .then(() => {
- cy.visit(this.dashboardUrl);
- return shareDashboard();
- })
- .then(secretAddress => {
- // disable the feature
- cy.updateOrgSettings({ disable_public_urls: true });
-
- // check the feature is disabled
- cy.visit(this.dashboardUrl);
- cy.getByTestId("DashboardMoreButton").should("exist");
- cy.getByTestId("OpenShareForm").should("not.exist");
-
- cy.logout();
- cy.visit(secretAddress);
- cy.wait(1500); // eslint-disable-line cypress/no-unnecessary-waiting
- cy.getByTestId("TableVisualization").should("not.exist");
-
- cy.login();
- cy.updateOrgSettings({ disable_public_urls: false });
- });
});
it("is possible if all queries are safe", function() {
@@ -53,7 +23,7 @@ describe("Dashboard Sharing", () => {
};
const dashboardUrl = this.dashboardUrl;
- cy.createQuery({ options }).then(({ id: queryId }) => {
+ createQuery({ options }).then(({ id: queryId }) => {
cy.visit(dashboardUrl);
editDashboard();
cy.getByTestId("AddWidgetButton").click();
@@ -178,7 +148,7 @@ describe("Dashboard Sharing", () => {
};
const dashboardUrl = this.dashboardUrl;
- cy.createQuery({ options }).then(({ id: queryId }) => {
+ createQuery({ options }).then(({ id: queryId }) => {
cy.visit(dashboardUrl);
editDashboard();
cy.getByTestId("AddWidgetButton").click();
diff --git a/client/cypress/integration/dashboard/textbox_spec.js b/client/cypress/integration/dashboard/textbox_spec.js
index 669e73e912..7e5beffa54 100644
--- a/client/cypress/integration/dashboard/textbox_spec.js
+++ b/client/cypress/integration/dashboard/textbox_spec.js
@@ -1,13 +1,14 @@
/* global cy */
+import { createDashboard, addTextbox } from "../../support/redash-api";
import { getWidgetTestId, editDashboard } from "../../support/dashboard";
describe("Textbox", () => {
beforeEach(function() {
cy.login();
- cy.createDashboard("Foo Bar").then(({ id }) => {
+ createDashboard("Foo Bar").then(({ slug, id }) => {
this.dashboardId = id;
- this.dashboardUrl = `/dashboards/${id}`;
+ this.dashboardUrl = `/dashboard/${slug}`;
});
});
@@ -30,7 +31,7 @@ describe("Textbox", () => {
});
it("removes textbox by X button", function() {
- cy.addTextbox(this.dashboardId, "Hello World!")
+ addTextbox(this.dashboardId, "Hello World!")
.then(getWidgetTestId)
.then(elTestId => {
cy.visit(this.dashboardUrl);
@@ -46,7 +47,7 @@ describe("Textbox", () => {
});
it("removes textbox by menu", function() {
- cy.addTextbox(this.dashboardId, "Hello World!")
+ addTextbox(this.dashboardId, "Hello World!")
.then(getWidgetTestId)
.then(elTestId => {
cy.visit(this.dashboardUrl);
@@ -64,11 +65,11 @@ describe("Textbox", () => {
it("allows opening menu after removal", function() {
let elTestId1;
- cy.addTextbox(this.dashboardId, "txb 1")
+ addTextbox(this.dashboardId, "txb 1")
.then(getWidgetTestId)
.then(elTestId => {
elTestId1 = elTestId;
- return cy.addTextbox(this.dashboardId, "txb 2").then(getWidgetTestId);
+ return addTextbox(this.dashboardId, "txb 2").then(getWidgetTestId);
})
.then(elTestId2 => {
cy.visit(this.dashboardUrl);
@@ -98,7 +99,7 @@ describe("Textbox", () => {
});
it("edits textbox", function() {
- cy.addTextbox(this.dashboardId, "Hello World!")
+ addTextbox(this.dashboardId, "Hello World!")
.then(getWidgetTestId)
.then(elTestId => {
cy.visit(this.dashboardUrl);
@@ -132,8 +133,8 @@ describe("Textbox", () => {
const txb2Pos = { col: 1, row: 1, sizeX: 3, sizeY: 4 };
cy.viewport(1215, 800);
- cy.addTextbox(id, "x", { position: txb1Pos })
- .then(() => cy.addTextbox(id, "x", { position: txb2Pos }))
+ addTextbox(id, "x", { position: txb1Pos })
+ .then(() => addTextbox(id, "x", { position: txb2Pos }))
.then(getWidgetTestId)
.then(elTestId => {
cy.visit(this.dashboardUrl);
@@ -141,7 +142,7 @@ describe("Textbox", () => {
})
.should($el => {
const { top, left } = $el.offset();
- expect(top).to.be.oneOf([162, 162.015625]);
+ expect(top).to.eq(162);
expect(left).to.eq(282);
expect($el.width()).to.eq(545);
expect($el.height()).to.eq(185);
diff --git a/client/cypress/integration/dashboard/widget_spec.js b/client/cypress/integration/dashboard/widget_spec.js
index 2c77fff832..2eff9e4f21 100644
--- a/client/cypress/integration/dashboard/widget_spec.js
+++ b/client/cypress/integration/dashboard/widget_spec.js
@@ -1,13 +1,14 @@
/* global cy */
+import { createDashboard, createQuery } from "../../support/redash-api";
import { createQueryAndAddWidget, editDashboard, resizeBy } from "../../support/dashboard";
describe("Widget", () => {
beforeEach(function() {
cy.login();
- cy.createDashboard("Foo Bar").then(({ id }) => {
+ createDashboard("Foo Bar").then(({ slug, id }) => {
this.dashboardId = id;
- this.dashboardUrl = `/dashboards/${id}`;
+ this.dashboardUrl = `/dashboard/${slug}`;
});
});
@@ -18,7 +19,7 @@ describe("Widget", () => {
};
it("adds widget", function() {
- cy.createQuery().then(({ id: queryId }) => {
+ createQuery().then(({ id: queryId }) => {
cy.visit(this.dashboardUrl);
editDashboard();
cy.getByTestId("AddWidgetButton").click();
@@ -103,7 +104,7 @@ describe("Widget", () => {
it("grows when dynamically adding table rows", () => {
// listen to results
cy.server();
- cy.route("GET", "**/api/query_results/*").as("FreshResults");
+ cy.route("GET", "api/query_results/*").as("FreshResults");
// start with 1 table row
cy.get("@paramInput")
@@ -131,7 +132,7 @@ describe("Widget", () => {
it("revokes auto height after manual height adjustment", () => {
// listen to results
cy.server();
- cy.route("GET", "**/api/query_results/*").as("FreshResults");
+ cy.route("GET", "api/query_results/*").as("FreshResults");
editDashboard();
@@ -177,7 +178,7 @@ describe("Widget", () => {
cy.visit(this.dashboardUrl);
cy.getByTestId("TableVisualization")
.its("0.offsetHeight")
- .should("be.oneOf", [380, 381]);
+ .should("eq", 381);
cy.percySnapshot("Shows correct height of table visualization");
});
});
diff --git a/client/cypress/integration/data-source/create_data_source_spec.js b/client/cypress/integration/data-source/create_data_source_spec.js
index 1549cef6b1..d062293285 100644
--- a/client/cypress/integration/data-source/create_data_source_spec.js
+++ b/client/cypress/integration/data-source/create_data_source_spec.js
@@ -1,25 +1,12 @@
describe("Create Data Source", () => {
beforeEach(() => {
cy.login();
- });
-
- it("opens the creation dialog when clicking in the create link or button", () => {
- cy.visit("/data_sources");
- cy.server();
- cy.route("**/api/data_sources", []); // force an empty response
-
- ["CreateDataSourceButton", "CreateDataSourceLink"].forEach(createElementTestId => {
- cy.getByTestId(createElementTestId).click();
- cy.getByTestId("CreateSourceDialog").should("exist");
- cy.getByTestId("CreateSourceCancelButton").click();
- cy.getByTestId("CreateSourceDialog").should("not.exist");
- });
+ cy.visit("/data_sources/new");
});
it("renders the page and takes a screenshot", function() {
- cy.visit("/data_sources/new");
cy.server();
- cy.route("**/api/data_sources/types").as("DataSourceTypesRequest");
+ cy.route("api/data_sources/types").as("DataSourceTypesRequest");
cy.wait("@DataSourceTypesRequest")
.then(({ response }) => response.body.filter(type => type.deprecated))
@@ -36,7 +23,6 @@ describe("Create Data Source", () => {
});
it("creates a new PostgreSQL data source", () => {
- cy.visit("/data_sources/new");
cy.getByTestId("SearchSource").type("PostgreSQL");
cy.getByTestId("CreateSourceDialog")
.contains("PostgreSQL")
@@ -47,7 +33,7 @@ describe("Create Data Source", () => {
cy.getByTestId("User").type("postgres");
cy.getByTestId("Password").type("postgres");
cy.getByTestId("Database Name").type("postgres{enter}");
- cy.getByTestId("CreateSourceSaveButton").click({ force: true });
+ cy.getByTestId("CreateSourceButton").click();
cy.contains("Saved.");
});
diff --git a/client/cypress/integration/destination/create_destination_spec.js b/client/cypress/integration/destination/create_destination_spec.js
index 9e0ff79fce..5a51d336a2 100644
--- a/client/cypress/integration/destination/create_destination_spec.js
+++ b/client/cypress/integration/destination/create_destination_spec.js
@@ -1,3 +1,5 @@
+import { createDestination } from "../../support/redash-api";
+
describe("Create Destination", () => {
beforeEach(() => {
cy.login();
@@ -6,7 +8,7 @@ describe("Create Destination", () => {
it("renders the page and takes a screenshot", function() {
cy.visit("/destinations/new");
cy.server();
- cy.route("**/api/destinations/types").as("DestinationTypesRequest");
+ cy.route("api/destinations/types").as("DestinationTypesRequest");
cy.wait("@DestinationTypesRequest")
.then(({ response }) => response.body.filter(type => type.deprecated))
@@ -15,7 +17,7 @@ describe("Create Destination", () => {
cy.getByTestId("PreviewItem")
.then($previewItems => Cypress.$.map($previewItems, item => Cypress.$(item).attr("data-test-type")))
- .then(availableTypes => expect(availableTypes).not.to.contain.oneOf(this.deprecatedTypes));
+ .then(availableTypes => expect(availableTypes).not.to.contain.members(this.deprecatedTypes));
cy.getByTestId("CreateSourceDialog").should("contain", "Email");
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
@@ -23,7 +25,7 @@ describe("Create Destination", () => {
});
it("shows a custom error message when destination name is already taken", () => {
- cy.createDestination("Slack Destination", "slack").then(() => {
+ createDestination("Slack Destination", "slack").then(() => {
cy.visit("/destinations/new");
cy.getByTestId("SearchSource").type("Slack");
@@ -32,7 +34,7 @@ describe("Create Destination", () => {
.click();
cy.getByTestId("Name").type("Slack Destination");
- cy.getByTestId("CreateSourceSaveButton").click();
+ cy.getByTestId("CreateSourceButton").click();
cy.contains("Alert Destination with the name Slack Destination already exists.");
});
diff --git a/client/cypress/integration/embed/share_embed_spec.js b/client/cypress/integration/embed/share_embed_spec.js
index 7928aa5b14..e077e19d37 100644
--- a/client/cypress/integration/embed/share_embed_spec.js
+++ b/client/cypress/integration/embed/share_embed_spec.js
@@ -1,48 +1,12 @@
+import { createQuery } from "../../support/redash-api";
+
describe("Embedded Queries", () => {
beforeEach(() => {
cy.login();
- cy.updateOrgSettings({ disable_public_urls: false });
- });
-
- it("is unavailable when public urls feature is disabled", () => {
- cy.createQuery({ query: "select name from users order by name" }).then(query => {
- cy.visit(`/queries/${query.id}/source`);
- cy.getByTestId("ExecuteButton").click();
- cy.getByTestId("QueryPageVisualizationTabs", { timeout: 10000 }).should("exist");
- cy.clickThrough(`
- QueryControlDropdownButton
- ShowEmbedDialogButton
- `);
- cy.getByTestId("EmbedIframe")
- .invoke("text")
- .then(embedUrl => {
- // disable the feature
- cy.updateOrgSettings({ disable_public_urls: true });
-
- // check the feature is disabled
- cy.visit(`/queries/${query.id}/source`);
- cy.getByTestId("QueryPageVisualizationTabs", { timeout: 10000 }).should("exist");
- cy.getByTestId("QueryPageHeaderMoreButton").click();
- cy.get(".ant-dropdown-menu-item")
- .should("exist")
- .should("not.contain", "Show API Key");
- cy.getByTestId("QueryControlDropdownButton").click();
- cy.get(".ant-dropdown-menu-item").should("exist");
- cy.getByTestId("ShowEmbedDialogButton").should("not.exist");
-
- cy.logout();
- cy.visit(embedUrl);
- cy.wait(1500); // eslint-disable-line cypress/no-unnecessary-waiting
- cy.getByTestId("TableVisualization").should("not.exist");
-
- cy.login();
- cy.updateOrgSettings({ disable_public_urls: false });
- });
- });
});
it("can be shared without parameters", () => {
- cy.createQuery({ query: "select name from users order by name" }).then(query => {
+ createQuery({ query: "select name from users order by name" }).then(query => {
cy.visit(`/queries/${query.id}/source`);
cy.getByTestId("ExecuteButton").click();
cy.getByTestId("QueryPageVisualizationTabs", { timeout: 10000 }).should("exist");
diff --git a/client/cypress/integration/query/create_query_spec.js b/client/cypress/integration/query/create_query_spec.js
index 12ea5161af..5071977a94 100644
--- a/client/cypress/integration/query/create_query_spec.js
+++ b/client/cypress/integration/query/create_query_spec.js
@@ -7,7 +7,7 @@ describe("Create Query", () => {
it("executes and saves a query", () => {
cy.clickThrough(`
SelectDataSource
- SelectDataSource${Cypress.env("dataSourceId")}
+ SelectDataSource1
`);
cy.getByTestId("QueryEditor")
@@ -22,6 +22,6 @@ describe("Create Query", () => {
cy.percySnapshot("Edit Query");
cy.getByTestId("SaveButton").click();
- cy.url().should("match", /\/queries\/.+\/source/);
+ cy.url().should("match", /\/queries\/\d+\/source/);
});
});
diff --git a/client/cypress/integration/query/filters_spec.js b/client/cypress/integration/query/filters_spec.js
index 50262738db..3e2a0b67b1 100644
--- a/client/cypress/integration/query/filters_spec.js
+++ b/client/cypress/integration/query/filters_spec.js
@@ -1,3 +1,4 @@
+import { createQuery } from "../../support/redash-api";
import { expectTableToHaveLength, expectFirstColumnToHaveMembers } from "../../support/visualizations/table";
const SQL = `
@@ -26,13 +27,13 @@ describe("Query Filters", () => {
query: `SELECT stage1 AS "stage1::filter", stage2, value FROM (${SQL}) q`,
};
- cy.createQuery(queryData).then(({ id }) => cy.visit(`/queries/${id}`));
+ createQuery(queryData).then(({ id }) => cy.visit(`/queries/${id}`));
cy.getByTestId("ExecuteButton").click();
});
it("filters rows in a Table Visualization", () => {
cy.getByTestId("FilterName-stage1::filter")
- .find(".ant-select-selection-item")
+ .find(".ant-select-selection-selected-value")
.should("have.text", "a");
expectTableToHaveLength(4);
@@ -42,7 +43,7 @@ describe("Query Filters", () => {
.find(".ant-select")
.click();
- cy.contains(".ant-select-item-option-content", "b").click();
+ cy.contains("li.ant-select-dropdown-menu-item", "b").click();
expectTableToHaveLength(3);
expectFirstColumnToHaveMembers(["b", "b", "b"]);
@@ -56,62 +57,46 @@ describe("Query Filters", () => {
query: `SELECT stage1 AS "stage1::multi-filter", stage2, value FROM (${SQL}) q`,
};
- cy.createQuery(queryData).then(({ id }) => cy.visit(`/queries/${id}`));
+ createQuery(queryData).then(({ id }) => cy.visit(`/queries/${id}`));
cy.getByTestId("ExecuteButton").click();
});
function expectSelectedOptionsToHaveMembers(values) {
cy.getByTestId("FilterName-stage1::multi-filter")
- .find(".ant-select-selection-item-content")
+ .find(".ant-select-selection__choice__content")
.then($selectedOptions => Cypress.$.map($selectedOptions, item => Cypress.$(item).text()))
.then(selectedOptions => expect(selectedOptions).to.have.members(values));
}
it("filters rows in a Table Visualization", () => {
- // Defaults to All Options Selected
-
- expectSelectedOptionsToHaveMembers(["a", "b", "c"]);
- expectTableToHaveLength(11);
- expectFirstColumnToHaveMembers(["a", "a", "a", "a", "b", "b", "b", "c", "c", "c", "c"]);
-
- // Clear Option
-
- cy.getByTestId("FilterName-stage1::multi-filter")
- .find(".ant-select-selector")
- .click();
- cy.getByTestId("ClearOption").click();
- cy.getByTestId("FilterName-stage1::multi-filter").click(); // close dropdown
-
- cy.getByTestId("TableVisualization").should("not.exist");
-
- // Single Option selected
-
- cy.getByTestId("FilterName-stage1::multi-filter")
- .find(".ant-select-selector")
- .click();
- cy.contains(".ant-select-item-option-grouped > .ant-select-item-option-content", "a").click();
- cy.getByTestId("FilterName-stage1::multi-filter").click(); // close dropdown
-
expectSelectedOptionsToHaveMembers(["a"]);
expectTableToHaveLength(4);
expectFirstColumnToHaveMembers(["a", "a", "a", "a"]);
- // Two Options selected
-
cy.getByTestId("FilterName-stage1::multi-filter")
- .find(".ant-select-selector")
+ .find(".ant-select-selection")
.click();
- cy.contains(".ant-select-item-option-content", "b").click();
+ cy.contains("li.ant-select-dropdown-menu-item", "b").click();
cy.getByTestId("FilterName-stage1::multi-filter").click(); // close dropdown
expectSelectedOptionsToHaveMembers(["a", "b"]);
expectTableToHaveLength(7);
expectFirstColumnToHaveMembers(["a", "a", "a", "a", "b", "b", "b"]);
+ // Clear Option
+
+ cy.getByTestId("FilterName-stage1::multi-filter")
+ .find(".ant-select-selection")
+ .click();
+ cy.getByTestId("ClearOption").click();
+ cy.getByTestId("FilterName-stage1::multi-filter").click(); // close dropdown
+
+ cy.getByTestId("TableVisualization").should("not.exist");
+
// Select All Option
cy.getByTestId("FilterName-stage1::multi-filter")
- .find(".ant-select-selector")
+ .find(".ant-select-selection")
.click();
cy.getByTestId("SelectAllOption").click();
cy.getByTestId("FilterName-stage1::multi-filter").click(); // close dropdown
diff --git a/client/cypress/integration/query/parameter_spec.js b/client/cypress/integration/query/parameter_spec.js
index dd26163591..a415e4581a 100644
--- a/client/cypress/integration/query/parameter_spec.js
+++ b/client/cypress/integration/query/parameter_spec.js
@@ -1,15 +1,10 @@
-import { dragParam } from "../../support/parameters";
-import dayjs from "dayjs";
-
-function openAndSearchAntdDropdown(testId, paramOption) {
- cy.getByTestId(testId).find(".ant-select-selection-search-input").type(paramOption, { force: true });
-}
+import { createQuery } from "../../support/redash-api";
describe("Parameter", () => {
- const expectDirtyStateChange = (edit) => {
+ const expectDirtyStateChange = edit => {
cy.getByTestId("ParameterName-test-parameter")
.find(".parameter-input")
- .should(($el) => {
+ .should($el => {
assert.isUndefined($el.data("dirty"));
});
@@ -17,7 +12,7 @@ describe("Parameter", () => {
cy.getByTestId("ParameterName-test-parameter")
.find(".parameter-input")
- .should(($el) => {
+ .should($el => {
assert.isTrue($el.data("dirty"));
});
};
@@ -36,11 +31,13 @@ describe("Parameter", () => {
},
};
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
});
it("updates the results after clicking Apply", () => {
- cy.getByTestId("ParameterName-test-parameter").find("input").type("Redash");
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .type("Redash");
cy.getByTestId("ParameterApplyButton").click();
@@ -49,64 +46,11 @@ describe("Parameter", () => {
it("sets dirty state when edited", () => {
expectDirtyStateChange(() => {
- cy.getByTestId("ParameterName-test-parameter").find("input").type("Redash");
- });
- });
- });
-
- describe("Text Pattern Parameter", () => {
- beforeEach(() => {
- const queryData = {
- name: "Text Pattern Parameter",
- query: "SELECT '{{test-parameter}}' AS parameter",
- options: {
- parameters: [{ name: "test-parameter", title: "Test Parameter", type: "text-pattern", regex: "a.*a" }],
- },
- };
-
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
- });
-
- it("updates the results after clicking Apply", () => {
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}arta");
-
- cy.getByTestId("ParameterApplyButton").click();
-
- cy.getByTestId("TableVisualization").should("contain", "arta");
-
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}arounda");
-
- cy.getByTestId("ParameterApplyButton").click();
-
- cy.getByTestId("TableVisualization").should("contain", "arounda");
- });
-
- it("throws error message with invalid query request", () => {
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}arta");
-
- cy.getByTestId("ParameterApplyButton").click();
-
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}abcab");
-
- cy.getByTestId("ParameterApplyButton").click();
-
- cy.getByTestId("QueryExecutionStatus").should("exist");
- });
-
- it("sets dirty state when edited", () => {
- expectDirtyStateChange(() => {
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}arta");
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .type("Redash");
});
});
-
- it("doesn't let user save invalid regex", () => {
- cy.get(".fa-cog").click();
- cy.getByTestId("RegexPatternInput").type("{selectall}[");
- cy.contains("Invalid Regex Pattern").should("exist");
- cy.getByTestId("SaveParameterSettings").click();
- cy.get(".fa-cog").click();
- cy.getByTestId("RegexPatternInput").should("not.equal", "[");
- });
});
describe("Number Parameter", () => {
@@ -119,17 +63,21 @@ describe("Parameter", () => {
},
};
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
});
it("updates the results after clicking Apply", () => {
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}42");
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .type("{selectall}42");
cy.getByTestId("ParameterApplyButton").click();
cy.getByTestId("TableVisualization").should("contain", 42);
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}31415");
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .type("{selectall}31415");
cy.getByTestId("ParameterApplyButton").click();
@@ -138,7 +86,9 @@ describe("Parameter", () => {
it("sets dirty state when edited", () => {
expectDirtyStateChange(() => {
- cy.getByTestId("ParameterName-test-parameter").find("input").type("{selectall}42");
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .type("{selectall}42");
});
});
});
@@ -155,14 +105,15 @@ describe("Parameter", () => {
},
};
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
});
it("updates the results after selecting a value", () => {
- openAndSearchAntdDropdown("ParameterName-test-parameter", "value2"); // asserts option filter prop
+ cy.getByTestId("ParameterName-test-parameter")
+ .find(".ant-select")
+ .click();
- // only the filtered option should be on the DOM
- cy.get(".ant-select-item-option").should("have.length", 1).and("contain", "value2").click();
+ cy.contains("li.ant-select-dropdown-menu-item", "value2").click();
cy.getByTestId("ParameterApplyButton").click();
// ensure that query is being executed
@@ -180,11 +131,13 @@ describe("Parameter", () => {
SaveParameterSettings
`);
- cy.getByTestId("ParameterName-test-parameter").find(".ant-select-selection-search").click();
+ cy.getByTestId("ParameterName-test-parameter")
+ .find(".ant-select")
+ .click();
// select all unselected options
- cy.get(".ant-select-item-option").each(($option) => {
- if (!$option.hasClass("ant-select-item-option-selected")) {
+ cy.get("li.ant-select-dropdown-menu-item").each($option => {
+ if (!$option.hasClass("ant-select-dropdown-menu-item-selected")) {
cy.wrap($option).click();
}
});
@@ -198,9 +151,11 @@ describe("Parameter", () => {
it("sets dirty state when edited", () => {
expectDirtyStateChange(() => {
- cy.getByTestId("ParameterName-test-parameter").find(".ant-select").click();
+ cy.getByTestId("ParameterName-test-parameter")
+ .find(".ant-select")
+ .click();
- cy.contains(".ant-select-item-option", "value2").click();
+ cy.contains("li.ant-select-dropdown-menu-item", "value2").click();
});
});
});
@@ -212,7 +167,7 @@ describe("Parameter", () => {
name: "Dropdown Query",
query: "",
};
- cy.createQuery(dropdownQueryData, true).then((dropdownQuery) => {
+ createQuery(dropdownQueryData, true).then(dropdownQuery => {
const queryData = {
name: "Query Based Dropdown Parameter",
query: "SELECT '{{test-parameter}}' AS parameter",
@@ -223,16 +178,16 @@ describe("Parameter", () => {
},
};
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
});
});
it("should show a 'No options available' message when you click", () => {
cy.getByTestId("ParameterName-test-parameter")
- .find(".ant-select:not(.ant-select-disabled) .ant-select-selector")
+ .find(".ant-select:not(.ant-select-disabled) .ant-select-selection")
.click();
- cy.contains(".ant-select-item-empty", "No options available");
+ cy.contains("li.ant-select-dropdown-menu-item", "No options available");
});
});
@@ -244,7 +199,7 @@ describe("Parameter", () => {
SELECT 'value2' AS name, 2 AS value UNION ALL
SELECT 'value3' AS name, 3 AS value`,
};
- cy.createQuery(dropdownQueryData, true).then((dropdownQuery) => {
+ createQuery(dropdownQueryData, true).then(dropdownQuery => {
const queryData = {
name: "Query Based Dropdown Parameter",
query: "SELECT '{{test-parameter}}' AS parameter",
@@ -262,23 +217,10 @@ describe("Parameter", () => {
.and("contain", "value2")
.and("contain", "value3");
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
});
});
- it("updates the results after selecting a value", () => {
- openAndSearchAntdDropdown("ParameterName-test-parameter", "value2"); // asserts option filter prop
-
- // only the filtered option should be on the DOM
- cy.get(".ant-select-item-option").should("have.length", 1).and("contain", "value2").click();
-
- cy.getByTestId("ParameterApplyButton").click();
- // ensure that query is being executed
- cy.getByTestId("QueryExecutionStatus").should("exist");
-
- cy.getByTestId("TableVisualization").should("contain", "2");
- });
-
it("supports multi-selection", () => {
cy.clickThrough(`
ParameterSettings-test-parameter
@@ -288,10 +230,12 @@ describe("Parameter", () => {
SaveParameterSettings
`);
- cy.getByTestId("ParameterName-test-parameter").find(".ant-select").click();
+ cy.getByTestId("ParameterName-test-parameter")
+ .find(".ant-select")
+ .click();
// make sure all options are unselected and select all
- cy.get(".ant-select-item-option").each(($option) => {
+ cy.get("li.ant-select-dropdown-menu-item").each($option => {
expect($option).not.to.have.class("ant-select-dropdown-menu-item-selected");
cy.wrap($option).click();
});
@@ -305,13 +249,17 @@ describe("Parameter", () => {
});
});
- const selectCalendarDate = (date) => {
- cy.getByTestId("ParameterName-test-parameter").find("input").click();
-
- cy.get(".ant-picker-panel").contains(".ant-picker-cell-inner", date).click();
- };
-
describe("Date Parameter", () => {
+ const selectCalendarDate = date => {
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .click();
+
+ cy.get(".ant-calendar-date-panel")
+ .contains(".ant-calendar-date", date)
+ .click();
+ };
+
beforeEach(() => {
const queryData = {
name: "Date Parameter",
@@ -326,29 +274,31 @@ describe("Parameter", () => {
cy.wrap(now.getTime()).as("now");
cy.clock(now.getTime(), ["Date"]);
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
});
afterEach(() => {
- cy.clock().then((clock) => clock.restore());
+ cy.clock().then(clock => clock.restore());
});
- it("updates the results after selecting a date", function () {
+ it("updates the results after selecting a date", function() {
selectCalendarDate("15");
cy.getByTestId("ParameterApplyButton").click();
- cy.getByTestId("TableVisualization").should("contain", dayjs(this.now).format("15/MM/YY"));
+ cy.getByTestId("TableVisualization").should("contain", Cypress.moment(this.now).format("15/MM/YY"));
});
- it("allows picking a dynamic date", function () {
+ it("allows picking a dynamic date", function() {
cy.getByTestId("DynamicButton").click();
- cy.getByTestId("DynamicButtonMenu").contains("Today/Now").click();
+ cy.getByTestId("DynamicButtonMenu")
+ .contains("Today/Now")
+ .click();
cy.getByTestId("ParameterApplyButton").click();
- cy.getByTestId("TableVisualization").should("contain", dayjs(this.now).format("DD/MM/YY"));
+ cy.getByTestId("TableVisualization").should("contain", Cypress.moment(this.now).format("DD/MM/YY"));
});
it("sets dirty state when edited", () => {
@@ -371,61 +321,84 @@ describe("Parameter", () => {
cy.wrap(now.getTime()).as("now");
cy.clock(now.getTime(), ["Date"]);
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
});
afterEach(() => {
- cy.clock().then((clock) => clock.restore());
+ cy.clock().then(clock => clock.restore());
});
- it("updates the results after selecting a date and clicking in ok", function () {
- cy.getByTestId("ParameterName-test-parameter").find("input").as("Input").click();
+ it("updates the results after selecting a date and clicking in ok", function() {
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .as("Input")
+ .click();
- selectCalendarDate("15");
+ cy.get(".ant-calendar-date-panel")
+ .contains(".ant-calendar-date", "15")
+ .click();
- cy.get(".ant-picker-ok button").click();
+ cy.get(".ant-calendar-ok-btn").click();
cy.getByTestId("ParameterApplyButton").click();
- cy.getByTestId("TableVisualization").should("contain", dayjs(this.now).format("YYYY-MM-15 HH:mm"));
+ cy.getByTestId("TableVisualization").should("contain", Cypress.moment(this.now).format("YYYY-MM-15 HH:mm"));
});
- it("shows the current datetime after clicking in Now", function () {
- cy.getByTestId("ParameterName-test-parameter").find("input").as("Input").click();
+ it("shows the current datetime after clicking in Now", function() {
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .as("Input")
+ .click();
- cy.get(".ant-picker-panel").contains("Now").click();
+ cy.get(".ant-calendar-date-panel")
+ .contains("Now")
+ .click();
cy.getByTestId("ParameterApplyButton").click();
- cy.getByTestId("TableVisualization").should("contain", dayjs(this.now).format("YYYY-MM-DD HH:mm"));
+ cy.getByTestId("TableVisualization").should("contain", Cypress.moment(this.now).format("YYYY-MM-DD HH:mm"));
});
- it("allows picking a dynamic date", function () {
+ it("allows picking a dynamic date", function() {
cy.getByTestId("DynamicButton").click();
- cy.getByTestId("DynamicButtonMenu").contains("Today/Now").click();
+ cy.getByTestId("DynamicButtonMenu")
+ .contains("Today/Now")
+ .click();
cy.getByTestId("ParameterApplyButton").click();
- cy.getByTestId("TableVisualization").should("contain", dayjs(this.now).format("YYYY-MM-DD HH:mm"));
+ cy.getByTestId("TableVisualization").should("contain", Cypress.moment(this.now).format("YYYY-MM-DD HH:mm"));
});
it("sets dirty state when edited", () => {
expectDirtyStateChange(() => {
- cy.getByTestId("ParameterName-test-parameter").find("input").click();
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .click();
- cy.get(".ant-picker-panel").contains("Now").click();
+ cy.get(".ant-calendar-date-panel")
+ .contains("Now")
+ .click();
});
});
});
describe("Date Range Parameter", () => {
const selectCalendarDateRange = (startDate, endDate) => {
- cy.getByTestId("ParameterName-test-parameter").find("input").first().click();
-
- cy.get(".ant-picker-panel").contains(".ant-picker-cell-inner", startDate).click();
-
- cy.get(".ant-picker-panel").contains(".ant-picker-cell-inner", endDate).click();
+ cy.getByTestId("ParameterName-test-parameter")
+ .find("input")
+ .first()
+ .click();
+
+ cy.get(".ant-calendar-date-panel")
+ .contains(".ant-calendar-date", startDate)
+ .click();
+
+ cy.get(".ant-calendar-date-panel")
+ .contains(".ant-calendar-date", endDate)
+ .click();
};
beforeEach(() => {
@@ -442,33 +415,35 @@ describe("Parameter", () => {
cy.wrap(now.getTime()).as("now");
cy.clock(now.getTime(), ["Date"]);
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
});
afterEach(() => {
- cy.clock().then((clock) => clock.restore());
+ cy.clock().then(clock => clock.restore());
});
- it("updates the results after selecting a date range", function () {
+ it("updates the results after selecting a date range", function() {
selectCalendarDateRange("15", "20");
cy.getByTestId("ParameterApplyButton").click();
- const now = dayjs(this.now);
+ const now = Cypress.moment(this.now);
cy.getByTestId("TableVisualization").should(
"contain",
now.format("YYYY-MM-15") + " - " + now.format("YYYY-MM-20")
);
});
- it("allows picking a dynamic date range", function () {
+ it("allows picking a dynamic date range", function() {
cy.getByTestId("DynamicButton").click();
- cy.getByTestId("DynamicButtonMenu").contains("Last month").click();
+ cy.getByTestId("DynamicButtonMenu")
+ .contains("Last month")
+ .click();
cy.getByTestId("ParameterApplyButton").click();
- const lastMonth = dayjs(this.now).subtract(1, "month");
+ const lastMonth = Cypress.moment(this.now).subtract(1, "month");
cy.getByTestId("TableVisualization").should(
"contain",
lastMonth.startOf("month").format("YYYY-MM-DD") + " - " + lastMonth.endOf("month").format("YYYY-MM-DD")
@@ -481,15 +456,20 @@ describe("Parameter", () => {
});
describe("Apply Changes", () => {
- const expectAppliedChanges = (apply) => {
- cy.getByTestId("ParameterName-test-parameter-1").find("input").as("Input").type("Redash");
+ const expectAppliedChanges = apply => {
+ cy.getByTestId("ParameterName-test-parameter-1")
+ .find("input")
+ .as("Input")
+ .type("Redash");
- cy.getByTestId("ParameterName-test-parameter-2").find("input").type("Redash");
+ cy.getByTestId("ParameterName-test-parameter-2")
+ .find("input")
+ .type("Redash");
cy.location("search").should("not.contain", "Redash");
cy.server();
- cy.route("POST", "**/api/queries/*/results").as("Results");
+ cy.route("POST", "api/queries/*/results").as("Results");
apply(cy.get("@Input"));
@@ -509,20 +489,18 @@ describe("Parameter", () => {
},
};
- cy.server();
- cy.route("GET", "**/api/data_sources/*/schema").as("Schema");
-
- cy.createQuery(queryData, false)
- .then(({ id }) => cy.visit(`/queries/${id}/source`))
- .then(() => cy.wait("@Schema"));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
});
it("shows and hides according to parameter dirty state", () => {
cy.getByTestId("ParameterApplyButton").should("not.be", "visible");
- cy.getByTestId("ParameterName-test-parameter-1").find("input").as("Param").type("Redash");
+ cy.getByTestId("ParameterName-test-parameter-1")
+ .find("input")
+ .as("Param")
+ .type("Redash");
- cy.getByTestId("ParameterApplyButton").should("be.visible");
+ cy.getByTestId("ParameterApplyButton").should("be", "visible");
cy.get("@Param").clear();
@@ -530,13 +508,21 @@ describe("Parameter", () => {
});
it("updates dirty counter", () => {
- cy.getByTestId("ParameterName-test-parameter-1").find("input").type("Redash");
+ cy.getByTestId("ParameterName-test-parameter-1")
+ .find("input")
+ .type("Redash");
- cy.getByTestId("ParameterApplyButton").find(".ant-badge-count p.current").should("contain", "1");
+ cy.getByTestId("ParameterApplyButton")
+ .find(".ant-badge-count p.current")
+ .should("contain", "1");
- cy.getByTestId("ParameterName-test-parameter-2").find("input").type("Redash");
+ cy.getByTestId("ParameterName-test-parameter-2")
+ .find("input")
+ .type("Redash");
- cy.getByTestId("ParameterApplyButton").find(".ant-badge-count p.current").should("contain", "2");
+ cy.getByTestId("ParameterApplyButton")
+ .find(".ant-badge-count p.current")
+ .should("contain", "2");
});
it('applies changes from "Apply Changes" button', () => {
@@ -546,13 +532,16 @@ describe("Parameter", () => {
});
it('applies changes from "alt+enter" keyboard shortcut', () => {
- expectAppliedChanges((input) => {
+ expectAppliedChanges(input => {
input.type("{alt}{enter}");
});
});
it('disables "Execute" button', () => {
- cy.getByTestId("ParameterName-test-parameter-1").find("input").as("Input").type("Redash");
+ cy.getByTestId("ParameterName-test-parameter-1")
+ .find("input")
+ .as("Input")
+ .type("Redash");
cy.getByTestId("ExecuteButton").should("be.disabled");
cy.get("@Input").clear();
@@ -575,16 +564,29 @@ describe("Parameter", () => {
},
};
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
- cy.get(".parameter-block").first().invoke("width").as("paramWidth");
+ cy.get(".parameter-block")
+ .first()
+ .invoke("width")
+ .as("paramWidth");
cy.get("body").type("{alt}D"); // hide schema browser
});
- it("is possible to rearrange parameters", function () {
+ const dragParam = (paramName, offsetLeft, offsetTop) => {
+ cy.getByTestId(`DragHandle-${paramName}`)
+ .trigger("mouseover")
+ .trigger("mousedown");
+
+ cy.get(".parameter-dragged .drag-handle")
+ .trigger("mousemove", offsetLeft, offsetTop, { force: true })
+ .trigger("mouseup", { force: true });
+ };
+
+ it("is possible to rearrange parameters", function() {
cy.server();
- cy.route("POST", "**/api/queries/*").as("QuerySave");
+ cy.route("POST", "api/queries/*").as("QuerySave");
dragParam("param1", this.paramWidth, 1);
cy.wait("@QuerySave");
@@ -608,7 +610,7 @@ describe("Parameter", () => {
},
};
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}/source`));
cy.getByTestId("ParameterSettings-parameter").click();
});
diff --git a/client/cypress/integration/query/query_tags_spec.js b/client/cypress/integration/query/query_tags_spec.js
index 0c8e01099d..48aae533c7 100644
--- a/client/cypress/integration/query/query_tags_spec.js
+++ b/client/cypress/integration/query/query_tags_spec.js
@@ -1,3 +1,4 @@
+import { createQuery } from "../../support/redash-api";
import { expectTagsToContain, typeInTagsSelectAndSave } from "../../support/tags";
describe("Query Tags", () => {
@@ -9,12 +10,12 @@ describe("Query Tags", () => {
query: "SELECT 1 as value",
};
- cy.createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
+ createQuery(queryData, false).then(({ id }) => cy.visit(`/queries/${id}`));
});
it("is possible to add and edit tags", () => {
cy.server();
- cy.route("POST", "**/api/queries/*").as("QuerySave");
+ cy.route("POST", "api/queries/*").as("QuerySave");
cy.getByTestId("TagsControl").contains(".label", "Unpublished");
@@ -22,13 +23,13 @@ describe("Query Tags", () => {
.should("contain", "Add tag")
.click();
- typeInTagsSelectAndSave("tag1{enter}tag2{enter}tag3{enter}");
+ typeInTagsSelectAndSave("tag1{enter}tag2{enter}tag3{enter}{esc}");
cy.wait("@QuerySave");
expectTagsToContain(["tag1", "tag2", "tag3"]);
cy.getByTestId("EditTagsButton").click();
- typeInTagsSelectAndSave("tag4{enter}");
+ typeInTagsSelectAndSave("tag4{enter}{esc}");
cy.wait("@QuerySave");
cy.reload();
diff --git a/client/cypress/integration/settings/organization_settings_spec.js b/client/cypress/integration/settings/organization_settings_spec.js
index 8318d7028b..b8fa7bfb60 100644
--- a/client/cypress/integration/settings/organization_settings_spec.js
+++ b/client/cypress/integration/settings/organization_settings_spec.js
@@ -1,45 +1,15 @@
describe("Settings", () => {
beforeEach(() => {
cy.login();
- cy.visit("/settings/general");
+ cy.visit("/settings/organization");
});
it("renders the page and takes a screenshot", () => {
cy.getByTestId("OrganizationSettings").within(() => {
+ cy.getByTestId("DateFormatSelect").should("contain", "DD/MM/YY");
cy.getByTestId("TimeFormatSelect").should("contain", "HH:mm");
});
cy.percySnapshot("Organization Settings");
});
-
- it("can set date format setting", () => {
- cy.getByTestId("DateFormatSelect").click();
- cy.getByTestId("DateFormatSelect:YYYY-MM-DD").click();
- cy.getByTestId("OrganizationSettingsSaveButton").click();
-
- cy.createQuery({
- name: "test date format",
- query: "SELECT NOW()",
- }).then(({ id: queryId }) => {
- cy.visit(`/queries/${queryId}`);
- cy.findByText("Refresh Now").click();
-
- // "created at" field is formatted with the date format.
- cy.getByTestId("TableVisualization")
- .findAllByText(/\d{4}-\d{2}-\d{2}/)
- .should("exist");
-
- // set to a different format and expect a different result in the table
- cy.visit("/settings/general");
- cy.getByTestId("DateFormatSelect").click();
- cy.getByTestId("DateFormatSelect:MM/DD/YY").click();
- cy.getByTestId("OrganizationSettingsSaveButton").click();
-
- cy.visit(`/queries/${queryId}`);
-
- cy.getByTestId("TableVisualization")
- .findAllByText(/\d{2}\/\d{2}\/\d{2}/)
- .should("exist");
- });
- });
});
diff --git a/client/cypress/integration/settings/settings_tabs_spec.js b/client/cypress/integration/settings/settings_tabs_spec.js
index d67886b53c..5c4ace6428 100644
--- a/client/cypress/integration/settings/settings_tabs_spec.js
+++ b/client/cypress/integration/settings/settings_tabs_spec.js
@@ -1,3 +1,5 @@
+import { createUser } from "../../support/redash-api";
+
describe("Settings Tabs", () => {
const regularUser = {
name: "Example User",
@@ -6,7 +8,7 @@ describe("Settings Tabs", () => {
};
const userTabs = ["Users", "Groups", "Query Snippets", "Account"];
- const adminTabs = ["Data Sources", "Alert Destinations", "General"];
+ const adminTabs = ["Data Sources", "Alert Destinations", "Settings"];
const expectSettingsTabsToBe = expectedTabs =>
cy.getByTestId("SettingsScreenItem").then($list => {
@@ -15,7 +17,7 @@ describe("Settings Tabs", () => {
});
before(() => {
- cy.login().then(() => cy.createUser(regularUser));
+ cy.login().then(() => createUser(regularUser));
});
describe("For admin user", () => {
diff --git a/client/cypress/integration/user/create_user_spec.js b/client/cypress/integration/user/create_user_spec.js
deleted file mode 100644
index 0b2e5b60b8..0000000000
--- a/client/cypress/integration/user/create_user_spec.js
+++ /dev/null
@@ -1,28 +0,0 @@
-describe("Create User", () => {
- beforeEach(() => {
- cy.login();
- cy.visit("/users/new");
- });
-
- const fillUserFormAndSubmit = (name, email) => {
- cy.getByTestId("CreateUserDialog").within(() => {
- cy.getByTestId("Name").type(name);
- cy.getByTestId("Email").type(email);
- });
- cy.getByTestId("SaveUserButton").click();
- };
-
- it("creates a new user", () => {
- // delete existing "new-user@redash.io"
- cy.request("GET", "api/users?q=new-user")
- .then(({ body }) => body.results.filter(user => user.email === "new-user@redash.io"))
- .each(user => cy.request("DELETE", `api/users/${user.id}`));
-
- fillUserFormAndSubmit("New User", "admin@redash.io");
-
- cy.getByTestId("CreateUserErrorAlert").should("contain", "Email already taken");
-
- fillUserFormAndSubmit("{selectall}New User", "{selectall}new-user@redash.io");
- cy.contains("Saved.");
- });
-});
diff --git a/client/cypress/integration/visualizations/box_plot_spec.js b/client/cypress/integration/visualizations/box_plot_spec.js
index 84cb9f4634..49fa00c528 100644
--- a/client/cypress/integration/visualizations/box_plot_spec.js
+++ b/client/cypress/integration/visualizations/box_plot_spec.js
@@ -1,5 +1,7 @@
/* global cy, Cypress */
+import { createQuery, createVisualization } from "../../support/redash-api";
+
const SQL = `
SELECT 12 AS mn, 4967 AS mx UNION ALL
SELECT 10 AS mn, 19430 AS mx UNION ALL
@@ -40,8 +42,8 @@ describe("Box Plot", () => {
beforeEach(() => {
cy.login();
- cy.createQuery({ query: SQL })
- .then(({ id }) => cy.createVisualization(id, "BOXPLOT", "Boxplot (Deprecated)", {}))
+ createQuery({ query: SQL })
+ .then(({ id }) => createVisualization(id, "BOXPLOT", "Boxplot (Deprecated)", {}))
.then(({ id: visualizationId, query_id: queryId }) => {
cy.visit(`queries/${queryId}/source#${visualizationId}`);
cy.getByTestId("ExecuteButton").click();
diff --git a/client/cypress/integration/visualizations/chart_spec.js b/client/cypress/integration/visualizations/chart_spec.js
deleted file mode 100644
index cb3795fdfc..0000000000
--- a/client/cypress/integration/visualizations/chart_spec.js
+++ /dev/null
@@ -1,140 +0,0 @@
-/* global cy */
-
-import { getWidgetTestId } from "../../support/dashboard";
-import {
- assertAxesAndAddLabels,
- assertPlotPreview,
- assertTabbedEditor,
- createChartThroughUI,
- createDashboardWithCharts,
-} from "../../support/visualizations/chart";
-
-const SQL = `
- SELECT 'a' AS stage, 11 AS value1, 22 AS value2 UNION ALL
- SELECT 'a' AS stage, 12 AS value1, 41 AS value2 UNION ALL
- SELECT 'a' AS stage, 45 AS value1, 93 AS value2 UNION ALL
- SELECT 'a' AS stage, 54 AS value1, 79 AS value2 UNION ALL
- SELECT 'b' AS stage, 33 AS value1, 65 AS value2 UNION ALL
- SELECT 'b' AS stage, 73 AS value1, 50 AS value2 UNION ALL
- SELECT 'b' AS stage, 90 AS value1, 40 AS value2 UNION ALL
- SELECT 'c' AS stage, 19 AS value1, 33 AS value2 UNION ALL
- SELECT 'c' AS stage, 92 AS value1, 14 AS value2 UNION ALL
- SELECT 'c' AS stage, 63 AS value1, 65 AS value2 UNION ALL
- SELECT 'c' AS stage, 44 AS value1, 27 AS value2\
-`;
-
-describe("Chart", () => {
- beforeEach(() => {
- cy.login();
- cy.createQuery({ name: "Chart Visualization", query: SQL }).its("id").as("queryId");
- });
-
- it("creates Bar charts", function () {
- cy.visit(`queries/${this.queryId}/source`);
- cy.getByTestId("ExecuteButton").click();
-
- const getBarChartAssertionFunction =
- (specificBarChartAssertionFn = () => {}) =>
- () => {
- // checks for TabbedEditor standard tabs
- assertTabbedEditor();
-
- // standard chart should be bar
- cy.getByTestId("Chart.GlobalSeriesType").contains(".ant-select-selection-item", "Bar");
-
- // checks the plot canvas exists and is empty
- assertPlotPreview("not.exist");
-
- // creates a chart and checks it is plotted
- cy.getByTestId("Chart.ColumnMapping.x").selectAntdOption("Chart.ColumnMapping.x.stage");
- cy.getByTestId("Chart.ColumnMapping.y").selectAntdOption("Chart.ColumnMapping.y.value1");
- cy.getByTestId("Chart.ColumnMapping.y").selectAntdOption("Chart.ColumnMapping.y.value2");
- assertPlotPreview("exist");
-
- specificBarChartAssertionFn();
- };
-
- const chartTests = [
- {
- name: "Basic Bar Chart",
- alias: "basicBarChart",
- assertionFn: () => {
- assertAxesAndAddLabels("Stage", "Value");
- },
- },
- {
- name: "Horizontal Bar Chart",
- alias: "horizontalBarChart",
- assertionFn: () => {
- cy.getByTestId("Chart.SwappedAxes").check();
- cy.getByTestId("VisualizationEditor.Tabs.XAxis").should("have.text", "Y Axis");
- cy.getByTestId("VisualizationEditor.Tabs.YAxis").should("have.text", "X Axis");
- },
- },
- {
- name: "Stacked Bar Chart",
- alias: "stackedBarChart",
- assertionFn: () => {
- cy.getByTestId("Chart.Stacking").selectAntdOption("Chart.Stacking.Stack");
- },
- },
- {
- name: "Normalized Bar Chart",
- alias: "normalizedBarChart",
- assertionFn: () => {
- cy.getByTestId("Chart.NormalizeValues").check();
- },
- },
- ];
-
- chartTests.forEach(({ name, alias, assertionFn }) => {
- createChartThroughUI(name, getBarChartAssertionFunction(assertionFn)).as(alias);
- });
-
- const chartGetters = chartTests.map(({ alias }) => alias);
-
- const withDashboardWidgetsAssertionFn = (widgetGetters, dashboardUrl) => {
- cy.visit(dashboardUrl);
- widgetGetters.forEach((widgetGetter) => {
- cy.get(`@${widgetGetter}`).then((widget) => {
- cy.getByTestId(getWidgetTestId(widget)).within(() => {
- cy.get("g.points").should("exist");
- });
- });
- });
- };
-
- createDashboardWithCharts("Bar chart visualizations", chartGetters, withDashboardWidgetsAssertionFn);
- cy.percySnapshot("Visualizations - Charts - Bar");
- });
- it("colors Bar charts", function () {
- cy.visit(`queries/${this.queryId}/source`);
- cy.getByTestId("ExecuteButton").click();
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("Chart.ColumnMapping.x").selectAntdOption("Chart.ColumnMapping.x.stage");
- cy.getByTestId("Chart.ColumnMapping.y").selectAntdOption("Chart.ColumnMapping.y.value1");
- cy.getByTestId("VisualizationEditor.Tabs.Colors").click();
- cy.getByTestId("ColorScheme").click();
- cy.getByTestId("ColorOptionViridis").click();
- cy.getByTestId("ColorScheme").click();
- cy.getByTestId("ColorOptionTableau 10").click();
- cy.getByTestId("ColorScheme").click();
- cy.getByTestId("ColorOptionD3 Category 10").click();
- });
- it("colors Pie charts", function () {
- cy.visit(`queries/${this.queryId}/source`);
- cy.getByTestId("ExecuteButton").click();
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("Chart.GlobalSeriesType").click();
- cy.getByTestId("Chart.ChartType.pie").click();
- cy.getByTestId("Chart.ColumnMapping.x").selectAntdOption("Chart.ColumnMapping.x.stage");
- cy.getByTestId("Chart.ColumnMapping.y").selectAntdOption("Chart.ColumnMapping.y.value1");
- cy.getByTestId("VisualizationEditor.Tabs.Colors").click();
- cy.getByTestId("ColorScheme").click();
- cy.getByTestId("ColorOptionViridis").click();
- cy.getByTestId("ColorScheme").click();
- cy.getByTestId("ColorOptionTableau 10").click();
- cy.getByTestId("ColorScheme").click();
- cy.getByTestId("ColorOptionD3 Category 10").click();
- });
-});
diff --git a/client/cypress/integration/visualizations/choropleth_spec.js b/client/cypress/integration/visualizations/choropleth_spec.js
index 100f261617..d9199f8e3f 100644
--- a/client/cypress/integration/visualizations/choropleth_spec.js
+++ b/client/cypress/integration/visualizations/choropleth_spec.js
@@ -1,5 +1,7 @@
/* global cy */
+import { createQuery } from "../../support/redash-api";
+
const SQL = `
SELECT 'AR' AS "code", 'Argentina' AS "name", 37.62 AS "value" UNION ALL
SELECT 'AU' AS "code", 'Australia' AS "name", 37.62 AS "value" UNION ALL
@@ -32,23 +34,27 @@ describe("Choropleth", () => {
beforeEach(() => {
cy.login();
- cy.createQuery({ query: SQL }).then(({ id }) => {
+ createQuery({ query: SQL }).then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId("ExecuteButton").click();
});
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.CHOROPLETH");
});
it("creates visualization", () => {
+ cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.CHOROPLETH
+ `);
+
cy.clickThrough(`
VisualizationEditor.Tabs.General
Choropleth.Editor.MapType
- Choropleth.Editor.MapType.countries
+ Choropleth.Editor.MapType.Countries
Choropleth.Editor.KeyColumn
Choropleth.Editor.KeyColumn.name
- Choropleth.Editor.TargetField
- Choropleth.Editor.TargetField.name
+ Choropleth.Editor.KeyType
+ Choropleth.Editor.KeyType.name
Choropleth.Editor.ValueColumn
Choropleth.Editor.ValueColumn.value
`);
diff --git a/client/cypress/integration/visualizations/cohort_spec.js b/client/cypress/integration/visualizations/cohort_spec.js
index e889157ea2..cb42026dca 100644
--- a/client/cypress/integration/visualizations/cohort_spec.js
+++ b/client/cypress/integration/visualizations/cohort_spec.js
@@ -1,5 +1,7 @@
/* global cy, Cypress */
+import { createQuery } from "../../support/redash-api";
+
const SQL = `
SELECT '2019-01-01' AS "date", 21 AS "bucket", 5 AS "value", 1 AS "stage" UNION ALL
SELECT '2019-01-01' AS "date", 21 AS "bucket", 8 AS "value", 2 AS "stage" UNION ALL
@@ -22,15 +24,19 @@ describe("Cohort", () => {
beforeEach(() => {
cy.login();
- cy.createQuery({ query: SQL }).then(({ id }) => {
+ createQuery({ query: SQL }).then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId("ExecuteButton").click();
});
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.COHORT");
});
it("creates visualization", () => {
+ cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COHORT
+ `);
+
cy.clickThrough(`
VisualizationEditor.Tabs.Options
Cohort.TimeInterval
diff --git a/client/cypress/integration/visualizations/counter_spec.js b/client/cypress/integration/visualizations/counter_spec.js
index 6ff573fb5c..b52f8454f4 100644
--- a/client/cypress/integration/visualizations/counter_spec.js
+++ b/client/cypress/integration/visualizations/counter_spec.js
@@ -1,5 +1,7 @@
/* global cy, Cypress */
+import { createQuery } from "../../support/redash-api";
+
const SQL = `
SELECT 27182.8182846 AS a, 20000 AS b, 'lorem' AS c UNION ALL
SELECT 31415.9265359 AS a, 40000 AS b, 'ipsum' AS c
@@ -10,16 +12,18 @@ describe("Counter", () => {
beforeEach(() => {
cy.login();
- cy.createQuery({ query: SQL }).then(({ id }) => {
+ createQuery({ query: SQL }).then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId("ExecuteButton").click();
});
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.COUNTER");
});
it("creates simple Counter", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.a
`);
@@ -35,6 +39,10 @@ describe("Counter", () => {
it("creates Counter with custom label", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.a
`);
@@ -54,6 +62,10 @@ describe("Counter", () => {
it("creates Counter with non-numeric value", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.c
@@ -76,6 +88,10 @@ describe("Counter", () => {
it("creates Counter with target value (trend positive)", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.a
@@ -94,6 +110,10 @@ describe("Counter", () => {
it("creates Counter with custom row number (trend negative)", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.a
@@ -117,6 +137,10 @@ describe("Counter", () => {
it("creates Counter with count rows", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.a
@@ -134,6 +158,10 @@ describe("Counter", () => {
it("creates Counter with formatting", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.a
@@ -162,6 +190,10 @@ describe("Counter", () => {
it("creates Counter with target value formatting", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.COUNTER
+
Counter.General.ValueColumn
Counter.General.ValueColumn.a
diff --git a/client/cypress/integration/visualizations/edit_visualization_dialog_spec.js b/client/cypress/integration/visualizations/edit_visualization_dialog_spec.js
index 494bb22290..e54b53581b 100644
--- a/client/cypress/integration/visualizations/edit_visualization_dialog_spec.js
+++ b/client/cypress/integration/visualizations/edit_visualization_dialog_spec.js
@@ -1,9 +1,11 @@
/* global cy */
+import { createQuery } from "../../support/redash-api";
+
describe("Edit visualization dialog", () => {
beforeEach(() => {
cy.login();
- cy.createQuery().then(({ id }) => {
+ createQuery().then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId("ExecuteButton").click();
});
diff --git a/client/cypress/integration/visualizations/funnel_spec.js b/client/cypress/integration/visualizations/funnel_spec.js
index 85b3419fed..4ccab3ecf2 100644
--- a/client/cypress/integration/visualizations/funnel_spec.js
+++ b/client/cypress/integration/visualizations/funnel_spec.js
@@ -1,5 +1,7 @@
/* global cy, Cypress */
+import { createQuery } from "../../support/redash-api";
+
const SQL = `
SELECT 'a.01' AS a, 1.758831600227 AS b UNION ALL
SELECT 'a.02' AS a, 613.4456936572 AS b UNION ALL
@@ -23,7 +25,7 @@ describe("Funnel", () => {
beforeEach(() => {
cy.login();
- cy.createQuery({ query: SQL }).then(({ id }) => {
+ createQuery({ query: SQL }).then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId("ExecuteButton").click();
});
@@ -32,8 +34,10 @@ describe("Funnel", () => {
it("creates visualization", () => {
cy.clickThrough(`
NewVisualization
+ VisualizationType
+ VisualizationType.FUNNEL
`);
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.FUNNEL");
+
cy.clickThrough(`
VisualizationEditor.Tabs.General
diff --git a/client/cypress/integration/visualizations/map_spec.js b/client/cypress/integration/visualizations/map_spec.js
index 223540752a..693a6eb910 100644
--- a/client/cypress/integration/visualizations/map_spec.js
+++ b/client/cypress/integration/visualizations/map_spec.js
@@ -1,5 +1,7 @@
/* global cy */
+import { createQuery, createVisualization } from "../../support/redash-api";
+
const SQL = `
SELECT 'Israel' AS country, 32.0808800 AS lat, 34.7805700 AS lng UNION ALL
SELECT 'Israel' AS country, 31.7690400 AS lat, 35.2163300 AS lng UNION ALL
@@ -20,8 +22,8 @@ describe("Map (Markers)", () => {
const mapTileUrl = "/static/images/fixtures/map-tile.png";
- cy.createQuery({ query: SQL })
- .then(({ id }) => cy.createVisualization(id, "MAP", "Map (Markers)", { mapTileUrl }))
+ createQuery({ query: SQL })
+ .then(({ id }) => createVisualization(id, "MAP", "Map (Markers)", { mapTileUrl }))
.then(({ id: visualizationId, query_id: queryId }) => {
cy.visit(`queries/${queryId}/source#${visualizationId}`);
cy.getByTestId("ExecuteButton").click();
diff --git a/client/cypress/integration/visualizations/pivot_spec.js b/client/cypress/integration/visualizations/pivot_spec.js
index ad10bc2a16..54d4cf53ab 100644
--- a/client/cypress/integration/visualizations/pivot_spec.js
+++ b/client/cypress/integration/visualizations/pivot_spec.js
@@ -1,7 +1,10 @@
-/* global cy */
+/* global cy, Cypress */
+import { createQuery, createVisualization, createDashboard, addWidget } from "../../support/redash-api";
import { getWidgetTestId } from "../../support/dashboard";
+const { get } = Cypress._;
+
const SQL = `
SELECT 'a' AS stage1, 'a1' AS stage2, 11 AS value UNION ALL
SELECT 'a' AS stage1, 'a2' AS stage2, 12 AS value UNION ALL
@@ -18,7 +21,8 @@ const SQL = `
function createPivotThroughUI(visualizationName, options = {}) {
cy.getByTestId("NewVisualization").click();
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.PIVOT");
+ cy.getByTestId("VisualizationType").click();
+ cy.getByTestId("VisualizationType.PIVOT").click();
cy.getByTestId("VisualizationName")
.clear()
.type(visualizationName);
@@ -40,7 +44,7 @@ function createPivotThroughUI(visualizationName, options = {}) {
describe("Pivot", () => {
beforeEach(() => {
cy.login();
- cy.createQuery({ name: "Pivot Visualization", query: SQL })
+ createQuery({ name: "Pivot Visualization", query: SQL })
.its("id")
.as("queryId");
});
@@ -64,16 +68,18 @@ describe("Pivot", () => {
const visualizationName = "Pivot";
cy.server();
- cy.route("POST", "**/api/visualizations").as("SaveVisualization");
+ cy.route("POST", "api/visualizations").as("SaveVisualization");
createPivotThroughUI(visualizationName, { hideControls: true });
- cy.wait("@SaveVisualization");
- // Added visualization should also have hidden controls
- cy.getByTestId("PivotTableVisualization")
- .find("table")
- .find(".pvtAxisContainer, .pvtRenderer, .pvtVals")
- .should("be.not.visible");
+ cy.wait("@SaveVisualization").then(xhr => {
+ const visualizationId = get(xhr, "response.body.id");
+ // Added visualization should also have hidden controls
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
+ .find("table")
+ .find(".pvtAxisContainer, .pvtRenderer, .pvtVals")
+ .should("be.not.visible");
+ });
});
it("updates the visualization when results change", function() {
@@ -86,12 +92,12 @@ describe("Pivot", () => {
vals: ["value"],
};
- cy.createVisualization(this.queryId, "PIVOT", "Pivot", options).then(visualization => {
+ createVisualization(this.queryId, "PIVOT", "Pivot", options).then(visualization => {
cy.visit(`queries/${this.queryId}/source#${visualization.id}`);
cy.getByTestId("ExecuteButton").click();
// assert number of rows is 11
- cy.getByTestId("PivotTableVisualization").contains(".pvtGrandTotal", "11");
+ cy.getByTestId(`QueryPageVisualization${visualization.id}`).contains(".pvtGrandTotal", "11");
cy.getByTestId("QueryEditor")
.get(".ace_text-input")
@@ -99,17 +105,11 @@ describe("Pivot", () => {
.focus()
.type(" UNION ALL {enter}SELECT 'c' AS stage1, 'c5' AS stage2, 55 AS value");
- // wait for the query text change to propagate (it's debounced in QuerySource.jsx)
- // eslint-disable-next-line cypress/no-unnecessary-waiting
- cy.wait(200);
-
cy.getByTestId("SaveButton").click();
- cy.getByTestId("ExecuteButton")
- .should("be.enabled")
- .click();
+ cy.getByTestId("ExecuteButton").click();
// assert number of rows is 12
- cy.getByTestId("PivotTableVisualization").contains(".pvtGrandTotal", "12");
+ cy.getByTestId(`QueryPageVisualization${visualization.id}`).contains(".pvtGrandTotal", "12");
});
});
@@ -141,14 +141,14 @@ describe("Pivot", () => {
},
];
- cy.createDashboard("Pivot Visualization")
+ createDashboard("Pivot Visualization")
.then(dashboard => {
- this.dashboardUrl = `/dashboards/${dashboard.id}`;
+ this.dashboardUrl = `/dashboard/${dashboard.slug}`;
return cy.all(
pivotTables.map(pivot => () =>
- cy
- .createVisualization(this.queryId, "PIVOT", pivot.name, pivot.options)
- .then(visualization => cy.addWidget(dashboard.id, visualization.id, { position: pivot.position }))
+ createVisualization(this.queryId, "PIVOT", pivot.name, pivot.options).then(visualization =>
+ addWidget(dashboard.id, visualization.id, { position: pivot.position })
+ )
)
);
})
diff --git a/client/cypress/integration/visualizations/sankey_sunburst_spec.js b/client/cypress/integration/visualizations/sankey_sunburst_spec.js
index 65de076730..5bb8eda9e9 100644
--- a/client/cypress/integration/visualizations/sankey_sunburst_spec.js
+++ b/client/cypress/integration/visualizations/sankey_sunburst_spec.js
@@ -1,5 +1,6 @@
/* global cy */
+import { createQuery, createDashboard, createVisualization, addWidget } from "../../support/redash-api";
import { getWidgetTestId } from "../../support/dashboard";
const SQL = `
@@ -23,17 +24,18 @@ describe("Sankey and Sunburst", () => {
describe("Creation through UI", () => {
beforeEach(() => {
- cy.createQuery({ query: SQL }).then(({ id }) => {
+ createQuery({ query: SQL }).then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId("ExecuteButton").click();
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.SUNBURST_SEQUENCE");
});
});
it("creates Sunburst", () => {
const visualizationName = "Sunburst";
+ cy.getByTestId("NewVisualization").click();
+ cy.getByTestId("VisualizationType").click();
+ cy.getByTestId("VisualizationType.SUNBURST_SEQUENCE").click();
cy.getByTestId("VisualizationName")
.clear()
.type(visualizationName);
@@ -52,6 +54,9 @@ describe("Sankey and Sunburst", () => {
it("creates Sankey", () => {
const visualizationName = "Sankey";
+ cy.getByTestId("NewVisualization").click();
+ cy.getByTestId("VisualizationType").click();
+ cy.getByTestId("VisualizationType.SANKEY").click();
cy.getByTestId("VisualizationName")
.clear()
.type(visualizationName);
@@ -93,15 +98,14 @@ describe("Sankey and Sunburst", () => {
];
it("takes a snapshot with Sunburst (1 - 5 stages)", function() {
- cy.createDashboard("Sunburst Visualization").then(dashboard => {
- this.dashboardUrl = `/dashboards/${dashboard.id}`;
+ createDashboard("Sunburst Visualization").then(dashboard => {
+ this.dashboardUrl = `/dashboard/${dashboard.slug}`;
return cy
.all(
STAGES_WIDGETS.map(sunburst => () =>
- cy
- .createQuery({ name: `Sunburst with ${sunburst.name}`, query: sunburst.query })
- .then(queryData => cy.createVisualization(queryData.id, "SUNBURST_SEQUENCE", "Sunburst", {}))
- .then(visualization => cy.addWidget(dashboard.id, visualization.id, { position: sunburst.position }))
+ createQuery({ name: `Sunburst with ${sunburst.name}`, query: sunburst.query })
+ .then(queryData => createVisualization(queryData.id, "SUNBURST_SEQUENCE", "Sunburst", {}))
+ .then(visualization => addWidget(dashboard.id, visualization.id, { position: sunburst.position }))
)
)
.then(widgets => {
@@ -118,15 +122,14 @@ describe("Sankey and Sunburst", () => {
});
it("takes a snapshot with Sankey (1 - 5 stages)", function() {
- cy.createDashboard("Sankey Visualization").then(dashboard => {
- this.dashboardUrl = `/dashboards/${dashboard.id}`;
+ createDashboard("Sankey Visualization").then(dashboard => {
+ this.dashboardUrl = `/dashboard/${dashboard.slug}`;
return cy
.all(
STAGES_WIDGETS.map(sankey => () =>
- cy
- .createQuery({ name: `Sankey with ${sankey.name}`, query: sankey.query })
- .then(queryData => cy.createVisualization(queryData.id, "SANKEY", "Sankey", {}))
- .then(visualization => cy.addWidget(dashboard.id, visualization.id, { position: sankey.position }))
+ createQuery({ name: `Sankey with ${sankey.name}`, query: sankey.query })
+ .then(queryData => createVisualization(queryData.id, "SANKEY", "Sankey", {}))
+ .then(visualization => addWidget(dashboard.id, visualization.id, { position: sankey.position }))
)
)
.then(widgets => {
diff --git a/client/cypress/integration/visualizations/table/table_spec.js b/client/cypress/integration/visualizations/table/table_spec.js
index 3191bc82b3..0104c2ee0e 100644
--- a/client/cypress/integration/visualizations/table/table_spec.js
+++ b/client/cypress/integration/visualizations/table/table_spec.js
@@ -4,15 +4,15 @@
This test suite relies on Percy (does not validate rendered visualizations)
*/
+import { createQuery, createVisualization } from "../../../support/redash-api";
import * as AllCellTypes from "./.mocks/all-cell-types";
import * as MultiColumnSort from "./.mocks/multi-column-sort";
import * as SearchInData from "./.mocks/search-in-data";
import * as LargeDataset from "./.mocks/large-dataset";
function prepareVisualization(query, type, name, options) {
- return cy
- .createQuery({ query })
- .then(({ id }) => cy.createVisualization(id, type, name, options))
+ return createQuery({ query })
+ .then(({ id }) => createVisualization(id, type, name, options))
.then(({ id: visualizationId, query_id: queryId }) => {
// use data-only view because we don't need editor features, but it will
// free more space for visualizations. Also, we'll hide schema browser (via shortcut)
@@ -22,7 +22,10 @@ function prepareVisualization(query, type, name, options) {
cy.get("body").type("{alt}D");
// do some pre-checks here to ensure that visualization was created and is visible
- cy.getByTestId("TableVisualization").should("exist").find("table").should("exist");
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
+ .should("exist")
+ .find("table")
+ .should("exist");
return cy.then(() => ({ queryId, visualizationId }));
});
@@ -38,19 +41,20 @@ describe("Table", () => {
it("renders all cell types", () => {
const { query, config } = AllCellTypes;
prepareVisualization(query, "TABLE", "All cell types", config).then(() => {
- // eslint-disable-next-line cypress/no-unnecessary-waiting
- cy.wait(500); // add some waiting to avoid an async update error from .jvi-toggle
-
// expand JSON cell
- cy.get(".jvi-item.jvi-root .jvi-toggle").click();
- cy.get(".jvi-item.jvi-root .jvi-item .jvi-toggle").click({ multiple: true });
+ cy.get(".jvi-item.jvi-root .jvi-toggle")
+ .should("exist")
+ .click();
+ cy.get(".jvi-item.jvi-root .jvi-item .jvi-toggle")
+ .should("exist")
+ .click({ multiple: true });
cy.percySnapshot("Visualizations - Table (All cell types)", { widths: [viewportWidth] });
});
});
describe("Sorting data", () => {
- beforeEach(function () {
+ beforeEach(function() {
const { query, config } = MultiColumnSort;
prepareVisualization(query, "TABLE", "Sort data", config).then(({ queryId, visualizationId }) => {
this.queryId = queryId;
@@ -58,22 +62,45 @@ describe("Table", () => {
});
});
- it("sorts data by a single column", function () {
- cy.getByTestId("TableVisualization").find("table th").contains("c").should("exist").click();
+ it("sorts data by a single column", function() {
+ const { visualizationId } = this;
+
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
+ .find("table th")
+ .contains("c")
+ .should("exist")
+ .click();
cy.percySnapshot("Visualizations - Table (Single-column sort)", { widths: [viewportWidth] });
});
- it("sorts data by a multiple columns", function () {
- cy.getByTestId("TableVisualization").find("table th").contains("a").should("exist").click();
+ it("sorts data by a multiple columns", function() {
+ const { visualizationId } = this;
+
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
+ .find("table th")
+ .contains("a")
+ .should("exist")
+ .click();
cy.get("body").type("{shift}", { release: false });
- cy.getByTestId("TableVisualization").find("table th").contains("b").should("exist").click();
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
+ .find("table th")
+ .contains("b")
+ .should("exist")
+ .click();
cy.percySnapshot("Visualizations - Table (Multi-column sort)", { widths: [viewportWidth] });
});
- it("sorts data in reverse order", function () {
- cy.getByTestId("TableVisualization").find("table th").contains("c").should("exist").click().click();
+ it("sorts data in reverse order", function() {
+ const { visualizationId } = this;
+
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
+ .find("table th")
+ .contains("c")
+ .should("exist")
+ .click()
+ .click();
cy.percySnapshot("Visualizations - Table (Single-column reverse sort)", { widths: [viewportWidth] });
});
});
@@ -81,7 +108,10 @@ describe("Table", () => {
it("searches in multiple columns", () => {
const { query, config } = SearchInData;
prepareVisualization(query, "TABLE", "Search", config).then(({ visualizationId }) => {
- cy.getByTestId("TableVisualization").find("table input").should("exist").type("test");
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
+ .find("table input")
+ .should("exist")
+ .type("test");
cy.percySnapshot("Visualizations - Table (Search in data)", { widths: [viewportWidth] });
});
});
@@ -89,7 +119,7 @@ describe("Table", () => {
it("shows pagination and navigates to third page", () => {
const { query, config } = LargeDataset;
prepareVisualization(query, "TABLE", "With pagination", config).then(({ visualizationId }) => {
- cy.get(".visualization-renderer")
+ cy.getByTestId(`QueryPageVisualization${visualizationId}`)
.find(".ant-table-pagination")
.should("exist")
.find("li")
diff --git a/client/cypress/integration/visualizations/word_cloud_spec.js b/client/cypress/integration/visualizations/word_cloud_spec.js
index bd9b5148d1..78663f1738 100644
--- a/client/cypress/integration/visualizations/word_cloud_spec.js
+++ b/client/cypress/integration/visualizations/word_cloud_spec.js
@@ -1,5 +1,7 @@
/* global cy, Cypress */
+import { createQuery } from "../../support/redash-api";
+
const { map } = Cypress._;
const SQL = `
@@ -62,17 +64,19 @@ describe("Word Cloud", () => {
beforeEach(() => {
cy.login();
- cy.createQuery({ query: SQL }).then(({ id }) => {
+ createQuery({ query: SQL }).then(({ id }) => {
cy.visit(`queries/${id}/source`);
cy.getByTestId("ExecuteButton").click();
});
cy.document().then(injectFont);
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.WORD_CLOUD");
});
it("creates visualization with automatic word frequencies", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.WORD_CLOUD
+
WordCloud.WordsColumn
WordCloud.WordsColumn.a
`);
@@ -89,6 +93,10 @@ describe("Word Cloud", () => {
it("creates visualization with word frequencies from another column", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.WORD_CLOUD
+
WordCloud.WordsColumn
WordCloud.WordsColumn.b
@@ -108,6 +116,10 @@ describe("Word Cloud", () => {
it("creates visualization with word length and frequencies limits", () => {
cy.clickThrough(`
+ NewVisualization
+ VisualizationType
+ VisualizationType.WORD_CLOUD
+
WordCloud.WordsColumn
WordCloud.WordsColumn.b
diff --git a/client/cypress/plugins/index.js b/client/cypress/plugins/index.js
new file mode 100644
index 0000000000..9dd11e4cfd
--- /dev/null
+++ b/client/cypress/plugins/index.js
@@ -0,0 +1,5 @@
+const percyHealthCheck = require("@percy/cypress/task"); // eslint-disable-line import/no-extraneous-dependencies, import/no-unresolved
+
+module.exports = on => {
+ on("task", percyHealthCheck);
+};
diff --git a/client/cypress/support/commands.js b/client/cypress/support/commands.js
index 675b58f033..1ac5aef0ec 100644
--- a/client/cypress/support/commands.js
+++ b/client/cypress/support/commands.js
@@ -2,40 +2,19 @@
import "@percy/cypress"; // eslint-disable-line import/no-extraneous-dependencies, import/no-unresolved
-import "@testing-library/cypress/add-commands";
-
const { each } = Cypress._;
-Cypress.Commands.add("login", (email = "admin@redash.io", password = "password") => {
- let csrf;
- cy.visit("/login");
- cy.getCookie("csrf_token")
- .then(cookie => {
- if (cookie) {
- csrf = cookie.value;
- } else {
- cy.visit("/login").then(() => {
- cy.get('input[name="csrf_token"]')
- .invoke("val")
- .then(csrf_token => {
- csrf = csrf_token;
- });
- });
- }
- })
- .then(() => {
- cy.request({
- url: "/login",
- method: "POST",
- form: true,
- body: {
- email,
- password,
- csrf_token: csrf,
- },
- });
- });
-});
+Cypress.Commands.add("login", (email = "admin@redash.io", password = "password") =>
+ cy.request({
+ url: "/login",
+ method: "POST",
+ form: true,
+ body: {
+ email,
+ password,
+ },
+ })
+);
Cypress.Commands.add("logout", () => cy.visit("/logout"));
Cypress.Commands.add("getByTestId", element => cy.get('[data-test="' + element + '"]'));
@@ -71,14 +50,6 @@ Cypress.Commands.add("clickThrough", (...args) => {
return undefined;
});
-/**
- * Selects ANTD selector option
- */
-Cypress.Commands.add("selectAntdOption", { prevSubject: "element" }, (subject, testId) => {
- cy.wrap(subject).click();
- return cy.getByTestId(testId).click({ force: true });
-});
-
Cypress.Commands.add("fillInputs", (elements, { wait = 0 } = {}) => {
each(elements, (value, testId) => {
cy.getByTestId(testId)
diff --git a/client/cypress/support/dashboard/index.js b/client/cypress/support/dashboard/index.js
index 1f92bc249b..238949bcef 100644
--- a/client/cypress/support/dashboard/index.js
+++ b/client/cypress/support/dashboard/index.js
@@ -1,5 +1,7 @@
/* global cy */
+import { createQuery, addWidget } from "../redash-api";
+
const { get } = Cypress._;
const RESIZE_HANDLE_SELECTOR = ".react-resizable-handle";
@@ -8,12 +10,11 @@ export function getWidgetTestId(widget) {
}
export function createQueryAndAddWidget(dashboardId, queryData = {}, widgetOptions = {}) {
- return cy
- .createQuery(queryData)
+ return createQuery(queryData)
.then(query => {
const visualizationId = get(query, "visualizations.0.id");
assert.isDefined(visualizationId, "Query api call returns at least one visualization with id");
- return cy.addWidget(dashboardId, visualizationId, widgetOptions);
+ return addWidget(dashboardId, visualizationId, widgetOptions);
})
.then(getWidgetTestId);
}
diff --git a/client/cypress/support/index.js b/client/cypress/support/index.js
index ca2180ebf1..f887c29aea 100644
--- a/client/cypress/support/index.js
+++ b/client/cypress/support/index.js
@@ -1,14 +1 @@
-/* global Cypress */
-
-import "@cypress/code-coverage/support";
import "./commands";
-import "./redash-api/index.js";
-
-Cypress.env("dataSourceId", 1);
-
-Cypress.on("uncaught:exception", err => {
- // Prevent ResizeObserver error from failing tests
- if (err && Cypress._.includes(err.message, "ResizeObserver loop limit exceeded")) {
- return false;
- }
-});
diff --git a/client/cypress/support/parameters.js b/client/cypress/support/parameters.js
deleted file mode 100644
index 335cc70f7d..0000000000
--- a/client/cypress/support/parameters.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export function dragParam(paramName, offsetLeft, offsetTop) {
- cy.getByTestId(`DragHandle-${paramName}`)
- .trigger("mouseover")
- .trigger("mousedown");
-
- cy.get(".parameter-dragged .drag-handle")
- .trigger("mousemove", offsetLeft, offsetTop, { force: true })
- .trigger("mouseup", { force: true });
-}
-
-export function expectParamOrder(expectedOrder) {
- cy.get(".parameter-container label").each(($label, index) => expect($label).to.have.text(expectedOrder[index]));
-}
diff --git a/client/cypress/support/redash-api/index.js b/client/cypress/support/redash-api/index.js
index 2204f381c2..647ff620cc 100644
--- a/client/cypress/support/redash-api/index.js
+++ b/client/cypress/support/redash-api/index.js
@@ -2,21 +2,16 @@
const { extend, get, merge, find } = Cypress._;
-const post = (options) =>
- cy
- .getCookie("csrf_token")
- .then((csrf) => cy.request({ ...options, method: "POST", headers: { "X-CSRF-TOKEN": csrf.value } }));
+export function createDashboard(name) {
+ return cy.request("POST", "api/dashboards", { name }).then(({ body }) => body);
+}
-Cypress.Commands.add("createDashboard", (name) => {
- return post({ url: "api/dashboards", body: { name } }).then(({ body }) => body);
-});
-
-Cypress.Commands.add("createQuery", (data, shouldPublish = true) => {
+export function createQuery(data, shouldPublish = true) {
const merged = extend(
{
name: "Test Query",
query: "select 1",
- data_source_id: Cypress.env("dataSourceId"),
+ data_source_id: 1,
options: {
parameters: [],
},
@@ -26,25 +21,25 @@ Cypress.Commands.add("createQuery", (data, shouldPublish = true) => {
);
// eslint-disable-next-line cypress/no-assigning-return-values
- let request = post({ url: "/api/queries", body: merged }).then(({ body }) => body);
+ let request = cy.request("POST", "/api/queries", merged).then(({ body }) => body);
if (shouldPublish) {
- request = request.then((query) =>
- post({ url: `/api/queries/${query.id}`, body: { is_draft: false } }).then(() => query)
+ request = request.then(query =>
+ cy.request("POST", `/api/queries/${query.id}`, { is_draft: false }).then(() => query)
);
}
return request;
-});
+}
-Cypress.Commands.add("createVisualization", (queryId, type, name, options) => {
+export function createVisualization(queryId, type, name, options) {
const data = { query_id: queryId, type, name, options };
- return post({ url: "/api/visualizations", body: data }).then(({ body }) => ({
+ return cy.request("POST", "/api/visualizations", data).then(({ body }) => ({
query_id: queryId,
...body,
}));
-});
+}
-Cypress.Commands.add("addTextbox", (dashboardId, text = "text", options = {}) => {
+export function addTextbox(dashboardId, text = "text", options = {}) {
const defaultOptions = {
position: { col: 0, row: 0, sizeX: 3, sizeY: 3 },
};
@@ -57,14 +52,14 @@ Cypress.Commands.add("addTextbox", (dashboardId, text = "text", options = {}) =>
options: merge(defaultOptions, options),
};
- return post({ url: "api/widgets", body: data }).then(({ body }) => {
+ return cy.request("POST", "api/widgets", data).then(({ body }) => {
const id = get(body, "id");
assert.isDefined(id, "Widget api call returns widget id");
return body;
});
-});
+}
-Cypress.Commands.add("addWidget", (dashboardId, visualizationId, options = {}) => {
+export function addWidget(dashboardId, visualizationId, options = {}) {
const defaultOptions = {
position: { col: 0, row: 0, sizeX: 3, sizeY: 3 },
};
@@ -76,17 +71,16 @@ Cypress.Commands.add("addWidget", (dashboardId, visualizationId, options = {}) =
options: merge(defaultOptions, options),
};
- return post({ url: "api/widgets", body: data }).then(({ body }) => {
+ return cy.request("POST", "api/widgets", data).then(({ body }) => {
const id = get(body, "id");
assert.isDefined(id, "Widget api call returns widget id");
return body;
});
-});
+}
-Cypress.Commands.add("createAlert", (queryId, options = {}, name) => {
+export function createAlert(queryId, options = {}, name) {
const defaultOptions = {
column: "?column?",
- selector: "first",
op: "greater than",
rearm: 0,
value: 1,
@@ -98,66 +92,67 @@ Cypress.Commands.add("createAlert", (queryId, options = {}, name) => {
options: merge(defaultOptions, options),
};
- return post({ url: "api/alerts", body: data }).then(({ body }) => {
+ return cy.request("POST", "api/alerts", data).then(({ body }) => {
const id = get(body, "id");
- assert.isDefined(id, "Alert api call retu ns alert id");
+ assert.isDefined(id, "Alert api call returns alert id");
return body;
});
-});
+}
-Cypress.Commands.add("createUser", ({ name, email, password }) => {
- return post({
- url: "api/users?no_invite=yes",
- body: { name, email },
- failOnStatusCode: false,
- }).then((xhr) => {
- const { status, body } = xhr;
- if (status < 200 || status > 400) {
- throw new Error(xhr);
- }
+export function createUser({ name, email, password }) {
+ return cy
+ .request({
+ method: "POST",
+ url: "api/users?no_invite=yes",
+ body: { name, email },
+ failOnStatusCode: false,
+ })
+ .then(xhr => {
+ const { status, body } = xhr;
+ if (status < 200 || status > 400) {
+ throw new Error(xhr);
+ }
- if (status === 400 && body.message === "Email already taken.") {
- // all is good, do nothing
- return;
- }
+ if (status === 400 && body.message === "Email already taken.") {
+ // all is good, do nothing
+ return;
+ }
- const id = get(body, "id");
- assert.isDefined(id, "User api call returns user id");
+ const id = get(body, "id");
+ assert.isDefined(id, "User api call returns user id");
- return post({
- url: body.invite_link,
- form: true,
- body: { password },
+ return cy.request({
+ url: body.invite_link,
+ method: "POST",
+ form: true,
+ body: { password },
+ });
});
- });
-});
+}
-Cypress.Commands.add("createDestination", (name, type, options = {}) => {
- return post({
+export function createDestination(name, type, options = {}) {
+ return cy.request({
+ method: "POST",
url: "api/destinations",
body: { name, type, options },
failOnStatusCode: false,
});
-});
+}
-Cypress.Commands.add("getDestinations", () => {
+export function getDestinations() {
return cy.request("GET", "api/destinations").then(({ body }) => body);
-});
+}
-Cypress.Commands.add("addDestinationSubscription", (alertId, destinationName) => {
- return cy
- .getDestinations()
- .then((destinations) => {
+export function addDestinationSubscription(alertId, destinationName) {
+ return getDestinations()
+ .then(destinations => {
const destination = find(destinations, { name: destinationName });
if (!destination) {
throw new Error("Destination not found");
}
- return post({
- url: `api/alerts/${alertId}/subscriptions`,
- body: {
- alert_id: alertId,
- destination_id: destination.id,
- },
+ return cy.request("POST", `api/alerts/${alertId}/subscriptions`, {
+ alert_id: alertId,
+ destination_id: destination.id,
});
})
.then(({ body }) => {
@@ -165,8 +160,4 @@ Cypress.Commands.add("addDestinationSubscription", (alertId, destinationName) =>
assert.isDefined(id, "Subscription api call returns subscription id");
return body;
});
-});
-
-Cypress.Commands.add("updateOrgSettings", (settings) => {
- return post({ url: "api/settings/organization", body: settings }).then(({ body }) => body);
-});
+}
diff --git a/client/cypress/support/tags/index.js b/client/cypress/support/tags/index.js
index cef4c429a3..25bcb97574 100644
--- a/client/cypress/support/tags/index.js
+++ b/client/cypress/support/tags/index.js
@@ -10,9 +10,8 @@ export function typeInTagsSelectAndSave(text) {
cy.getByTestId("EditTagsDialog").within(() => {
cy.get(".ant-select")
.find("input")
- .type(text, { force: true });
+ .type(text);
- cy.get(".ant-modal-header").click(); // hide dropdown options
cy.contains("OK").click();
});
}
diff --git a/client/cypress/support/visualizations/chart.js b/client/cypress/support/visualizations/chart.js
deleted file mode 100644
index f55c0093e7..0000000000
--- a/client/cypress/support/visualizations/chart.js
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Asserts the preview canvas exists, then captures the g.points element, which should be generated by plotly and asserts whether it exists
- * @param should Passed to should expression after plot points are captured
- */
-export function assertPlotPreview(should = "exist") {
- cy.getByTestId("VisualizationPreview").find("g.overplot").should("exist").find("g.points").should(should);
-}
-
-export function createChartThroughUI(chartName, chartSpecificAssertionFn = () => {}) {
- cy.getByTestId("NewVisualization").click();
- cy.getByTestId("VisualizationType").selectAntdOption("VisualizationType.CHART");
- cy.getByTestId("VisualizationName").clear().type(chartName);
-
- chartSpecificAssertionFn();
-
- cy.server();
- cy.route("POST", "**/api/visualizations").as("SaveVisualization");
-
- cy.getByTestId("EditVisualizationDialog").contains("button", "Save").click();
-
- cy.getByTestId("QueryPageVisualizationTabs").contains("span", chartName).should("exist");
-
- cy.wait("@SaveVisualization").should("have.property", "status", 200);
-
- return cy.get("@SaveVisualization").then((xhr) => {
- const { id, name, options } = xhr.response.body;
- return cy.wrap({ id, name, options });
- });
-}
-
-export function assertTabbedEditor(chartSpecificTabbedEditorAssertionFn = () => {}) {
- cy.getByTestId("Chart.GlobalSeriesType").should("exist");
-
- cy.getByTestId("VisualizationEditor.Tabs.Series").click();
- cy.getByTestId("VisualizationEditor").find("table").should("exist");
-
- cy.getByTestId("VisualizationEditor.Tabs.Colors").click();
- cy.getByTestId("VisualizationEditor").find("table").should("exist");
-
- cy.getByTestId("VisualizationEditor.Tabs.DataLabels").click();
- cy.getByTestId("VisualizationEditor").getByTestId("Chart.DataLabels.ShowDataLabels").should("exist");
-
- chartSpecificTabbedEditorAssertionFn();
-
- cy.getByTestId("VisualizationEditor.Tabs.General").click();
-}
-
-export function assertAxesAndAddLabels(xaxisLabel, yaxisLabel) {
- cy.getByTestId("VisualizationEditor.Tabs.XAxis").click();
- cy.getByTestId("Chart.XAxis.Type").contains(".ant-select-selection-item", "Auto Detect").should("exist");
-
- cy.getByTestId("Chart.XAxis.Name").clear().type(xaxisLabel);
-
- cy.getByTestId("VisualizationEditor.Tabs.YAxis").click();
- cy.getByTestId("Chart.LeftYAxis.Type").contains(".ant-select-selection-item", "Linear").should("exist");
-
- cy.getByTestId("Chart.LeftYAxis.Name").clear().type(yaxisLabel);
-
- cy.getByTestId("Chart.LeftYAxis.TickFormat").clear().type("+");
-
- cy.getByTestId("VisualizationEditor.Tabs.General").click();
-}
-
-export function createDashboardWithCharts(title, chartGetters, widgetsAssertionFn = () => {}) {
- cy.createDashboard(title).then((dashboard) => {
- const dashboardUrl = `/dashboards/${dashboard.id}`;
- const widgetGetters = chartGetters.map((chartGetter) => `${chartGetter}Widget`);
-
- chartGetters.forEach((chartGetter, i) => {
- const position = { autoHeight: false, sizeY: 8, sizeX: 3, col: (i % 2) * 3 };
- cy.get(`@${chartGetter}`)
- .then((chart) => cy.addWidget(dashboard.id, chart.id, { position }))
- .as(widgetGetters[i]);
- });
-
- widgetsAssertionFn(widgetGetters, dashboardUrl);
- });
-}
diff --git a/client/cypress/support/visualizations/table.js b/client/cypress/support/visualizations/table.js
index 42645ed270..2095b9fb88 100644
--- a/client/cypress/support/visualizations/table.js
+++ b/client/cypress/support/visualizations/table.js
@@ -1,10 +1,12 @@
export function expectTableToHaveLength(length) {
- cy.getByTestId("TableVisualization").find("tbody tr").should("have.length", length);
+ cy.getByTestId("TableVisualization")
+ .find("tbody tr")
+ .should("have.length", length);
}
export function expectFirstColumnToHaveMembers(values) {
cy.getByTestId("TableVisualization")
.find("tbody tr td:first-child")
- .then(($cell) => Cypress.$.map($cell, (item) => Cypress.$(item).text()))
- .then((firstColumnCells) => expect(firstColumnCells).to.have.members(values));
+ .then($cell => Cypress.$.map($cell, item => Cypress.$(item).text()))
+ .then(firstColumnCells => expect(firstColumnCells).to.have.members(values));
}
diff --git a/client/cypress/tsconfig.json b/client/cypress/tsconfig.json
deleted file mode 100644
index d4c0b515ba..0000000000
--- a/client/cypress/tsconfig.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "types": ["cypress", "@percy/cypress", "@testing-library/cypress"]
- },
- "include": ["./**/*.ts"]
-}
diff --git a/client/jsconfig.json b/client/jsconfig.json
new file mode 100644
index 0000000000..fe68d7ac70
--- /dev/null
+++ b/client/jsconfig.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "baseUrl": "./",
+ "paths": {
+ "@/*": ["./app/*"]
+ }
+ },
+ "exclude": ["dist"]
+}
\ No newline at end of file
diff --git a/client/tsconfig.json b/client/tsconfig.json
deleted file mode 100644
index e8f8852003..0000000000
--- a/client/tsconfig.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "compilerOptions": {
- // Target latest version of ECMAScript.
- "target": "es2019",
- // Search under node_modules for non-relative imports.
- "moduleResolution": "node",
- // Process & infer types from .js files.
- "allowJs": true,
- // Don't emit; allow Babel to transform files.
- "noEmit": true,
- // Enable strictest settings like strictNullChecks & noImplicitAny.
- "strict": true,
- // Import non-ES modules as default imports.
- "esModuleInterop": true,
- "jsx": "react",
- "allowSyntheticDefaultImports": true,
- "noUnusedLocals": true,
- "lib": ["dom", "dom.iterable", "esnext"],
- "forceConsistentCasingInFileNames": true,
- "baseUrl": "./",
- "paths": {
- "@/*": ["./app/*"]
- },
- "skipLibCheck": true
- },
- "include": ["app/**/*"],
- "exclude": ["dist"]
-}
diff --git a/codecov.yml b/codecov.yml
deleted file mode 100644
index 8742d9f869..0000000000
--- a/codecov.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-comment:
- layout: " diff, flags, files"
- behavior: default
- require_changes: false
- require_base: true
- require_head: true
diff --git a/cypress.config.js b/cypress.config.js
deleted file mode 100644
index 292af0d4cb..0000000000
--- a/cypress.config.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const { defineConfig } = require('cypress')
-
-module.exports = defineConfig({
- e2e: {
- baseUrl: 'http://localhost:5001',
- defaultCommandTimeout: 20000,
- downloadsFolder: 'client/cypress/downloads',
- fixturesFolder: 'client/cypress/fixtures',
- requestTimeout: 15000,
- screenshotsFolder: 'client/cypress/screenshots',
- specPattern: 'client/cypress/integration/',
- supportFile: 'client/cypress/support/index.js',
- video: true,
- videoUploadOnPasses: false,
- videosFolder: 'client/cypress/videos',
- viewportHeight: 1024,
- viewportWidth: 1280,
- env: {
- coverage: false
- }
- },
-})
diff --git a/cypress.json b/cypress.json
new file mode 100644
index 0000000000..aa7b1e6851
--- /dev/null
+++ b/cypress.json
@@ -0,0 +1,15 @@
+{
+ "baseUrl": "http://localhost:5000",
+ "video": true,
+ "videoUploadOnPasses": false,
+ "fixturesFolder": "client/cypress/fixtures",
+ "integrationFolder": "client/cypress/integration",
+ "pluginsFile": "client/cypress/plugins/index.js",
+ "screenshotsFolder": "client/cypress/screenshots",
+ "videosFolder": "client/cypress/videos",
+ "supportFile": "client/cypress/support/index.js",
+ "defaultCommandTimeout": 20000,
+ "requestTimeout": 15000,
+ "viewportWidth": 1280,
+ "viewportHeight": 1024
+}
diff --git a/compose.yaml b/docker-compose.yml
similarity index 80%
rename from compose.yaml
rename to docker-compose.yml
index 8badaa3fef..bf56e3af09 100644
--- a/compose.yaml
+++ b/docker-compose.yml
@@ -1,26 +1,20 @@
# This configuration file is for the **development** setup.
# For a production example please refer to getredash/setup repository on GitHub.
+version: "2"
x-redash-service: &redash-service
build:
context: .
args:
- skip_frontend_build: "true" # set to empty string to build
+ skip_frontend_build: "true"
volumes:
- .:/app
- env_file:
- - .env
x-redash-environment: &redash-environment
- REDASH_HOST: http://localhost:5001
REDASH_LOG_LEVEL: "INFO"
REDASH_REDIS_URL: "redis://redis:6379/0"
REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
REDASH_RATELIMIT_ENABLED: "false"
REDASH_MAIL_DEFAULT_SENDER: "redash@example.com"
REDASH_MAIL_SERVER: "email"
- REDASH_MAIL_PORT: 1025
- REDASH_ENFORCE_CSRF: "true"
- REDASH_GUNICORN_TIMEOUT: 60
- # Set secret keys in the .env file
services:
server:
<<: *redash-service
@@ -29,7 +23,7 @@ services:
- postgres
- redis
ports:
- - "5001:5000"
+ - "5000:5000"
- "5678:5678"
environment:
<<: *redash-environment
@@ -50,22 +44,21 @@ services:
<<: *redash-environment
PYTHONUNBUFFERED: 0
redis:
- image: redis:7-alpine
+ image: redis:3-alpine
restart: unless-stopped
postgres:
- image: postgres:18-alpine
- ports:
- - "15432:5432"
+ image: postgres:9.5-alpine
# The following turns the DB into less durable, but gains significant performance improvements for the tests run (x3
# improvement on my personal machine). We should consider moving this into a dedicated Docker Compose configuration for
# tests.
+ ports:
+ - "15432:5432"
command: "postgres -c fsync=off -c full_page_writes=off -c synchronous_commit=OFF"
restart: unless-stopped
environment:
POSTGRES_HOST_AUTH_METHOD: "trust"
email:
- image: maildev/maildev
+ image: djfarrelly/maildev
ports:
- - "1080:1080"
- - "1025:1025"
+ - "1080:80"
restart: unless-stopped
diff --git a/manage.py b/manage.py
index aa8d85c81e..3ccb9ecb51 100755
--- a/manage.py
+++ b/manage.py
@@ -5,5 +5,5 @@
from redash.cli import manager
-if __name__ == "__main__":
+if __name__ == '__main__':
manager()
diff --git a/migrations/alembic.ini b/migrations/alembic.ini
index 138c144473..cda6dc822f 100644
--- a/migrations/alembic.ini
+++ b/migrations/alembic.ini
@@ -1,6 +1,7 @@
# A generic, single database configuration.
[alembic]
+script_location = migrations
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
diff --git a/migrations/env.py b/migrations/env.py
index 1e80c143ae..4593816063 100755
--- a/migrations/env.py
+++ b/migrations/env.py
@@ -11,17 +11,16 @@
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
-logger = logging.getLogger("alembic.env")
+logger = logging.getLogger('alembic.env')
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from flask import current_app
-
-db_url_escaped = current_app.config.get("SQLALCHEMY_DATABASE_URI").replace("%", "%%")
-config.set_main_option("sqlalchemy.url", db_url_escaped)
-target_metadata = current_app.extensions["migrate"].db.metadata
+config.set_main_option('sqlalchemy.url',
+ current_app.config.get('SQLALCHEMY_DATABASE_URI'))
+target_metadata = current_app.extensions['migrate'].db.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
@@ -60,25 +59,21 @@ def run_migrations_online():
# when there are no changes to the schema
# reference: http://alembic.readthedocs.org/en/latest/cookbook.html
def process_revision_directives(context, revision, directives):
- if getattr(config.cmd_opts, "autogenerate", False):
+ if getattr(config.cmd_opts, 'autogenerate', False):
script = directives[0]
if script.upgrade_ops.is_empty():
directives[:] = []
- logger.info("No changes in schema detected.")
+ logger.info('No changes in schema detected.')
- engine = engine_from_config(
- config.get_section(config.config_ini_section),
- prefix="sqlalchemy.",
- poolclass=pool.NullPool,
- )
+ engine = engine_from_config(config.get_section(config.config_ini_section),
+ prefix='sqlalchemy.',
+ poolclass=pool.NullPool)
connection = engine.connect()
- context.configure(
- connection=connection,
- target_metadata=target_metadata,
- process_revision_directives=process_revision_directives,
- **current_app.extensions["migrate"].configure_args
- )
+ context.configure(connection=connection,
+ target_metadata=target_metadata,
+ process_revision_directives=process_revision_directives,
+ **current_app.extensions['migrate'].configure_args)
try:
with context.begin_transaction():
@@ -86,7 +81,6 @@ def process_revision_directives(context, revision, directives):
finally:
connection.close()
-
if context.is_offline_mode():
run_migrations_offline()
else:
diff --git a/migrations/versions/0ec979123ba4_.py b/migrations/versions/0ec979123ba4_.py
deleted file mode 100644
index 9f931b1c56..0000000000
--- a/migrations/versions/0ec979123ba4_.py
+++ /dev/null
@@ -1,28 +0,0 @@
-"""empty message
-
-Revision ID: 0ec979123ba4
-Revises: e5c7a4e2df4d
-Create Date: 2020-12-23 21:35:32.766354
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import JSON
-
-# revision identifiers, used by Alembic.
-revision = '0ec979123ba4'
-down_revision = 'e5c7a4e2df4d'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.add_column('dashboards', sa.Column('options', JSON(astext_type=sa.Text()), server_default='{}', nullable=False))
- # ### end Alembic commands ###
-
-
-def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- op.drop_column('dashboards', 'options')
- # ### end Alembic commands ###
diff --git a/migrations/versions/1038c2174f5d_make_case_insensitive_hash_of_query_text.py b/migrations/versions/1038c2174f5d_make_case_insensitive_hash_of_query_text.py
deleted file mode 100644
index c872a918b9..0000000000
--- a/migrations/versions/1038c2174f5d_make_case_insensitive_hash_of_query_text.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""Make case insensitive hash of query text
-
-Revision ID: 1038c2174f5d
-Revises: fd4fc850d7ea
-Create Date: 2023-07-16 23:10:12.885949
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.sql import table
-
-from redash.utils import gen_query_hash
-
-# revision identifiers, used by Alembic.
-revision = '1038c2174f5d'
-down_revision = 'fd4fc850d7ea'
-branch_labels = None
-depends_on = None
-
-
-
-def change_query_hash(conn, table, query_text_to):
- for record in conn.execute(table.select()):
- query_text = query_text_to(record.query)
- conn.execute(
- table
- .update()
- .where(table.c.id == record.id)
- .values(query_hash=gen_query_hash(query_text)))
-
-
-def upgrade():
- queries = table(
- 'queries',
- sa.Column('id', sa.Integer, primary_key=True),
- sa.Column('query', sa.Text),
- sa.Column('query_hash', sa.String(length=10)))
-
- conn = op.get_bind()
- change_query_hash(conn, queries, query_text_to=str)
-
-
-def downgrade():
- queries = table(
- 'queries',
- sa.Column('id', sa.Integer, primary_key=True),
- sa.Column('query', sa.Text),
- sa.Column('query_hash', sa.String(length=10)))
-
- conn = op.get_bind()
- change_query_hash(conn, queries, query_text_to=str.lower)
diff --git a/migrations/versions/118aa16f565b_.py b/migrations/versions/118aa16f565b_.py
new file mode 100644
index 0000000000..6265d6eddb
--- /dev/null
+++ b/migrations/versions/118aa16f565b_.py
@@ -0,0 +1,38 @@
+"""empty message
+
+Revision ID: 118aa16f565b
+Revises: cf135a57332e
+Create Date: 2019-02-05 20:16:52.182780
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "118aa16f565b"
+down_revision = "cf135a57332e"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.create_table(
+ "tablemetadata_queries_link",
+ sa.Column("table_id", sa.Integer(), nullable=False),
+ sa.Column("query_id", sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(["query_id"], ["queries.id"], ondelete="CASCADE"),
+ sa.ForeignKeyConstraint(
+ ["table_id"], ["table_metadata.id"], ondelete="CASCADE"
+ ),
+ sa.PrimaryKeyConstraint("table_id", "query_id"),
+ )
+ op.drop_column(u"table_metadata", "sample_query")
+
+
+def downgrade():
+ op.add_column(
+ u"table_metadata",
+ sa.Column("sample_query", sa.TEXT(), autoincrement=False, nullable=True),
+ )
+ op.drop_table("tablemetadata_queries_link")
diff --git a/migrations/versions/151a4c333e96_.py b/migrations/versions/151a4c333e96_.py
new file mode 100644
index 0000000000..46e3124ee4
--- /dev/null
+++ b/migrations/versions/151a4c333e96_.py
@@ -0,0 +1,24 @@
+"""empty message
+
+Revision ID: 151a4c333e96
+Revises: e5c7a4e2df4d
+Create Date: 2019-03-26 19:32:29.052222
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "151a4c333e96"
+down_revision = "e5c7a4e2df4d"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ pass
+
+
+def downgrade():
+ pass
diff --git a/migrations/versions/280daa582976_.py b/migrations/versions/280daa582976_.py
new file mode 100644
index 0000000000..983efbc862
--- /dev/null
+++ b/migrations/versions/280daa582976_.py
@@ -0,0 +1,59 @@
+"""Add column metadata and table metadata
+
+Revision ID: 280daa582976
+Revises: 151a4c333e96
+Create Date: 2019-01-24 18:23:53.040608
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "280daa582976"
+down_revision = "151a4c333e96"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.create_table(
+ "table_metadata",
+ sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
+ sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("org_id", sa.Integer(), nullable=False),
+ sa.Column("data_source_id", sa.Integer(), nullable=False),
+ sa.Column("exists", sa.Boolean(), nullable=False),
+ sa.Column("name", sa.String(length=255), nullable=False),
+ sa.Column("description", sa.String(length=4096), nullable=True),
+ sa.Column("column_metadata", sa.Boolean(), nullable=False),
+ sa.Column("sample_query", sa.Text(), nullable=True),
+ sa.ForeignKeyConstraint(
+ ["data_source_id"], ["data_sources.id"], ondelete="CASCADE"
+ ),
+ sa.ForeignKeyConstraint(["org_id"], ["organizations.id"]),
+ sa.PrimaryKeyConstraint("id"),
+ )
+ op.create_table(
+ "column_metadata",
+ sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False),
+ sa.Column("created_at", sa.DateTime(timezone=True), nullable=False),
+ sa.Column("id", sa.Integer(), nullable=False),
+ sa.Column("org_id", sa.Integer(), nullable=False),
+ sa.Column("table_id", sa.Integer(), nullable=False),
+ sa.Column("name", sa.String(length=255), nullable=False),
+ sa.Column("type", sa.String(length=255), nullable=True),
+ sa.Column("example", sa.String(length=4096), nullable=True),
+ sa.Column("exists", sa.Boolean(), nullable=False),
+ sa.ForeignKeyConstraint(
+ ["table_id"], ["table_metadata.id"], ondelete="CASCADE"
+ ),
+ sa.ForeignKeyConstraint(["org_id"], ["organizations.id"]),
+ sa.PrimaryKeyConstraint("id"),
+ )
+
+
+def downgrade():
+ op.drop_table("column_metadata")
+ op.drop_table("table_metadata")
diff --git a/migrations/versions/640888ce445d_.py b/migrations/versions/640888ce445d_.py
index 876ce8b7ba..0017a09527 100644
--- a/migrations/versions/640888ce445d_.py
+++ b/migrations/versions/640888ce445d_.py
@@ -10,7 +10,8 @@
from alembic import op
import sqlalchemy as sa
from sqlalchemy.sql import table
-from redash.models import MutableDict
+
+from redash.models import MutableDict, PseudoJSON
# revision identifiers, used by Alembic.
@@ -40,7 +41,7 @@ def upgrade():
"queries",
sa.Column(
"schedule",
- sa.Text(),
+ MutableDict.as_mutable(PseudoJSON),
nullable=False,
server_default=json.dumps({}),
),
@@ -50,7 +51,7 @@ def upgrade():
queries = table(
"queries",
sa.Column("id", sa.Integer, primary_key=True),
- sa.Column("schedule", sa.Text()),
+ sa.Column("schedule", MutableDict.as_mutable(PseudoJSON)),
sa.Column("old_schedule", sa.String(length=10)),
)
@@ -84,7 +85,7 @@ def downgrade():
"queries",
sa.Column(
"old_schedule",
- sa.Text(),
+ MutableDict.as_mutable(PseudoJSON),
nullable=False,
server_default=json.dumps({}),
),
@@ -92,8 +93,8 @@ def downgrade():
queries = table(
"queries",
- sa.Column("schedule", sa.Text()),
- sa.Column("old_schedule", sa.Text()),
+ sa.Column("schedule", MutableDict.as_mutable(PseudoJSON)),
+ sa.Column("old_schedule", MutableDict.as_mutable(PseudoJSON)),
)
op.execute(queries.update().values({"old_schedule": queries.c.schedule}))
@@ -105,7 +106,7 @@ def downgrade():
"queries",
sa.Column("id", sa.Integer, primary_key=True),
sa.Column("schedule", sa.String(length=10)),
- sa.Column("old_schedule", sa.Text()),
+ sa.Column("old_schedule", MutableDict.as_mutable(PseudoJSON)),
)
conn = op.get_bind()
diff --git a/migrations/versions/65a9c2387a07_match_column_name_length_to_bq.py b/migrations/versions/65a9c2387a07_match_column_name_length_to_bq.py
new file mode 100644
index 0000000000..d14d20ab10
--- /dev/null
+++ b/migrations/versions/65a9c2387a07_match_column_name_length_to_bq.py
@@ -0,0 +1,34 @@
+"""match column name length to bq
+
+Revision ID: 65a9c2387a07
+Revises: da6767746e76
+Create Date: 2024-01-16 15:00:11.918349
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '65a9c2387a07'
+down_revision = 'da6767746e76'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.alter_column('column_metadata', 'name',
+ existing_type=sa.VARCHAR(length=255),
+ type_=sa.String(length=300),
+ existing_nullable=False)
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.alter_column('column_metadata', 'name',
+ existing_type=sa.String(length=300),
+ type_=sa.VARCHAR(length=255),
+ existing_nullable=False)
+ # ### end Alembic commands ###
diff --git a/migrations/versions/6adb92e75691_.py b/migrations/versions/6adb92e75691_.py
new file mode 100644
index 0000000000..c2825e4867
--- /dev/null
+++ b/migrations/versions/6adb92e75691_.py
@@ -0,0 +1,27 @@
+"""Add sample_updated_at column to table_metadata
+
+Revision ID: 6adb92e75691
+Revises: 280daa582976
+Create Date: 2019-04-10 20:13:13.714589
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "6adb92e75691"
+down_revision = "280daa582976"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.add_column(
+ "table_metadata",
+ sa.Column("sample_updated_at", sa.DateTime(timezone=True), nullable=True),
+ )
+
+
+def downgrade():
+ op.drop_column("table_metadata", "sample_updated_at")
diff --git a/migrations/versions/7205816877ec_change_type_of_json_fields_from_varchar_.py b/migrations/versions/7205816877ec_change_type_of_json_fields_from_varchar_.py
deleted file mode 100644
index a907599261..0000000000
--- a/migrations/versions/7205816877ec_change_type_of_json_fields_from_varchar_.py
+++ /dev/null
@@ -1,135 +0,0 @@
-"""change type of json fields from varchar to json
-
-Revision ID: 7205816877ec
-Revises: 7ce5925f832b
-Create Date: 2024-01-03 13:55:18.885021
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import JSONB, JSON
-
-
-# revision identifiers, used by Alembic.
-revision = '7205816877ec'
-down_revision = '7ce5925f832b'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- connection = op.get_bind()
- op.alter_column('queries', 'options',
- existing_type=sa.Text(),
- type_=JSONB(astext_type=sa.Text()),
- nullable=True,
- postgresql_using='options::jsonb',
- )
- op.alter_column('queries', 'schedule',
- existing_type=sa.Text(),
- type_=JSONB(astext_type=sa.Text()),
- nullable=True,
- postgresql_using='schedule::jsonb',
- )
- op.alter_column('events', 'additional_properties',
- existing_type=sa.Text(),
- type_=JSONB(astext_type=sa.Text()),
- nullable=True,
- postgresql_using='additional_properties::jsonb',
- )
- op.alter_column('organizations', 'settings',
- existing_type=sa.Text(),
- type_=JSONB(astext_type=sa.Text()),
- nullable=True,
- postgresql_using='settings::jsonb',
- )
- op.alter_column('alerts', 'options',
- existing_type=JSON(astext_type=sa.Text()),
- type_=JSONB(astext_type=sa.Text()),
- nullable=True,
- postgresql_using='options::jsonb',
- )
- op.alter_column('dashboards', 'options',
- existing_type=JSON(astext_type=sa.Text()),
- type_=JSONB(astext_type=sa.Text()),
- postgresql_using='options::jsonb',
- )
- op.alter_column('dashboards', 'layout',
- existing_type=sa.Text(),
- type_=JSONB(astext_type=sa.Text()),
- postgresql_using='layout::jsonb',
- )
- op.alter_column('changes', 'change',
- existing_type=JSON(astext_type=sa.Text()),
- type_=JSONB(astext_type=sa.Text()),
- postgresql_using='change::jsonb',
- )
- op.alter_column('visualizations', 'options',
- existing_type=sa.Text(),
- type_=JSONB(astext_type=sa.Text()),
- postgresql_using='options::jsonb',
- )
- op.alter_column('widgets', 'options',
- existing_type=sa.Text(),
- type_=JSONB(astext_type=sa.Text()),
- postgresql_using='options::jsonb',
- )
-
-
-def downgrade():
- connection = op.get_bind()
- op.alter_column('queries', 'options',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=sa.Text(),
- postgresql_using='options::text',
- existing_nullable=True,
- )
- op.alter_column('queries', 'schedule',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=sa.Text(),
- postgresql_using='schedule::text',
- existing_nullable=True,
- )
- op.alter_column('events', 'additional_properties',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=sa.Text(),
- postgresql_using='additional_properties::text',
- existing_nullable=True,
- )
- op.alter_column('organizations', 'settings',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=sa.Text(),
- postgresql_using='settings::text',
- existing_nullable=True,
- )
- op.alter_column('alerts', 'options',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=JSON(astext_type=sa.Text()),
- postgresql_using='options::json',
- existing_nullable=True,
- )
- op.alter_column('dashboards', 'options',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=JSON(astext_type=sa.Text()),
- postgresql_using='options::json',
- )
- op.alter_column('dashboards', 'layout',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=sa.Text(),
- postgresql_using='layout::text',
- )
- op.alter_column('changes', 'change',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=JSON(astext_type=sa.Text()),
- postgresql_using='change::json',
- )
- op.alter_column('visualizations', 'options',
- type_=sa.Text(),
- existing_type=JSONB(astext_type=sa.Text()),
- postgresql_using='options::text',
- )
- op.alter_column('widgets', 'options',
- type_=sa.Text(),
- existing_type=JSONB(astext_type=sa.Text()),
- postgresql_using='options::text',
- )
diff --git a/migrations/versions/73beceabb948_bring_back_null_schedule.py b/migrations/versions/73beceabb948_bring_back_null_schedule.py
index 2a07a0b7bd..b510639dd2 100644
--- a/migrations/versions/73beceabb948_bring_back_null_schedule.py
+++ b/migrations/versions/73beceabb948_bring_back_null_schedule.py
@@ -7,9 +7,10 @@
"""
from alembic import op
import sqlalchemy as sa
+from sqlalchemy.dialects import postgresql
from sqlalchemy.sql import table
-from redash.models import MutableDict
+from redash.models import MutableDict, PseudoJSON
# revision identifiers, used by Alembic.
revision = "73beceabb948"
@@ -42,7 +43,7 @@ def upgrade():
queries = table(
"queries",
sa.Column("id", sa.Integer, primary_key=True),
- sa.Column("schedule", sa.Text()),
+ sa.Column("schedule", MutableDict.as_mutable(PseudoJSON)),
)
conn = op.get_bind()
diff --git a/migrations/versions/7ce5925f832b_create_sqlalchemy_searchable_expressions.py b/migrations/versions/7ce5925f832b_create_sqlalchemy_searchable_expressions.py
deleted file mode 100644
index 46a0370392..0000000000
--- a/migrations/versions/7ce5925f832b_create_sqlalchemy_searchable_expressions.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""create sqlalchemy_searchable expressions
-
-Revision ID: 7ce5925f832b
-Revises: 1038c2174f5d
-Create Date: 2023-09-29 16:48:29.517762
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy_searchable import sql_expressions
-
-
-# revision identifiers, used by Alembic.
-revision = '7ce5925f832b'
-down_revision = '1038c2174f5d'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- op.execute(sql_expressions)
-
-
-def downgrade():
- pass
diff --git a/migrations/versions/89bc7873a3e0_fix_multiple_heads.py b/migrations/versions/89bc7873a3e0_fix_multiple_heads.py
deleted file mode 100644
index 54c744fbcd..0000000000
--- a/migrations/versions/89bc7873a3e0_fix_multiple_heads.py
+++ /dev/null
@@ -1,24 +0,0 @@
-"""fix_multiple_heads
-
-Revision ID: 89bc7873a3e0
-Revises: 0ec979123ba4, d7d747033183
-Create Date: 2021-01-21 18:11:04.312259
-
-"""
-from alembic import op
-import sqlalchemy as sa
-
-
-# revision identifiers, used by Alembic.
-revision = '89bc7873a3e0'
-down_revision = ('0ec979123ba4', 'd7d747033183')
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- pass
-
-
-def downgrade():
- pass
diff --git a/migrations/versions/969126bd800f_.py b/migrations/versions/969126bd800f_.py
index 1a49383861..17eec1153d 100644
--- a/migrations/versions/969126bd800f_.py
+++ b/migrations/versions/969126bd800f_.py
@@ -6,7 +6,7 @@
"""
-import json
+import simplejson
from alembic import op
import sqlalchemy as sa
@@ -27,7 +27,7 @@ def upgrade():
dashboard_result = db.session.execute("SELECT id, layout FROM dashboards")
for dashboard in dashboard_result:
print(" Updating dashboard: {}".format(dashboard["id"]))
- layout = json.loads(dashboard["layout"])
+ layout = simplejson.loads(dashboard["layout"])
print(" Building widgets map:")
widgets = {}
@@ -53,7 +53,7 @@ def upgrade():
if widget is None:
continue
- options = json.loads(widget["options"]) or {}
+ options = simplejson.loads(widget["options"]) or {}
options["position"] = {
"row": row_index,
"col": column_index * column_size,
@@ -62,7 +62,7 @@ def upgrade():
db.session.execute(
"UPDATE widgets SET options=:options WHERE id=:id",
- {"options": json.dumps(options), "id": widget_id},
+ {"options": simplejson.dumps(options), "id": widget_id},
)
dashboard_result.close()
diff --git a/migrations/versions/98af61feea92_add_encrypted_options_to_data_sources.py b/migrations/versions/98af61feea92_add_encrypted_options_to_data_sources.py
index 0c22043613..23670adfee 100644
--- a/migrations/versions/98af61feea92_add_encrypted_options_to_data_sources.py
+++ b/migrations/versions/98af61feea92_add_encrypted_options_to_data_sources.py
@@ -7,7 +7,7 @@
"""
from alembic import op
import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import BYTEA
+from sqlalchemy.dialects import postgresql
from sqlalchemy.sql import table
from sqlalchemy_utils.types.encrypted.encrypted_type import FernetEngine
@@ -18,6 +18,7 @@
Configuration,
MutableDict,
MutableList,
+ PseudoJSON,
)
# revision identifiers, used by Alembic.
@@ -30,7 +31,7 @@
def upgrade():
op.add_column(
"data_sources",
- sa.Column("encrypted_options", BYTEA(), nullable=True),
+ sa.Column("encrypted_options", postgresql.BYTEA(), nullable=True),
)
# copy values
diff --git a/migrations/versions/9e8c841d1a30_fix_hash.py b/migrations/versions/9e8c841d1a30_fix_hash.py
deleted file mode 100644
index 966d393391..0000000000
--- a/migrations/versions/9e8c841d1a30_fix_hash.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""fix_hash
-
-Revision ID: 9e8c841d1a30
-Revises: 7205816877ec
-Create Date: 2024-10-05 18:55:35.730573
-
-"""
-import logging
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.sql import table
-from sqlalchemy import select
-
-from redash.query_runner import BaseQueryRunner, get_query_runner
-
-
-# revision identifiers, used by Alembic.
-revision = '9e8c841d1a30'
-down_revision = '7205816877ec'
-branch_labels = None
-depends_on = None
-
-
-def update_query_hash(record):
- should_apply_auto_limit = record['options'].get("apply_auto_limit", False) if record['options'] else False
- query_runner = get_query_runner(record['type'], {}) if record['type'] else BaseQueryRunner({})
- query_text = record['query']
-
- parameters_dict = {p["name"]: p.get("value") for p in record['options'].get('parameters', [])} if record.options else {}
- if any(parameters_dict):
- print(f"Query {record['query_id']} has parameters. Hash might be incorrect.")
-
- return query_runner.gen_query_hash(query_text, should_apply_auto_limit)
-
-
-def upgrade():
- conn = op.get_bind()
-
- metadata = sa.MetaData(bind=conn)
- queries = sa.Table("queries", metadata, autoload=True)
- data_sources = sa.Table("data_sources", metadata, autoload=True)
-
- joined_table = queries.outerjoin(data_sources, queries.c.data_source_id == data_sources.c.id)
-
- query = select([
- queries.c.id.label("query_id"),
- queries.c.query,
- queries.c.query_hash,
- queries.c.options,
- data_sources.c.id.label("data_source_id"),
- data_sources.c.type
- ]).select_from(joined_table)
-
- for record in conn.execute(query):
- new_hash = update_query_hash(record)
- print(f"Updating hash for query {record['query_id']} from {record['query_hash']} to {new_hash}")
- conn.execute(
- queries.update()
- .where(queries.c.id == record['query_id'])
- .values(query_hash=new_hash))
-
-
-def downgrade():
- pass
\ No newline at end of file
diff --git a/migrations/versions/a92d92aa678e_inline_tags.py b/migrations/versions/a92d92aa678e_inline_tags.py
index 40421cf468..f79924dc62 100644
--- a/migrations/versions/a92d92aa678e_inline_tags.py
+++ b/migrations/versions/a92d92aa678e_inline_tags.py
@@ -9,7 +9,7 @@
from funcy import flatten, compact
from alembic import op
import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import ARRAY
+from sqlalchemy.dialects import postgresql
from redash import models
# revision identifiers, used by Alembic.
@@ -21,10 +21,10 @@
def upgrade():
op.add_column(
- "dashboards", sa.Column("tags", ARRAY(sa.Unicode()), nullable=True)
+ "dashboards", sa.Column("tags", postgresql.ARRAY(sa.Unicode()), nullable=True)
)
op.add_column(
- "queries", sa.Column("tags", ARRAY(sa.Unicode()), nullable=True)
+ "queries", sa.Column("tags", postgresql.ARRAY(sa.Unicode()), nullable=True)
)
diff --git a/migrations/versions/ba150362b02e_.py b/migrations/versions/ba150362b02e_.py
new file mode 100644
index 0000000000..636415557a
--- /dev/null
+++ b/migrations/versions/ba150362b02e_.py
@@ -0,0 +1,26 @@
+"""Add description field for data_sources
+
+Revision ID: ba150362b02e
+Revises: 118aa16f565b
+Create Date: 2019-02-05 21:21:08.069390
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "ba150362b02e"
+down_revision = "118aa16f565b"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.add_column(
+ "data_sources", sa.Column("description", sa.String(length=4096), nullable=True)
+ )
+
+
+def downgrade():
+ op.drop_column("data_sources", "description")
diff --git a/migrations/versions/cf135a57332e_.py b/migrations/versions/cf135a57332e_.py
new file mode 100644
index 0000000000..01694219e5
--- /dev/null
+++ b/migrations/versions/cf135a57332e_.py
@@ -0,0 +1,32 @@
+"""Add column description and table visibility fields
+
+Revision ID: cf135a57332e
+Revises: 6adb92e75691
+Create Date: 2019-02-05 19:52:48.233070
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = "cf135a57332e"
+down_revision = "6adb92e75691"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.add_column(
+ "column_metadata",
+ sa.Column("description", sa.String(length=4096), nullable=True),
+ )
+ op.add_column(
+ "table_metadata",
+ sa.Column("visible", sa.Boolean(), nullable=False, server_default="True"),
+ )
+
+
+def downgrade():
+ op.drop_column("table_metadata", "visible")
+ op.drop_column("column_metadata", "description")
diff --git a/migrations/versions/d7d747033183_encrypt_alert_destinations.py b/migrations/versions/d7d747033183_encrypt_alert_destinations.py
deleted file mode 100644
index d66460c3f2..0000000000
--- a/migrations/versions/d7d747033183_encrypt_alert_destinations.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""encrypt alert destinations
-
-Revision ID: d7d747033183
-Revises: e5c7a4e2df4d
-Create Date: 2020-12-14 21:42:48.661684
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import BYTEA
-from sqlalchemy.sql import table
-from sqlalchemy_utils.types.encrypted.encrypted_type import FernetEngine
-
-from redash import settings
-from redash.utils.configuration import ConfigurationContainer
-from redash.models.base import key_type
-from redash.models.types import (
- EncryptedConfiguration,
- Configuration,
-)
-
-
-# revision identifiers, used by Alembic.
-revision = 'd7d747033183'
-down_revision = 'e5c7a4e2df4d'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- op.add_column(
- "notification_destinations",
- sa.Column("encrypted_options", BYTEA(), nullable=True)
- )
-
- # copy values
- notification_destinations = table(
- "notification_destinations",
- sa.Column("id", key_type("NotificationDestination"), primary_key=True),
- sa.Column(
- "encrypted_options",
- ConfigurationContainer.as_mutable(
- EncryptedConfiguration(
- sa.Text, settings.DATASOURCE_SECRET_KEY, FernetEngine
- )
- ),
- ),
- sa.Column("options", ConfigurationContainer.as_mutable(Configuration)),
- )
-
- conn = op.get_bind()
- for dest in conn.execute(notification_destinations.select()):
- conn.execute(
- notification_destinations.update()
- .where(notification_destinations.c.id == dest.id)
- .values(encrypted_options=dest.options)
- )
-
- op.drop_column("notification_destinations", "options")
- op.alter_column("notification_destinations", "encrypted_options", nullable=False)
-
-
-def downgrade():
- pass
diff --git a/migrations/versions/da6767746e76_add_more_db_indexes.py b/migrations/versions/da6767746e76_add_more_db_indexes.py
new file mode 100644
index 0000000000..478aa42435
--- /dev/null
+++ b/migrations/versions/da6767746e76_add_more_db_indexes.py
@@ -0,0 +1,101 @@
+"""Add more db indexes.
+
+Revision ID: da6767746e76
+Revises: ba150362b02e
+Create Date: 2019-12-02 11:48:52.611441
+
+"""
+from alembic import op
+
+
+# revision identifiers, used by Alembic.
+revision = "da6767746e76"
+down_revision = "ba150362b02e"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.create_index(
+ op.f("ix_column_metadata_exists"), "column_metadata", ["exists"], unique=False
+ )
+ op.create_index(
+ op.f("ix_column_metadata_name"), "column_metadata", ["name"], unique=False
+ )
+ op.create_index(
+ op.f("ix_column_metadata_table_id"),
+ "column_metadata",
+ ["table_id"],
+ unique=False,
+ )
+ op.create_index(
+ "ix_column_metadata_table_id_exists",
+ "column_metadata",
+ ["table_id", "exists"],
+ unique=False,
+ )
+ op.create_index(
+ "ix_column_metadata_table_id_name_exists",
+ "column_metadata",
+ ["table_id", "exists", "name"],
+ unique=False,
+ )
+ op.create_index(
+ "ix_column_metadata_table_id_pkey",
+ "column_metadata",
+ ["table_id", "id"],
+ unique=False,
+ )
+ op.create_index(
+ op.f("ix_table_metadata_data_source_id"),
+ "table_metadata",
+ ["data_source_id"],
+ unique=False,
+ )
+ op.create_index(
+ "ix_table_metadata_data_source_id_exists",
+ "table_metadata",
+ ["data_source_id", "exists"],
+ unique=False,
+ )
+ op.create_index(
+ "ix_table_metadata_data_source_id_name_exists",
+ "table_metadata",
+ ["data_source_id", "exists", "name"],
+ unique=False,
+ )
+ op.create_index(
+ op.f("ix_table_metadata_exists"), "table_metadata", ["exists"], unique=False
+ )
+ op.create_index(
+ op.f("ix_table_metadata_name"), "table_metadata", ["name"], unique=False
+ )
+ op.create_index(
+ op.f("ix_table_metadata_sample_updated_at"),
+ "table_metadata",
+ ["sample_updated_at"],
+ unique=False,
+ )
+
+
+def downgrade():
+ op.drop_index(
+ op.f("ix_table_metadata_sample_updated_at"), table_name="table_metadata"
+ )
+ op.drop_index(op.f("ix_table_metadata_name"), table_name="table_metadata")
+ op.drop_index(op.f("ix_table_metadata_exists"), table_name="table_metadata")
+ op.drop_index(
+ "ix_table_metadata_data_source_id_name_exists", table_name="table_metadata"
+ )
+ op.drop_index(
+ "ix_table_metadata_data_source_id_exists", table_name="table_metadata"
+ )
+ op.drop_index(op.f("ix_table_metadata_data_source_id"), table_name="table_metadata")
+ op.drop_index("ix_column_metadata_table_id_pkey", table_name="column_metadata")
+ op.drop_index(
+ "ix_column_metadata_table_id_name_exists", table_name="column_metadata"
+ )
+ op.drop_index("ix_column_metadata_table_id_exists", table_name="column_metadata")
+ op.drop_index(op.f("ix_column_metadata_table_id"), table_name="column_metadata")
+ op.drop_index(op.f("ix_column_metadata_name"), table_name="column_metadata")
+ op.drop_index(op.f("ix_column_metadata_exists"), table_name="column_metadata")
diff --git a/migrations/versions/e7f8a917aa8e_add_user_details_json_column.py b/migrations/versions/e7f8a917aa8e_add_user_details_json_column.py
index 77c4f54485..a5a827091c 100644
--- a/migrations/versions/e7f8a917aa8e_add_user_details_json_column.py
+++ b/migrations/versions/e7f8a917aa8e_add_user_details_json_column.py
@@ -7,7 +7,7 @@
"""
from alembic import op
import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import JSON
+from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = "e7f8a917aa8e"
@@ -21,7 +21,7 @@ def upgrade():
"users",
sa.Column(
"details",
- JSON(astext_type=sa.Text()),
+ postgresql.JSON(astext_type=sa.Text()),
server_default="{}",
nullable=True,
),
diff --git a/migrations/versions/fd4fc850d7ea_.py b/migrations/versions/fd4fc850d7ea_.py
deleted file mode 100644
index 18c6ba01ea..0000000000
--- a/migrations/versions/fd4fc850d7ea_.py
+++ /dev/null
@@ -1,60 +0,0 @@
-"""Convert user details to jsonb and move user profile image url into details column
-
-Revision ID: fd4fc850d7ea
-Revises: 89bc7873a3e0
-Create Date: 2022-01-31 15:24:16.507888
-
-"""
-from alembic import op
-import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import JSON, JSONB
-
-from redash.models import db
-
-# revision identifiers, used by Alembic.
-revision = 'fd4fc850d7ea'
-down_revision = '89bc7873a3e0'
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
- connection = op.get_bind()
-
- ### commands auto generated by Alembic - please adjust! ###
- op.alter_column('users', 'details',
- existing_type=JSON(astext_type=sa.Text()),
- type_=JSONB(astext_type=sa.Text()),
- existing_nullable=True,
- existing_server_default=sa.text("'{}'::jsonb"))
- ### end Alembic commands ###
-
- update_query = """
- update users
- set details = details::jsonb || ('{"profile_image_url": "' || profile_image_url || '"}')::jsonb
- where 1=1
- """
- connection.execute(update_query)
- op.drop_column("users", "profile_image_url")
-
-
-def downgrade():
- # ### commands auto generated by Alembic - please adjust! ###
- connection = op.get_bind()
- op.add_column("users", sa.Column("profile_image_url", db.String(320), nullable=True))
-
- update_query = """
- update users set
- profile_image_url = details->>'profile_image_url',
- details = details - 'profile_image_url' ;
- """
-
- connection.execute(update_query)
- db.session.commit()
- op.alter_column('users', 'details',
- existing_type=JSONB(astext_type=sa.Text()),
- type_=JSON(astext_type=sa.Text()),
- existing_nullable=True,
- existing_server_default=sa.text("'{}'::json"))
-
- # ### end Alembic commands ###
diff --git a/netlify.toml b/netlify.toml
index 47c48e1fbb..950366eb2e 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -1,16 +1,7 @@
[build]
base = "client"
publish = "client/dist"
- # Netlify doesn't seem to install Yarn even though NETLIFY_USE_YARN is set below
- # command = "cd ../ && npm i -g yarn@1.22.19 && yarn --frozen-lockfile --force && cd viz-lib && yarn build:babel && cd .. && rm -r ./node_modules/@redash/viz && cp -r ./viz-lib/. ./node_modules/@redash/viz && yarn build && cd ./client"
- command = "cd ../ && yarn cache clean && yarn --frozen-lockfile --network-concurrency 1 && yarn build && cd ./client"
-
-[build.environment]
- NODE_VERSION = "18"
- NETLIFY_USE_YARN = "true"
- YARN_VERSION = "1.22.19"
- CYPRESS_INSTALL_BINARY = "0"
- PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = "1"
+ command = "npm ci && npm run build"
[[redirects]]
from = "/api/*"
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000000..fbb74d5513
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,18326 @@
+{
+ "name": "redash-client",
+ "version": "9.0.0-beta",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "3d-view": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/3d-view/-/3d-view-2.0.0.tgz",
+ "integrity": "sha1-gxrpQtdQjFCAHj4G+v4ejFdOF74=",
+ "requires": {
+ "matrix-camera-controller": "^2.1.1",
+ "orbit-camera-controller": "^4.0.0",
+ "turntable-camera-controller": "^3.0.0"
+ }
+ },
+ "@ant-design/colors": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.2.2.tgz",
+ "integrity": "sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==",
+ "requires": {
+ "tinycolor2": "^1.4.1"
+ }
+ },
+ "@ant-design/create-react-context": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/@ant-design/create-react-context/-/create-react-context-0.2.5.tgz",
+ "integrity": "sha512-1rMAa4qgP2lfl/QBH9i78+Gjxtj9FTMpMyDGZsEBW5Kih72EuUo9958mV8PgpRkh4uwPSQ7vVZWXeyNZXVAFDg==",
+ "requires": {
+ "gud": "^1.0.0",
+ "warning": "^4.0.3"
+ }
+ },
+ "@ant-design/css-animation": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/@ant-design/css-animation/-/css-animation-1.7.2.tgz",
+ "integrity": "sha512-bvVOe7A+r7lws58B7r+fgnQDK90cV45AXuvGx6i5CCSX1W/M3AJnHsNggDANBxEtWdNdFWcDd5LorB+RdSIlBw=="
+ },
+ "@ant-design/icons": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
+ "integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w=="
+ },
+ "@ant-design/icons-react": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@ant-design/icons-react/-/icons-react-2.0.1.tgz",
+ "integrity": "sha512-r1QfoltMuruJZqdiKcbPim3d8LNsVPB733U0gZEUSxBLuqilwsW28K2rCTWSMTjmFX7Mfpf+v/wdiFe/XCqThw==",
+ "requires": {
+ "@ant-design/colors": "^3.1.0",
+ "babel-runtime": "^6.26.0"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
+ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.0.0"
+ }
+ },
+ "@babel/core": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.3.4.tgz",
+ "integrity": "sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/generator": "^7.3.4",
+ "@babel/helpers": "^7.2.0",
+ "@babel/parser": "^7.3.4",
+ "@babel/template": "^7.2.2",
+ "@babel/traverse": "^7.3.4",
+ "@babel/types": "^7.3.4",
+ "convert-source-map": "^1.1.0",
+ "debug": "^4.1.0",
+ "json5": "^2.1.0",
+ "lodash": "^4.17.11",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.4.tgz",
+ "integrity": "sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.3.4",
+ "jsesc": "^2.5.1",
+ "lodash": "^4.17.11",
+ "source-map": "^0.5.0",
+ "trim-right": "^1.0.1"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz",
+ "integrity": "sha512-3UYcJUj9kvSLbLbUIfQTqzcy5VX7GRZ/CCDrnOaZorFFM01aXp1+GJwuFGV4NDDoAS+mOUyHcO6UD/RfqOks3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.1.0.tgz",
+ "integrity": "sha512-qNSR4jrmJ8M1VMM9tibvyRAHXQs2PmaksQF7c1CGJNipfe3D8p+wgNwgso/P2A2r2mdgBWAXljNWR0QRZAMW8w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-explode-assignable-expression": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-builder-react-jsx": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.3.0.tgz",
+ "integrity": "sha512-MjA9KgwCuPEkQd9ncSXvSyJ5y+j2sICHyrI0M3L+6fnS4wMSNDc1ARXsbTfbb2cXHn17VisSnU/sHFTCxVxSMw==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.3.0",
+ "esutils": "^2.0.0"
+ }
+ },
+ "@babel/helper-call-delegate": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-call-delegate/-/helper-call-delegate-7.1.0.tgz",
+ "integrity": "sha512-YEtYZrw3GUK6emQHKthltKNZwszBcHK58Ygcis+gVUrF4/FmTVr5CCqQNSfmvg2y+YDEANyYoaLz/SHsnusCwQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.0.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.4.tgz",
+ "integrity": "sha512-uFpzw6L2omjibjxa8VGZsJUPL5wJH0zzGKpoz0ccBkzIa6C8kWNUbiBmQ0rgOKWlHJ6qzmfa6lTiGchiV8SC+g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.3.4",
+ "@babel/helper-split-export-declaration": "^7.0.0"
+ }
+ },
+ "@babel/helper-define-map": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz",
+ "integrity": "sha512-yPPcW8dc3gZLN+U1mhYV91QU3n5uTbx7DUdf8NnPbjS0RMwBuHi9Xt2MUgppmNz7CJxTBWsGczTiEp1CSOTPRg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/types": "^7.0.0",
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/helper-explode-assignable-expression": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.1.0.tgz",
+ "integrity": "sha512-NRQpfHrJ1msCHtKjbzs9YcMmJZOg6mQMmGRB+hbamEdG5PNpaSm95275VD92DvJKuyl0s2sFiDmMZ+EnnvufqA==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz",
+ "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.0.0",
+ "@babel/template": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz",
+ "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0.tgz",
+ "integrity": "sha512-Ggv5sldXUeSKsuzLkddtyhyHe2YantsxWKNi7A+7LeD12ExRDWTRk29JCXpaHPAbMaIPZSil7n+lq78WY2VY7w==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0.tgz",
+ "integrity": "sha512-avo+lm/QmZlv27Zsi0xEor2fKcqWG56D5ae9dzklpIaY7cQMK5N8VSpaNVPPagiqmy7LrEjK1IWdGMOqPu5csg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz",
+ "integrity": "sha512-aP/hlLq01DWNEiDg4Jn23i+CXxW/owM4WpDLFUbpjxe4NS3BhLVZQ5i7E0ZrxuQ/vwekIeciyamgB1UIYxxM6A==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.2.2.tgz",
+ "integrity": "sha512-YRD7I6Wsv+IHuTPkAmAS4HhY0dkPobgLftHp0cRGZSdrRvmZY8rFvae/GVu3bD00qscuvK3WPHB3YdNpBXUqrA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-simple-access": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.0.0",
+ "@babel/template": "^7.2.2",
+ "@babel/types": "^7.2.2",
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0.tgz",
+ "integrity": "sha512-u8nd9NQePYNQV8iPWu/pLLYBqZBa4ZaY1YWRFMuxrid94wKI1QNt67NEZ7GAe5Kc/0LLScbim05xZFWkAdrj9g==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz",
+ "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==",
+ "dev": true
+ },
+ "@babel/helper-regex": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.0.0.tgz",
+ "integrity": "sha512-TR0/N0NDCcUIUEbqV6dCO+LptmmSQFQ7q70lfcEB4URsjD0E1HzicrwUH+ap6BAQ2jhCX9Q4UqZy4wilujWlkg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.10"
+ }
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.1.0.tgz",
+ "integrity": "sha512-3fOK0L+Fdlg8S5al8u/hWE6vhufGSn0bN09xm2LXMy//REAF8kDCrYoOBKYmA8m5Nom+sV9LyLCwrFynA8/slg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-wrap-function": "^7.1.0",
+ "@babel/template": "^7.1.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.3.4.tgz",
+ "integrity": "sha512-pvObL9WVf2ADs+ePg0jrqlhHoxRXlOa+SHRHzAXIz2xkYuOHfGl+fKxPMaS4Fq+uje8JQPobnertBBvyrWnQ1A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.0.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/traverse": "^7.3.4",
+ "@babel/types": "^7.3.4"
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.1.0.tgz",
+ "integrity": "sha512-Vk+78hNjRbsiu49zAPALxTb+JUQCz1aolpd8osOF16BGnLtseD21nbHgLPGUwrXEurZgiCOUmvs3ExTu4F5x6w==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0.tgz",
+ "integrity": "sha512-MXkOJqva62dfC0w85mEf/LucPPS/1+04nmmRMPEBUB++hiiThQ2zPtX/mEWQ3mtzCEjIJvPY8nuwxXtQeQwUag==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.2.0.tgz",
+ "integrity": "sha512-o9fP1BZLLSrYlxYEYyl2aS+Flun5gtjTIG8iln+XuEzQTs0PLagAGSXUcqruJwD5fM48jzIEggCKpIfWTcR7pQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/template": "^7.1.0",
+ "@babel/traverse": "^7.1.0",
+ "@babel/types": "^7.2.0"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.3.1.tgz",
+ "integrity": "sha512-Q82R3jKsVpUV99mgX50gOPCWwco9Ec5Iln/8Vyu4osNIOQgSrd9RFrQeUvmvddFNoLwMyOUWU+5ckioEKpDoGA==",
+ "dev": true,
+ "requires": {
+ "@babel/template": "^7.1.2",
+ "@babel/traverse": "^7.1.5",
+ "@babel/types": "^7.3.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
+ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.4.tgz",
+ "integrity": "sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==",
+ "dev": true
+ },
+ "@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz",
+ "integrity": "sha512-+Dfo/SCQqrwx48ptLVGLdE39YtWRuKc/Y9I5Fy0P1DDBB9lsAHpjcEJQt+4IifuSOSTLBKJObJqMvaO1pIE8LQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-remap-async-to-generator": "^7.1.0",
+ "@babel/plugin-syntax-async-generators": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.4.tgz",
+ "integrity": "sha512-lUf8D3HLs4yYlAo8zjuneLvfxN7qfKv1Yzbj5vjqaqMJxgJA3Ipwp4VUJ+OrOdz53Wbww6ahwB8UhB2HQyLotA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.3.4",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-proposal-json-strings": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz",
+ "integrity": "sha512-MAFV1CA/YVmYwZG0fBQyXhmj0BHCB5egZHCKWIFVv/XCxAeVGIHfos3SwDck4LvCllENIAg7xMKOG5kH0dzyUg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-json-strings": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.3.4.tgz",
+ "integrity": "sha512-j7VQmbbkA+qrzNqbKHrBsW3ddFnOeva6wzSe/zB7T+xaxGc+RCpwo44wCmRixAIGRoIpmVgvzFzNJqQcO3/9RA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.2.0.tgz",
+ "integrity": "sha512-mgYj3jCcxug6KUcX4OBoOJz3CMrwRfQELPQ5560F70YQUBZB7uac9fqaWamKR1iWUzGiK2t0ygzjTScZnVz75g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.2.0"
+ }
+ },
+ "@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.2.0.tgz",
+ "integrity": "sha512-LvRVYb7kikuOtIoUeWTkOxQEV1kYvL5B6U3iWEGCzPNRus1MzJweFqORTj+0jkxozkTSYNJozPOddxmqdqsRpw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.0.0",
+ "regexpu-core": "^4.2.0"
+ }
+ },
+ "@babel/plugin-syntax-async-generators": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.2.0.tgz",
+ "integrity": "sha512-1ZrIRBv2t0GSlcwVoQ6VgSLpLgiN/FVQUzt9znxo7v2Ov4jJrs8RY8tv0wvDmFN3qIdMKWrmMMW6yZ0G19MfGg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-json-strings": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz",
+ "integrity": "sha512-5UGYnMSLRE1dqqZwug+1LISpA403HzlSfsg6P9VXU6TBjcSHeNlw4DxDx7LgpF+iKZoOG/+uzqoRHTdcUpiZNg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-jsx": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.2.0.tgz",
+ "integrity": "sha512-VyN4QANJkRW6lDBmENzRszvZf3/4AXaj9YR7GwrWeeN9tEBPuXbmDYVU9bYBN0D70zCWVwUy0HWq2553VCb6Hw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz",
+ "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.2.0.tgz",
+ "integrity": "sha512-bDe4xKNhb0LI7IvZHiA13kff0KEfaGX/Hv4lMA9+7TEc63hMNvfKo6ZFpXhKuEp+II/q35Gc4NoMeDZyaUbj9w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-arrow-functions": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz",
+ "integrity": "sha512-ER77Cax1+8/8jCB9fo4Ud161OZzWN5qawi4GusDuRLcDbDG+bIGYY20zb2dfAFdTRGzrfq2xZPvF0R64EHnimg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.3.4.tgz",
+ "integrity": "sha512-Y7nCzv2fw/jEZ9f678MuKdMo99MFDJMT/PvD9LisrR5JDFcJH6vYeH6RnjVt3p5tceyGRvTtEN0VOlU+rgHZjA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-remap-async-to-generator": "^7.1.0"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.2.0.tgz",
+ "integrity": "sha512-ntQPR6q1/NKuphly49+QiQiTN0O63uOwjdD6dhIjSWBI5xlrbUFh720TIpzBhpnrLfv2tNH/BXvLIab1+BAI0w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.3.4.tgz",
+ "integrity": "sha512-blRr2O8IOZLAOJklXLV4WhcEzpYafYQKSGT3+R26lWG41u/FODJuBggehtOwilVAcFu393v3OFj+HmaE6tVjhA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "lodash": "^4.17.11"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.3.4.tgz",
+ "integrity": "sha512-J9fAvCFBkXEvBimgYxCjvaVDzL6thk0j0dBvCeZmIUDBwyt+nv6HfbImsSrWsYXfDNDivyANgJlFXDUWRTZBuA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-define-map": "^7.1.0",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-optimise-call-expression": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.3.4",
+ "@babel/helper-split-export-declaration": "^7.0.0",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.2.0.tgz",
+ "integrity": "sha512-kP/drqTxY6Xt3NNpKiMomfgkNn4o7+vKxK2DDKcBG9sHj51vHqMBGy8wbDS/J4lMxnqs153/T3+DmCEAkC5cpA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.3.2.tgz",
+ "integrity": "sha512-Lrj/u53Ufqxl/sGxyjsJ2XNtNuEjDyjpqdhMNh5aZ+XFOdThL46KBj27Uem4ggoezSYBxKWAil6Hu8HtwqesYw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-dotall-regex": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.2.0.tgz",
+ "integrity": "sha512-sKxnyHfizweTgKZf7XsXu/CNupKhzijptfTM+bozonIuyVrLWVUvYjE2bhuSBML8VQeMxq4Mm63Q9qvcvUcciQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.0.0",
+ "regexpu-core": "^4.1.3"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.2.0.tgz",
+ "integrity": "sha512-q+yuxW4DsTjNceUiTzK0L+AfQ0zD9rWaTLiUqHA8p0gxx7lu1EylenfzjeIWNkPy6e/0VG/Wjw9uf9LueQwLOw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.2.0.tgz",
+ "integrity": "sha512-umh4hR6N7mu4Elq9GG8TOu9M0bakvlsREEC+ialrQN6ABS4oDQ69qJv1VtR3uxlKMCQMCvzk7vr17RHKcjx68A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.2.0.tgz",
+ "integrity": "sha512-Kz7Mt0SsV2tQk6jG5bBv5phVbkd0gd27SgYD4hH1aLMJRchM0dzHaXvrWhVZ+WxAlDoAKZ7Uy3jVTW2mKXQ1WQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.2.0.tgz",
+ "integrity": "sha512-kWgksow9lHdvBC2Z4mxTsvc7YdY7w/V6B2vy9cTIPtLEE9NhwoWivaxdNM/S37elu5bqlLP/qOY906LukO9lkQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.2.0.tgz",
+ "integrity": "sha512-2ThDhm4lI4oV7fVQ6pNNK+sx+c/GM5/SaML0w/r4ZB7sAneD/piDJtwdKlNckXeyGK7wlwg2E2w33C/Hh+VFCg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.2.0.tgz",
+ "integrity": "sha512-mK2A8ucqz1qhrdqjS9VMIDfIvvT2thrEsIQzbaTdc5QFzhDjQv2CkJJ5f6BXIkgbmaoax3zBr2RyvV/8zeoUZw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-modules-commonjs": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.2.0.tgz",
+ "integrity": "sha512-V6y0uaUQrQPXUrmj+hgnks8va2L0zcZymeU7TtWEgdRLNkceafKXEduv7QzgQAE4lT+suwooG9dC7LFhdRAbVQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-simple-access": "^7.1.0"
+ }
+ },
+ "@babel/plugin-transform-modules-systemjs": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.3.4.tgz",
+ "integrity": "sha512-VZ4+jlGOF36S7TjKs8g4ojp4MEI+ebCQZdswWb/T9I4X84j8OtFAyjXjt/M16iIm5RIZn0UMQgg/VgIwo/87vw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-modules-umd": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.2.0.tgz",
+ "integrity": "sha512-BV3bw6MyUH1iIsGhXlOK6sXhmSarZjtJ/vMiD9dNmpY8QXFFQTj+6v92pcfy1iqa8DeAfJFwoxcrS/TUZda6sw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.1.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.3.0.tgz",
+ "integrity": "sha512-NxIoNVhk9ZxS+9lSoAQ/LM0V2UEvARLttEHUrRDGKFaAxOYQcrkN/nLRE+BbbicCAvZPl7wMP0X60HsHE5DtQw==",
+ "dev": true,
+ "requires": {
+ "regexp-tree": "^0.1.0"
+ }
+ },
+ "@babel/plugin-transform-new-target": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0.tgz",
+ "integrity": "sha512-yin069FYjah+LbqfGeTfzIBODex/e++Yfa0rH0fpfam9uTbuEeEOx5GLGr210ggOV77mVRNoeqSYqeuaqSzVSw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-object-assign": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-assign/-/plugin-transform-object-assign-7.2.0.tgz",
+ "integrity": "sha512-nmE55cZBPFgUktbF2OuoZgPRadfxosLOpSgzEPYotKSls9J4pEPcembi8r78RU37Rph6UApCpNmsQA4QMWK9Ng==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.2.0.tgz",
+ "integrity": "sha512-VMyhPYZISFZAqAPVkiYb7dUe2AsVi2/wCT5+wZdsNO31FojQJa9ns40hzZ6U9f50Jlq4w6qwzdBB2uwqZ00ebg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-replace-supers": "^7.1.0"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.3.3.tgz",
+ "integrity": "sha512-IrIP25VvXWu/VlBWTpsjGptpomtIkYrN/3aDp4UKm7xK6UxZY88kcJ1UwETbzHAlwN21MnNfwlar0u8y3KpiXw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-call-delegate": "^7.1.0",
+ "@babel/helper-get-function-arity": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-react-display-name": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.2.0.tgz",
+ "integrity": "sha512-Htf/tPa5haZvRMiNSQSFifK12gtr/8vwfr+A9y69uF0QcU77AVu4K7MiHEkTxF7lQoHOL0F9ErqgfNEAKgXj7A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-react-jsx": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.3.0.tgz",
+ "integrity": "sha512-a/+aRb7R06WcKvQLOu4/TpjKOdvVEKRLWFpKcNuHhiREPgGRB4TQJxq07+EZLS8LFVYpfq1a5lDUnuMdcCpBKg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-react-jsx": "^7.3.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-jsx": "^7.2.0"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-self": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.2.0.tgz",
+ "integrity": "sha512-v6S5L/myicZEy+jr6ielB0OR8h+EH/1QFx/YJ7c7Ua+7lqsjj/vW6fD5FR9hB/6y7mGbfT4vAURn3xqBxsUcdg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-jsx": "^7.2.0"
+ }
+ },
+ "@babel/plugin-transform-react-jsx-source": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.2.0.tgz",
+ "integrity": "sha512-A32OkKTp4i5U6aE88GwwcuV4HAprUgHcTq0sSafLxjr6AW0QahrCRCjxogkbbcdtpbXkuTOlgpjophCxb6sh5g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-syntax-jsx": "^7.2.0"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.3.4.tgz",
+ "integrity": "sha512-hvJg8EReQvXT6G9H2MvNPXkv9zK36Vxa1+csAVTpE1J3j0zlHplw76uudEbJxgvqZzAq9Yh45FLD4pk5mKRFQA==",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "^0.13.4"
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
+ "integrity": "sha512-QP4eUM83ha9zmYtpbnyjTLAGKQritA5XW/iG9cjtuOI8s1RuL/3V6a3DeSHfKutJQ+ayUfeZJPcnCYEQzaPQqg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.2.2.tgz",
+ "integrity": "sha512-KWfky/58vubwtS0hLqEnrWJjsMGaOeSBn90Ezn5Jeg9Z8KKHmELbP1yGylMlm5N6TPKeY9A2+UaSYLdxahg01w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.2.0.tgz",
+ "integrity": "sha512-KKYCoGaRAf+ckH8gEL3JHUaFVyNHKe3ASNsZ+AlktgHevvxGigoIttrEJb8iKN03Q7Eazlv1s6cx2B2cQ3Jabw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.2.0.tgz",
+ "integrity": "sha512-FkPix00J9A/XWXv4VoKJBMeSkyY9x/TqIh76wzcdfl57RJJcf8CehQ08uwfhCDNtRQYtHQKBTwKZDEyjE13Lwg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.2.0.tgz",
+ "integrity": "sha512-2LNhETWYxiYysBtrBTqL8+La0jIoQQnIScUJc74OYvUGRmkskNY4EzLCnjHBzdmb38wqtTaixpo1NctEcvMDZw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz",
+ "integrity": "sha512-m48Y0lMhrbXEJnVUaYly29jRXbQ3ksxPrS1Tg8t+MHqzXhtBYAvI51euOBaoAlZLPHsieY9XPVMf80a5x0cPcA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/helper-regex": "^7.0.0",
+ "regexpu-core": "^4.1.3"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.3.4.tgz",
+ "integrity": "sha512-2mwqfYMK8weA0g0uBKOt4FE3iEodiHy9/CW0b+nWXcbL+pGzLx8ESYc+j9IIxr6LTDHWKgPm71i9smo02bw+gA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.0.0",
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-proposal-async-generator-functions": "^7.2.0",
+ "@babel/plugin-proposal-json-strings": "^7.2.0",
+ "@babel/plugin-proposal-object-rest-spread": "^7.3.4",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.2.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.2.0",
+ "@babel/plugin-syntax-async-generators": "^7.2.0",
+ "@babel/plugin-syntax-json-strings": "^7.2.0",
+ "@babel/plugin-syntax-object-rest-spread": "^7.2.0",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.2.0",
+ "@babel/plugin-transform-arrow-functions": "^7.2.0",
+ "@babel/plugin-transform-async-to-generator": "^7.3.4",
+ "@babel/plugin-transform-block-scoped-functions": "^7.2.0",
+ "@babel/plugin-transform-block-scoping": "^7.3.4",
+ "@babel/plugin-transform-classes": "^7.3.4",
+ "@babel/plugin-transform-computed-properties": "^7.2.0",
+ "@babel/plugin-transform-destructuring": "^7.2.0",
+ "@babel/plugin-transform-dotall-regex": "^7.2.0",
+ "@babel/plugin-transform-duplicate-keys": "^7.2.0",
+ "@babel/plugin-transform-exponentiation-operator": "^7.2.0",
+ "@babel/plugin-transform-for-of": "^7.2.0",
+ "@babel/plugin-transform-function-name": "^7.2.0",
+ "@babel/plugin-transform-literals": "^7.2.0",
+ "@babel/plugin-transform-modules-amd": "^7.2.0",
+ "@babel/plugin-transform-modules-commonjs": "^7.2.0",
+ "@babel/plugin-transform-modules-systemjs": "^7.3.4",
+ "@babel/plugin-transform-modules-umd": "^7.2.0",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.3.0",
+ "@babel/plugin-transform-new-target": "^7.0.0",
+ "@babel/plugin-transform-object-super": "^7.2.0",
+ "@babel/plugin-transform-parameters": "^7.2.0",
+ "@babel/plugin-transform-regenerator": "^7.3.4",
+ "@babel/plugin-transform-shorthand-properties": "^7.2.0",
+ "@babel/plugin-transform-spread": "^7.2.0",
+ "@babel/plugin-transform-sticky-regex": "^7.2.0",
+ "@babel/plugin-transform-template-literals": "^7.2.0",
+ "@babel/plugin-transform-typeof-symbol": "^7.2.0",
+ "@babel/plugin-transform-unicode-regex": "^7.2.0",
+ "browserslist": "^4.3.4",
+ "invariant": "^2.2.2",
+ "js-levenshtein": "^1.1.3",
+ "semver": "^5.3.0"
+ }
+ },
+ "@babel/preset-react": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.0.0.tgz",
+ "integrity": "sha512-oayxyPS4Zj+hF6Et11BwuBkmpgT/zMxyuZgFrMeZID6Hdh3dGlk4sHCAhdBCpuCKW2ppBfl2uCCetlrUIJRY3w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-transform-react-display-name": "^7.0.0",
+ "@babel/plugin-transform-react-jsx": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.0.0",
+ "@babel/plugin-transform-react-jsx-source": "^7.0.0"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz",
+ "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==",
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
+ }
+ }
+ },
+ "@babel/runtime-corejs3": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.7.4.tgz",
+ "integrity": "sha512-BBIEhzk8McXDcB3IbOi8zQPzzINUp4zcLesVlBSOcyGhzPUU8Xezk5GAG7Sy5GVhGmAO0zGd2qRSeY2g4Obqxw==",
+ "dev": true,
+ "requires": {
+ "core-js-pure": "^3.0.0",
+ "regenerator-runtime": "^0.13.2"
+ },
+ "dependencies": {
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/template": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.2.2.tgz",
+ "integrity": "sha512-zRL0IMM02AUDwghf5LMSSDEz7sBCO2YnNmpg3uWTZj/v1rcG2BmQUvaGU8GhU8BvfMh1k2KIAYZ7Ji9KXPUg7g==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/parser": "^7.2.2",
+ "@babel/types": "^7.2.2"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.3.4.tgz",
+ "integrity": "sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/generator": "^7.3.4",
+ "@babel/helper-function-name": "^7.1.0",
+ "@babel/helper-split-export-declaration": "^7.0.0",
+ "@babel/parser": "^7.3.4",
+ "@babel/types": "^7.3.4",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.11"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.3.4",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.4.tgz",
+ "integrity": "sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.11",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@choojs/findup": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@choojs/findup/-/findup-0.2.1.tgz",
+ "integrity": "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw==",
+ "requires": {
+ "commander": "^2.15.1"
+ }
+ },
+ "@mapbox/geojson-area": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@mapbox/geojson-area/-/geojson-area-0.2.2.tgz",
+ "integrity": "sha1-GNeBSqNr8j+7zDefjiaiKSfevxA=",
+ "requires": {
+ "wgs84": "0.0.0"
+ }
+ },
+ "@mapbox/geojson-rewind": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.4.1.tgz",
+ "integrity": "sha512-mxo2MEr7izA1uOXcDsw99Kgg6xW3P4H2j4n1lmldsgviIelpssvP+jQDivFKOHrOVJDpTTi5oZJvRcHtU9Uufw==",
+ "requires": {
+ "@mapbox/geojson-area": "0.2.2",
+ "concat-stream": "~1.6.0",
+ "minimist": "^1.2.5",
+ "sharkdown": "^0.1.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ }
+ }
+ },
+ "@mapbox/geojson-types": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz",
+ "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw=="
+ },
+ "@mapbox/jsonlint-lines-primitives": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz",
+ "integrity": "sha1-zlblOfg1UrWNENZy6k1vya3HsjQ="
+ },
+ "@mapbox/mapbox-gl-supported": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.5.0.tgz",
+ "integrity": "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg=="
+ },
+ "@mapbox/point-geometry": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz",
+ "integrity": "sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI="
+ },
+ "@mapbox/tiny-sdf": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz",
+ "integrity": "sha512-Ihn1nZcGIswJ5XGbgFAvVumOgWpvIjBX9jiRlIl46uQG9vJOF51ViBYHF95rEZupuyQbEmhLaDPLQlU7fUTsBg=="
+ },
+ "@mapbox/unitbezier": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz",
+ "integrity": "sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4="
+ },
+ "@mapbox/vector-tile": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz",
+ "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==",
+ "requires": {
+ "@mapbox/point-geometry": "~0.1.0"
+ }
+ },
+ "@mapbox/whoots-js": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
+ "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="
+ },
+ "@plotly/d3-sankey": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/@plotly/d3-sankey/-/d3-sankey-0.7.2.tgz",
+ "integrity": "sha512-2jdVos1N3mMp3QW0k2q1ph7Gd6j5PY1YihBrwpkFnKqO+cqtZq3AdEYUeSGXMeLsBDQYiqTVcihYfk8vr5tqhw==",
+ "requires": {
+ "d3-array": "1",
+ "d3-collection": "1",
+ "d3-shape": "^1.2.0"
+ }
+ },
+ "@plotly/d3-sankey-circular": {
+ "version": "0.33.1",
+ "resolved": "https://registry.npmjs.org/@plotly/d3-sankey-circular/-/d3-sankey-circular-0.33.1.tgz",
+ "integrity": "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ==",
+ "requires": {
+ "d3-array": "^1.2.1",
+ "d3-collection": "^1.0.4",
+ "d3-shape": "^1.2.0",
+ "elementary-circuits-directed-graph": "^1.0.4"
+ }
+ },
+ "@redash/viz": {
+ "version": "file:viz-lib",
+ "requires": {
+ "axios": "^0.19.2",
+ "beautifymarker": "^1.0.7",
+ "chroma-js": "^1.3.6",
+ "classnames": "^2.2.6",
+ "d3": "^3.5.17",
+ "d3-cloud": "^1.2.4",
+ "debug": "^3.1.0",
+ "dompurify": "^2.0.7",
+ "font-awesome": "^4.7.0",
+ "hoist-non-react-statics": "^3.3.0",
+ "leaflet": "^1.2.0",
+ "leaflet-fullscreen": "^1.0.2",
+ "leaflet.markercluster": "^1.1.0",
+ "lodash": "^4.17.10",
+ "moment": "^2.19.3",
+ "numeral": "^2.0.6",
+ "plotly.js": "1.52.3",
+ "react-pivottable": "^0.9.0",
+ "react-sortable-hoc": "^1.10.1",
+ "tinycolor2": "^1.4.1",
+ "use-debounce": "^3.4.1",
+ "use-media": "^1.4.0"
+ },
+ "dependencies": {
+ "axios": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
+ "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
+ "requires": {
+ "follow-redirects": "1.5.10"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "requires": {
+ "debug": "=3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "use-debounce": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-3.4.2.tgz",
+ "integrity": "sha512-rW44wZaFPh3XiwUzGBA0JRuklpbfKO/szU/5CYD2Q/erLmCem63lJ650p3a+NJE6S+g0rulKtBOfa/3rw/GN+Q=="
+ }
+ }
+ },
+ "@turf/area": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@turf/area/-/area-6.0.1.tgz",
+ "integrity": "sha512-Zv+3N1ep9P5JvR0YOYagLANyapGWQBh8atdeR3bKpWcigVXFsEKNUw03U/5xnh+cKzm7yozHD6MFJkqQv55y0g==",
+ "requires": {
+ "@turf/helpers": "6.x",
+ "@turf/meta": "6.x"
+ }
+ },
+ "@turf/bbox": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@turf/bbox/-/bbox-6.0.1.tgz",
+ "integrity": "sha512-EGgaRLettBG25Iyx7VyUINsPpVj1x3nFQFiGS3ER8KCI1MximzNLsam3eXRabqQDjyAKyAE1bJ4EZEpGvspQxw==",
+ "requires": {
+ "@turf/helpers": "6.x",
+ "@turf/meta": "6.x"
+ }
+ },
+ "@turf/centroid": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/@turf/centroid/-/centroid-6.0.2.tgz",
+ "integrity": "sha512-auyDauOtC4eddH7GC3CHFTDu2PKhpSeKCRhwhHhXtJqn2dWCJQNIoCeJRmfXRIbzCWhWvgvQafvvhq8HNvmvWw==",
+ "requires": {
+ "@turf/helpers": "6.x",
+ "@turf/meta": "6.x"
+ }
+ },
+ "@turf/helpers": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-6.1.4.tgz",
+ "integrity": "sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g=="
+ },
+ "@turf/meta": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/@turf/meta/-/meta-6.0.2.tgz",
+ "integrity": "sha512-VA7HJkx7qF1l3+GNGkDVn2oXy4+QoLP6LktXAaZKjuT1JI0YESat7quUkbCMy4zP9lAUuvS4YMslLyTtr919FA==",
+ "requires": {
+ "@turf/helpers": "6.x"
+ }
+ },
+ "@types/eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
+ "dev": true
+ },
+ "@types/json-schema": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
+ "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "11.9.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.5.tgz",
+ "integrity": "sha512-vVjM0SVzgaOUpflq4GYBvCpozes8OgIIS5gVXVka+OfK3hvnkC1i93U8WiY2OtNE4XUWyyy/86Kf6e0IHTQw1Q==",
+ "dev": true
+ },
+ "@types/prop-types": {
+ "version": "15.7.0",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.0.tgz",
+ "integrity": "sha512-eItQyV43bj4rR3JPV0Skpl1SncRCdziTEK9/v8VwXmV6d/qOUO8/EuWeHBbCZcsfSHfzI5UyMJLCSXtxxznyZg=="
+ },
+ "@types/react": {
+ "version": "16.8.5",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-16.8.5.tgz",
+ "integrity": "sha512-8LRySaaSJVLNZb2dbOGvGmzn88cbAfrgDpuWy+6lLgQ0OJFgHHvyuaCX4/7ikqJlpmCPf4uazJAZcfTQRdJqdQ==",
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^2.2.0"
+ }
+ },
+ "@types/react-dom": {
+ "version": "16.8.2",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.8.2.tgz",
+ "integrity": "sha512-MX7n1wq3G/De15RGAAqnmidzhr2Y9O/ClxPxyqaNg96pGyeXUYPSvujgzEVpLo9oIP4Wn1UETl+rxTN02KEpBw==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@types/react-slick": {
+ "version": "0.23.4",
+ "resolved": "https://registry.npmjs.org/@types/react-slick/-/react-slick-0.23.4.tgz",
+ "integrity": "sha512-vXoIy4GUfB7/YgqubR4H7RALo+pRdMYCeLgWwV3MPwl5pggTlEkFBTF19R7u+LJc85uMqC7RfsbkqPLMQ4ab+A==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "@typescript-eslint/eslint-plugin": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.10.0.tgz",
+ "integrity": "sha512-rT51fNLW0u3fnDGnAHVC5nu+Das+y2CpW10yqvf6/j5xbuUV3FxA3mBaIbM24CXODXjbgUznNb4Kg9XZOUxKAw==",
+ "dev": true,
+ "requires": {
+ "@typescript-eslint/experimental-utils": "2.10.0",
+ "eslint-utils": "^1.4.3",
+ "functional-red-black-tree": "^1.0.1",
+ "regexpp": "^3.0.0",
+ "tsutils": "^3.17.1"
+ }
+ },
+ "@typescript-eslint/experimental-utils": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz",
+ "integrity": "sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.3",
+ "@typescript-eslint/typescript-estree": "2.10.0",
+ "eslint-scope": "^5.0.0"
+ }
+ },
+ "@typescript-eslint/parser": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.10.0.tgz",
+ "integrity": "sha512-wQNiBokcP5ZsTuB+i4BlmVWq6o+oAhd8en2eSm/EE9m7BgZUIfEeYFd6z3S+T7bgNuloeiHA1/cevvbBDLr98g==",
+ "dev": true,
+ "requires": {
+ "@types/eslint-visitor-keys": "^1.0.0",
+ "@typescript-eslint/experimental-utils": "2.10.0",
+ "@typescript-eslint/typescript-estree": "2.10.0",
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "@typescript-eslint/typescript-estree": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.10.0.tgz",
+ "integrity": "sha512-oOYnplddQNm/LGVkqbkAwx4TIBuuZ36cAQq9v3nFIU9FmhemHuVzAesMSXNQDdAzCa5bFgCrfD3JWhYVKlRN2g==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "eslint-visitor-keys": "^1.1.0",
+ "glob": "^7.1.6",
+ "is-glob": "^4.0.1",
+ "lodash.unescape": "4.0.1",
+ "semver": "^6.3.0",
+ "tsutils": "^3.17.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
+ "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/helper-module-context": "1.8.5",
+ "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+ "@webassemblyjs/wast-parser": "1.8.5"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz",
+ "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz",
+ "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz",
+ "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-code-frame": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz",
+ "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/wast-printer": "1.8.5"
+ }
+ },
+ "@webassemblyjs/helper-fsm": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz",
+ "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-module-context": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz",
+ "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "mamacro": "^0.0.3"
+ }
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz",
+ "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz",
+ "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/helper-buffer": "1.8.5",
+ "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+ "@webassemblyjs/wasm-gen": "1.8.5"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz",
+ "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==",
+ "dev": true,
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz",
+ "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==",
+ "dev": true,
+ "requires": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz",
+ "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==",
+ "dev": true
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz",
+ "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/helper-buffer": "1.8.5",
+ "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+ "@webassemblyjs/helper-wasm-section": "1.8.5",
+ "@webassemblyjs/wasm-gen": "1.8.5",
+ "@webassemblyjs/wasm-opt": "1.8.5",
+ "@webassemblyjs/wasm-parser": "1.8.5",
+ "@webassemblyjs/wast-printer": "1.8.5"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz",
+ "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+ "@webassemblyjs/ieee754": "1.8.5",
+ "@webassemblyjs/leb128": "1.8.5",
+ "@webassemblyjs/utf8": "1.8.5"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz",
+ "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/helper-buffer": "1.8.5",
+ "@webassemblyjs/wasm-gen": "1.8.5",
+ "@webassemblyjs/wasm-parser": "1.8.5"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz",
+ "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/helper-api-error": "1.8.5",
+ "@webassemblyjs/helper-wasm-bytecode": "1.8.5",
+ "@webassemblyjs/ieee754": "1.8.5",
+ "@webassemblyjs/leb128": "1.8.5",
+ "@webassemblyjs/utf8": "1.8.5"
+ }
+ },
+ "@webassemblyjs/wast-parser": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz",
+ "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/floating-point-hex-parser": "1.8.5",
+ "@webassemblyjs/helper-api-error": "1.8.5",
+ "@webassemblyjs/helper-code-frame": "1.8.5",
+ "@webassemblyjs/helper-fsm": "1.8.5",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.8.5",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz",
+ "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/wast-parser": "1.8.5",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
+ },
+ "a-big-triangle": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/a-big-triangle/-/a-big-triangle-1.0.3.tgz",
+ "integrity": "sha1-7v0wsCqPUl6LH3K7a7GwwWdRx5Q=",
+ "requires": {
+ "gl-buffer": "^2.1.1",
+ "gl-vao": "^1.2.0",
+ "weak-map": "^1.0.5"
+ }
+ },
+ "abab": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz",
+ "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==",
+ "dev": true
+ },
+ "abbrev": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
+ },
+ "abs-svg-path": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
+ "integrity": "sha1-32Acjo0roQ1KdtYl4japo5wnI78="
+ },
+ "accepts": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
+ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.18",
+ "negotiator": "0.6.1"
+ }
+ },
+ "ace-builds": {
+ "version": "1.4.12",
+ "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.12.tgz",
+ "integrity": "sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg=="
+ },
+ "acorn": {
+ "version": "5.7.3",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
+ "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+ "dev": true
+ },
+ "acorn-dynamic-import": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
+ "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw=="
+ },
+ "acorn-globals": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz",
+ "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^6.0.1",
+ "acorn-walk": "^6.0.1"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
+ "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
+ "dev": true
+ }
+ }
+ },
+ "acorn-jsx": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz",
+ "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw=="
+ },
+ "acorn-walk": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz",
+ "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==",
+ "dev": true
+ },
+ "add-dom-event-listener": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz",
+ "integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==",
+ "requires": {
+ "object-assign": "4.x"
+ }
+ },
+ "add-line-numbers": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/add-line-numbers/-/add-line-numbers-1.0.1.tgz",
+ "integrity": "sha1-SNu96kfb0jTer+rGyTzqb3C0t+M=",
+ "requires": {
+ "pad-left": "^1.0.2"
+ }
+ },
+ "affine-hull": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/affine-hull/-/affine-hull-1.0.0.tgz",
+ "integrity": "sha1-dj/x040GPOt+Jy8X7k17vK+QXF0=",
+ "requires": {
+ "robust-orientation": "^1.1.3"
+ }
+ },
+ "ajv": {
+ "version": "6.9.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.2.tgz",
+ "integrity": "sha512-4UFy0/LgDo7Oa/+wOAlj44tp9K78u38E5/359eSrqEp1Z5PdVfimCcs7SluXMP755RUQu6d2b4AvF0R1C9RZjg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-errors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+ "dev": true
+ },
+ "ajv-keywords": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.0.tgz",
+ "integrity": "sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw==",
+ "dev": true
+ },
+ "align-text": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
+ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=",
+ "requires": {
+ "kind-of": "^3.0.2",
+ "longest": "^1.0.1",
+ "repeat-string": "^1.5.2"
+ }
+ },
+ "almost-equal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/almost-equal/-/almost-equal-1.1.0.tgz",
+ "integrity": "sha1-+FHGMROHV5lCdqou++jfowZszN0="
+ },
+ "alpha-complex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/alpha-complex/-/alpha-complex-1.0.0.tgz",
+ "integrity": "sha1-kIZYcNawVCrnPAwTHU75iWabctI=",
+ "requires": {
+ "circumradius": "^1.0.0",
+ "delaunay-triangulate": "^1.1.6"
+ }
+ },
+ "alpha-shape": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/alpha-shape/-/alpha-shape-1.0.0.tgz",
+ "integrity": "sha1-yDEJkj7P2mZ9IWP+Tyb+JHJvZKk=",
+ "requires": {
+ "alpha-complex": "^1.0.0",
+ "simplicial-complex-boundary": "^1.0.0"
+ }
+ },
+ "alphanum-sort": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
+ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
+ "dev": true
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+ "optional": true
+ },
+ "ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+ "dev": true
+ },
+ "ansi-html": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
+ "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "ansicolors": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz",
+ "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8="
+ },
+ "antd": {
+ "version": "3.26.17",
+ "resolved": "https://registry.npmjs.org/antd/-/antd-3.26.17.tgz",
+ "integrity": "sha512-P9uSK8SZ/1AvhQCC6aaLEkVrQhjbfZyUnqNV+lDnPqtudnZD2Ycy7Og+/EhuOBsQpYQvVT2aPLMgQWFv8tdJkA==",
+ "requires": {
+ "@ant-design/create-react-context": "^0.2.4",
+ "@ant-design/icons": "~2.1.1",
+ "@ant-design/icons-react": "~2.0.1",
+ "@types/react-slick": "^0.23.4",
+ "array-tree-filter": "^2.1.0",
+ "babel-runtime": "6.x",
+ "classnames": "~2.2.6",
+ "copy-to-clipboard": "^3.2.0",
+ "css-animation": "^1.5.0",
+ "dom-closest": "^0.2.0",
+ "enquire.js": "^2.1.6",
+ "is-mobile": "^2.1.0",
+ "lodash": "^4.17.13",
+ "moment": "^2.24.0",
+ "omit.js": "^1.0.2",
+ "prop-types": "^15.7.2",
+ "raf": "^3.4.1",
+ "rc-animate": "^2.10.2",
+ "rc-calendar": "~9.15.7",
+ "rc-cascader": "~0.17.4",
+ "rc-checkbox": "~2.1.6",
+ "rc-collapse": "~1.11.3",
+ "rc-dialog": "~7.6.0",
+ "rc-drawer": "~3.1.1",
+ "rc-dropdown": "~2.4.1",
+ "rc-editor-mention": "^1.1.13",
+ "rc-form": "^2.4.10",
+ "rc-input-number": "~4.5.0",
+ "rc-mentions": "~0.4.0",
+ "rc-menu": "~7.5.1",
+ "rc-notification": "~3.3.1",
+ "rc-pagination": "~1.20.11",
+ "rc-progress": "~2.5.0",
+ "rc-rate": "~2.5.0",
+ "rc-resize-observer": "^0.1.0",
+ "rc-select": "~9.2.0",
+ "rc-slider": "~8.7.1",
+ "rc-steps": "~3.5.0",
+ "rc-switch": "~1.9.0",
+ "rc-table": "~6.10.5",
+ "rc-tabs": "~9.7.0",
+ "rc-time-picker": "~3.7.1",
+ "rc-tooltip": "~3.7.3",
+ "rc-tree": "~2.1.0",
+ "rc-tree-select": "~2.9.1",
+ "rc-trigger": "^2.6.2",
+ "rc-upload": "~2.9.1",
+ "rc-util": "^4.16.1",
+ "react-lazy-load": "^3.0.13",
+ "react-lifecycles-compat": "^3.0.4",
+ "react-slick": "~0.25.2",
+ "resize-observer-polyfill": "^1.5.1",
+ "shallowequal": "^1.1.0",
+ "warning": "~4.0.3"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+ }
+ }
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "append-transform": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz",
+ "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==",
+ "dev": true,
+ "requires": {
+ "default-require-extensions": "^2.0.0"
+ }
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ },
+ "dependencies": {
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ }
+ }
+ },
+ "aria-query": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
+ "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
+ "dev": true,
+ "requires": {
+ "ast-types-flow": "0.0.7",
+ "commander": "^2.11.0"
+ }
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg=="
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-bounds": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-bounds/-/array-bounds-1.0.1.tgz",
+ "integrity": "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ=="
+ },
+ "array-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+ "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+ "dev": true
+ },
+ "array-filter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz",
+ "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=",
+ "dev": true
+ },
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+ "dev": true
+ },
+ "array-includes": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
+ "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.7.0"
+ }
+ },
+ "array-normalize": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/array-normalize/-/array-normalize-1.1.4.tgz",
+ "integrity": "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg==",
+ "requires": {
+ "array-bounds": "^1.0.0"
+ }
+ },
+ "array-range": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/array-range/-/array-range-1.0.1.tgz",
+ "integrity": "sha1-9W5GWRhDYRxqVvd+8C7afFAIm/w="
+ },
+ "array-rearrange": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/array-rearrange/-/array-rearrange-2.2.2.tgz",
+ "integrity": "sha512-UfobP5N12Qm4Qu4fwLDIi2v6+wZsSf6snYSxAMeKhrh37YGnNWZPRmVEKc/2wfms53TLQnzfpG8wCx2Y/6NG1w=="
+ },
+ "array-tree-filter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
+ "integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw=="
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "array.prototype.flat": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz",
+ "integrity": "sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.10.0",
+ "function-bind": "^1.1.1"
+ }
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "asn1.js": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
+ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "assert": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "dev": true,
+ "requires": {
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "ast-metadata-inferer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ast-metadata-inferer/-/ast-metadata-inferer-0.1.1.tgz",
+ "integrity": "sha512-hc9w8Qrgg9Lf9iFcZVhNjUnhrd2BBpTlyCnegPVvCe6O0yMrF57a6Cmh7k+xUsfUOMh9wajOL5AsGOBNEyTCcw==",
+ "dev": true
+ },
+ "ast-types-flow": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+ "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
+ "dev": true
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
+ "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ },
+ "async-each": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
+ "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
+ "dev": true
+ },
+ "async-validator": {
+ "version": "1.11.5",
+ "resolved": "https://registry.npmjs.org/async-validator/-/async-validator-1.11.5.tgz",
+ "integrity": "sha512-XNtCsMAeAH1pdLMEg1z8/Bb3a8cdCbui9QbJATRFHHHW5kT6+NPI3zSVQUXgikTFITzsg+kYY5NTWhM2Orwt9w=="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "atob-lite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-1.0.0.tgz",
+ "integrity": "sha1-uI3KYAaSK5YglPdVaCa6sxxKKWs="
+ },
+ "autoprefixer": {
+ "version": "6.7.7",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz",
+ "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=",
+ "dev": true,
+ "requires": {
+ "browserslist": "^1.7.6",
+ "caniuse-db": "^1.0.30000634",
+ "normalize-range": "^0.1.2",
+ "num2fraction": "^1.2.2",
+ "postcss": "^5.2.16",
+ "postcss-value-parser": "^3.2.3"
+ },
+ "dependencies": {
+ "browserslist": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
+ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
+ "dev": true,
+ "requires": {
+ "caniuse-db": "^1.0.30000639",
+ "electron-to-chromium": "^1.2.7"
+ }
+ }
+ }
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
+ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+ "dev": true
+ },
+ "axios": {
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
+ "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
+ "requires": {
+ "follow-redirects": "1.5.10",
+ "is-buffer": "^2.0.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
+ "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
+ "requires": {
+ "debug": "=3.1.0"
+ }
+ },
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ }
+ }
+ },
+ "axobject-query": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.1.1.tgz",
+ "integrity": "sha512-lF98xa/yvy6j3fBHAgQXIYl+J4eZadOSqsPojemUqClzNbBV38wWGpUbQbVEyf4eUF5yF7eHmGgGA2JiHyjeqw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.7.4",
+ "@babel/runtime-corejs3": "^7.7.4"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
+ "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
+ "dev": true
+ }
+ }
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "esutils": "^2.0.2",
+ "js-tokens": "^3.0.2"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "babel-eslint": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.3.tgz",
+ "integrity": "sha512-z3U7eMY6r/3f3/JB9mTsLjyxrv0Yb1zb8PCWCLpguxfCzBIZUwy23R1t/XKewP+8mEN2Ck8Dtr4q20z6ce6SoA==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "eslint-visitor-keys": "^1.0.0",
+ "resolve": "^1.12.0"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz",
+ "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ }
+ }
+ },
+ "babel-jest": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.1.0.tgz",
+ "integrity": "sha512-MLcagnVrO9ybQGLEfZUqnOzv36iQzU7Bj4elm39vCukumLVSfoX+tRy3/jW7lUKc7XdpRmB/jech6L/UCsSZjw==",
+ "dev": true,
+ "requires": {
+ "babel-plugin-istanbul": "^5.1.0",
+ "babel-preset-jest": "^24.1.0",
+ "chalk": "^2.4.2",
+ "slash": "^2.0.0"
+ }
+ },
+ "babel-loader": {
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.5.tgz",
+ "integrity": "sha512-NTnHnVRd2JnRqPC0vW+iOQWU5pchDbYXsG2E6DMXEpMfUcQKclF9gmf3G3ZMhzG7IG9ji4coL0cm+FxeWxDpnw==",
+ "dev": true,
+ "requires": {
+ "find-cache-dir": "^2.0.0",
+ "loader-utils": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "util.promisify": "^1.0.0"
+ }
+ },
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.22.0"
+ }
+ },
+ "babel-plugin-istanbul": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.1.tgz",
+ "integrity": "sha512-RNNVv2lsHAXJQsEJ5jonQwrJVWK8AcZpG1oxhnjCUaAjL7xahYLANhPUZbzEQHjKy1NMYUwn+0NPKQc8iSY4xQ==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0",
+ "istanbul-lib-instrument": "^3.0.0",
+ "test-exclude": "^5.0.0"
+ }
+ },
+ "babel-plugin-jest-hoist": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.1.0.tgz",
+ "integrity": "sha512-gljYrZz8w1b6fJzKcsfKsipSru2DU2DmQ39aB6nV3xQ0DDv3zpIzKGortA5gknrhNnPN8DweaEgrnZdmbGmhnw==",
+ "dev": true
+ },
+ "babel-plugin-transform-builtin-extend": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/babel-plugin-transform-builtin-extend/-/babel-plugin-transform-builtin-extend-1.1.2.tgz",
+ "integrity": "sha1-Xpb+z1i4+h7XTvytiEdbKvPJEW4=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.2.0",
+ "babel-template": "^6.3.0"
+ }
+ },
+ "babel-preset-jest": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.1.0.tgz",
+ "integrity": "sha512-FfNLDxFWsNX9lUmtwY7NheGlANnagvxq8LZdl5PKnVG3umP+S/g0XbVBfwtA4Ai3Ri/IMkWabBz3Tyk9wdspcw==",
+ "dev": true,
+ "requires": {
+ "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
+ "babel-plugin-jest-hoist": "^24.1.0"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "requires": {
+ "core-js": "^2.4.0",
+ "regenerator-runtime": "^0.11.0"
+ }
+ },
+ "babel-template": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
+ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "babel-traverse": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "lodash": "^4.17.4"
+ },
+ "dependencies": {
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ }
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "babel-messages": "^6.23.0",
+ "babel-runtime": "^6.26.0",
+ "babel-types": "^6.26.0",
+ "babylon": "^6.18.0",
+ "debug": "^2.6.8",
+ "globals": "^9.18.0",
+ "invariant": "^2.2.2",
+ "lodash": "^4.17.4"
+ },
+ "dependencies": {
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "esutils": "^2.0.2",
+ "lodash": "^4.17.4",
+ "to-fast-properties": "^1.0.3"
+ },
+ "dependencies": {
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+ "dev": true
+ }
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ },
+ "barycentric": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/barycentric/-/barycentric-1.0.1.tgz",
+ "integrity": "sha1-8VYruJGyb0/sRjqC7to2V4AOxog=",
+ "requires": {
+ "robust-linear-solve": "^1.0.0"
+ }
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ }
+ }
+ },
+ "base64-js": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
+ "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+ "dev": true
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "beautifymarker": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/beautifymarker/-/beautifymarker-1.0.7.tgz",
+ "integrity": "sha512-iWj/8cZOJ2jW7N7VkmmmPg7gFLgU6C8ArX6m6/hkIz0LaktVWtE+kpL351AE6MK5sX57/CzzXH54jlYD9agxnQ=="
+ },
+ "bfj-node4": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/bfj-node4/-/bfj-node4-5.3.1.tgz",
+ "integrity": "sha512-SOmOsowQWfXc7ybFARsK3C4MCOWzERaOMV/Fl3Tgjs+5dJWyzo3oa127jL44eMbQiAN17J7SvAs2TRxEScTUmg==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.1",
+ "check-types": "^7.3.0",
+ "tryer": "^1.0.0"
+ }
+ },
+ "big-rat": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/big-rat/-/big-rat-1.0.4.tgz",
+ "integrity": "sha1-do0JO7V5MN0Y7Vdcf8on3FORreo=",
+ "requires": {
+ "bit-twiddle": "^1.0.2",
+ "bn.js": "^4.11.6",
+ "double-bits": "^1.1.1"
+ }
+ },
+ "big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz",
+ "integrity": "sha512-EgmjVLMn22z7eGGv3kcnHwSnJXmFHjISTY9E/S5lIcTD3Oxw05QTcBLNkJFzcb3cNueUdF/IN4U+d78V0zO8Hw==",
+ "dev": true
+ },
+ "binary-search-bounds": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.4.tgz",
+ "integrity": "sha512-2hg5kgdKql5ClF2ErBcSx0U5bnl5hgS4v7wMnLFodyR47yMtj2w+UAZB+0CiqyHct2q543i7Bi4/aMIegorCCg=="
+ },
+ "bit-twiddle": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-1.0.2.tgz",
+ "integrity": "sha1-DGwfq+KyPRcXPZpht7cJPrnhdp4="
+ },
+ "bitmap-sdf": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/bitmap-sdf/-/bitmap-sdf-1.0.3.tgz",
+ "integrity": "sha512-ojYySSvWTx21cbgntR942zgEgqj38wHctN64vr4vYRFf3GKVmI23YlA94meWGkFslidwLwGCsMy2laJ3g/94Sg==",
+ "requires": {
+ "clamp": "^1.0.1"
+ }
+ },
+ "bl": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
+ "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
+ "requires": {
+ "readable-stream": "^2.3.5",
+ "safe-buffer": "^5.1.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "bluebird": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
+ "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==",
+ "dev": true
+ },
+ "bn.js": {
+ "version": "4.11.8",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
+ "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
+ },
+ "body-parser": {
+ "version": "1.18.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+ "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
+ "dev": true,
+ "requires": {
+ "bytes": "3.0.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "~1.6.3",
+ "iconv-lite": "0.4.23",
+ "on-finished": "~2.3.0",
+ "qs": "6.5.2",
+ "raw-body": "2.3.3",
+ "type-is": "~1.6.16"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ }
+ }
+ },
+ "bonjour": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+ "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+ "dev": true,
+ "requires": {
+ "array-flatten": "^2.1.0",
+ "deep-equal": "^1.0.1",
+ "dns-equal": "^1.0.0",
+ "dns-txt": "^2.0.2",
+ "multicast-dns": "^6.0.1",
+ "multicast-dns-service-types": "^1.1.0"
+ },
+ "dependencies": {
+ "array-flatten": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+ "dev": true
+ }
+ }
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+ "dev": true
+ },
+ "bootstrap": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz",
+ "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA=="
+ },
+ "boundary-cells": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/boundary-cells/-/boundary-cells-2.0.1.tgz",
+ "integrity": "sha1-6QWo0UGc9Hyza+Pb9SXbXiTeAEI=",
+ "requires": {
+ "tape": "^4.0.0"
+ }
+ },
+ "box-intersect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/box-intersect/-/box-intersect-1.0.2.tgz",
+ "integrity": "sha512-yJeMwlmFPG1gIa7Rs/cGXeI6iOj6Qz5MG5PE61xLKpElUGzmJ4abm+qsLpzxKJFpsSDq742BQEocr8dI2t8Nxw==",
+ "requires": {
+ "bit-twiddle": "^1.0.2",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browser-process-hrtime": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
+ "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==",
+ "dev": true
+ },
+ "browser-resolve": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
+ "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
+ "dev": true,
+ "requires": {
+ "resolve": "1.1.7"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "requires": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "browserify-sign": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.1",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.2",
+ "elliptic": "^6.0.0",
+ "inherits": "^2.0.1",
+ "parse-asn1": "^5.0.0"
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "requires": {
+ "pako": "~1.0.5"
+ }
+ },
+ "browserslist": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.2.tgz",
+ "integrity": "sha512-ISS/AIAiHERJ3d45Fz0AVYKkgcy+F/eJHzKEvv1j0wwKGKD9T3BrwKr/5g45L+Y4XIK5PlTqefHciRFcfE1Jxg==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30000939",
+ "electron-to-chromium": "^1.3.113",
+ "node-releases": "^1.1.8"
+ }
+ },
+ "bser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/bser/-/bser-2.0.0.tgz",
+ "integrity": "sha1-mseNPtXZFYBP2HrLFYvHlxR6Fxk=",
+ "dev": true,
+ "requires": {
+ "node-int64": "^0.4.0"
+ }
+ },
+ "buble": {
+ "version": "0.19.8",
+ "resolved": "https://registry.npmjs.org/buble/-/buble-0.19.8.tgz",
+ "integrity": "sha512-IoGZzrUTY5fKXVkgGHw3QeXFMUNBFv+9l8a4QJKG1JhG3nCMHTdEX1DCOg8568E2Q9qvAQIiSokv6Jsgx8p2cA==",
+ "requires": {
+ "acorn": "^6.1.1",
+ "acorn-dynamic-import": "^4.0.0",
+ "acorn-jsx": "^5.0.1",
+ "chalk": "^2.4.2",
+ "magic-string": "^0.25.3",
+ "minimist": "^1.2.0",
+ "os-homedir": "^2.0.0",
+ "regexpu-core": "^4.5.4"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+ "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
+ },
+ "regenerate-unicode-properties": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz",
+ "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==",
+ "requires": {
+ "regenerate": "^1.4.0"
+ }
+ },
+ "regexpu-core": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
+ "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
+ "requires": {
+ "regenerate": "^1.4.0",
+ "regenerate-unicode-properties": "^8.2.0",
+ "regjsgen": "^0.5.1",
+ "regjsparser": "^0.6.4",
+ "unicode-match-property-ecmascript": "^1.0.4",
+ "unicode-match-property-value-ecmascript": "^1.2.0"
+ }
+ },
+ "regjsgen": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.1.tgz",
+ "integrity": "sha512-5qxzGZjDs9w4tzT3TPhCJqWdCc3RLYwy9J2NB0nm5Lz+S273lvWcpjaTGHsT1dc6Hhfq41uSEOw8wBmxrKOuyg=="
+ },
+ "regjsparser": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz",
+ "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==",
+ "requires": {
+ "jsesc": "~0.5.0"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz",
+ "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ=="
+ }
+ }
+ },
+ "bubleify": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/bubleify/-/bubleify-1.2.1.tgz",
+ "integrity": "sha512-vp3NHmaQVoKaKWvi15FTMinPNjfp+47+/kFJ9ifezdMF/CBLArCxDVUh+FQE3qRxCRj1qyjJqilTBHHqlM8MaQ==",
+ "requires": {
+ "buble": "^0.19.3"
+ }
+ },
+ "buffer": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ }
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
+ },
+ "buffer-indexof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
+ "dev": true
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
+ "builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+ "dev": true
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "dev": true
+ },
+ "cacache": {
+ "version": "10.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz",
+ "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.1",
+ "chownr": "^1.0.1",
+ "glob": "^7.1.2",
+ "graceful-fs": "^4.1.11",
+ "lru-cache": "^4.1.1",
+ "mississippi": "^2.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.2",
+ "ssri": "^5.2.4",
+ "unique-filename": "^1.1.0",
+ "y18n": "^4.0.0"
+ }
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+ "dev": true
+ },
+ "camel-case": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
+ "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
+ "dev": true,
+ "requires": {
+ "no-case": "^2.2.0",
+ "upper-case": "^1.1.1"
+ }
+ },
+ "camelcase": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
+ "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
+ },
+ "caniuse-api": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
+ "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=",
+ "dev": true,
+ "requires": {
+ "browserslist": "^1.3.6",
+ "caniuse-db": "^1.0.30000529",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
+ },
+ "dependencies": {
+ "browserslist": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
+ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
+ "dev": true,
+ "requires": {
+ "caniuse-db": "^1.0.30000639",
+ "electron-to-chromium": "^1.2.7"
+ }
+ }
+ }
+ },
+ "caniuse-db": {
+ "version": "1.0.30000939",
+ "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000939.tgz",
+ "integrity": "sha512-nB5tLf3hOs+biXl1lhKjHRgNC0J1I7H52h/t1FP7qxARKKwpB0z+P/JewJLYAlxCBP/q7rxJzQzHHrQMl0viKg==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30000939",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000939.tgz",
+ "integrity": "sha512-oXB23ImDJOgQpGjRv1tCtzAvJr4/OvrHi5SO2vUgB0g0xpdZZoA/BxfImiWfdwoYdUTtQrPsXsvYU/dmCSM8gg==",
+ "dev": true
+ },
+ "canvas-fit": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/canvas-fit/-/canvas-fit-1.5.0.tgz",
+ "integrity": "sha1-rhO+Zq3kL1vg5IfjRfzjCl5bXl8=",
+ "requires": {
+ "element-size": "^1.1.1"
+ }
+ },
+ "capture-exit": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-1.2.0.tgz",
+ "integrity": "sha1-HF/MSJ/QqwDU8ax64QcuMXP7q28=",
+ "dev": true,
+ "requires": {
+ "rsvp": "^3.3.3"
+ }
+ },
+ "cardinal": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-0.4.4.tgz",
+ "integrity": "sha1-ylu2iltRG5D+k7ms6km97lwyv+I=",
+ "requires": {
+ "ansicolors": "~0.2.1",
+ "redeyed": "~0.4.0"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "cdt2d": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cdt2d/-/cdt2d-1.0.0.tgz",
+ "integrity": "sha1-TyEkNLzWe9s9aLj+9KzcLFRBUUE=",
+ "requires": {
+ "binary-search-bounds": "^2.0.3",
+ "robust-in-sphere": "^1.1.3",
+ "robust-orientation": "^1.1.3"
+ }
+ },
+ "cell-orientation": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cell-orientation/-/cell-orientation-1.0.1.tgz",
+ "integrity": "sha1-tQStlqZq0obZ7dmFoiU9A7gNKFA="
+ },
+ "center-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
+ "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
+ "requires": {
+ "align-text": "^0.1.3",
+ "lazy-cache": "^1.0.3"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "check-types": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/check-types/-/check-types-7.4.0.tgz",
+ "integrity": "sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg==",
+ "dev": true
+ },
+ "cheerio": {
+ "version": "1.0.0-rc.2",
+ "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz",
+ "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=",
+ "dev": true,
+ "requires": {
+ "css-select": "~1.2.0",
+ "dom-serializer": "~0.1.0",
+ "entities": "~1.1.1",
+ "htmlparser2": "^3.9.1",
+ "lodash": "^4.15.0",
+ "parse5": "^3.0.1"
+ }
+ },
+ "chokidar": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.2.tgz",
+ "integrity": "sha512-IwXUx0FXc5ibYmPC2XeEj5mpXoV66sR+t3jqu2NS2GYwCktt3KF1/Qqjws/NkegajBA4RbZ5+DDwlOiJsxDHEg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.0"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ }
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
+ "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
+ "dev": true
+ },
+ "chroma-js": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-1.4.1.tgz",
+ "integrity": "sha512-jTwQiT859RTFN/vIf7s+Vl/Z2LcMrvMv3WUFmd/4u76AdlFC0NTNgqEEFPcRiHmAswPsMiQEDZLM8vX8qXpZNQ=="
+ },
+ "chrome-trace-event": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz",
+ "integrity": "sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "circumcenter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/circumcenter/-/circumcenter-1.0.0.tgz",
+ "integrity": "sha1-INeqE7F/usUvUtpPVMasi5Bu5Sk=",
+ "requires": {
+ "dup": "^1.0.0",
+ "robust-linear-solve": "^1.0.0"
+ }
+ },
+ "circumradius": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/circumradius/-/circumradius-1.0.0.tgz",
+ "integrity": "sha1-cGxEfj5VzR7T0RvRM+N8JSzDBbU=",
+ "requires": {
+ "circumcenter": "^1.0.0"
+ }
+ },
+ "clamp": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
+ "integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
+ },
+ "clap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
+ "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "classnames": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+ "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+ },
+ "clean-css": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
+ "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
+ "dev": true,
+ "requires": {
+ "source-map": "~0.6.0"
+ }
+ },
+ "clean-pslg": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/clean-pslg/-/clean-pslg-1.1.2.tgz",
+ "integrity": "sha1-vTXHRgt+irWp92Gl7VF5aqPIbBE=",
+ "requires": {
+ "big-rat": "^1.0.3",
+ "box-intersect": "^1.0.1",
+ "nextafter": "^1.0.0",
+ "rat-vec": "^1.1.1",
+ "robust-segment-intersect": "^1.0.1",
+ "union-find": "^1.0.2",
+ "uniq": "^1.0.1"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
+ "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
+ "requires": {
+ "center-align": "^0.1.1",
+ "right-align": "^0.1.1",
+ "wordwrap": "0.0.2"
+ },
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
+ "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8="
+ }
+ }
+ },
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
+ "dev": true
+ },
+ "clsx": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.0.4.tgz",
+ "integrity": "sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg=="
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "coa": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz",
+ "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=",
+ "dev": true,
+ "requires": {
+ "q": "^1.1.2"
+ }
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
+ "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
+ "dev": true,
+ "requires": {
+ "clone": "^1.0.2",
+ "color-convert": "^1.3.0",
+ "color-string": "^0.3.0"
+ }
+ },
+ "color-alpha": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/color-alpha/-/color-alpha-1.0.4.tgz",
+ "integrity": "sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A==",
+ "requires": {
+ "color-parse": "^1.3.8"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "requires": {
+ "color-name": "1.1.3"
+ },
+ "dependencies": {
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ }
+ }
+ },
+ "color-id": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/color-id/-/color-id-1.1.0.tgz",
+ "integrity": "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g==",
+ "requires": {
+ "clamp": "^1.0.1"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "color-normalize": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/color-normalize/-/color-normalize-1.5.0.tgz",
+ "integrity": "sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw==",
+ "requires": {
+ "clamp": "^1.0.1",
+ "color-rgba": "^2.1.1",
+ "dtype": "^2.0.0"
+ }
+ },
+ "color-parse": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/color-parse/-/color-parse-1.3.8.tgz",
+ "integrity": "sha512-1Y79qFv0n1xair3lNMTNeoFvmc3nirMVBij24zbs1f13+7fPpQClMg5b4AuKXLt3szj7BRlHMCXHplkce6XlmA==",
+ "requires": {
+ "color-name": "^1.0.0",
+ "defined": "^1.0.0",
+ "is-plain-obj": "^1.1.0"
+ }
+ },
+ "color-rgba": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/color-rgba/-/color-rgba-2.1.1.tgz",
+ "integrity": "sha512-VaX97wsqrMwLSOR6H7rU1Doa2zyVdmShabKrPEIFywLlHoibgD3QW9Dw6fSqM4+H/LfjprDNAUUW31qEQcGzNw==",
+ "requires": {
+ "clamp": "^1.0.1",
+ "color-parse": "^1.3.8",
+ "color-space": "^1.14.6"
+ }
+ },
+ "color-space": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/color-space/-/color-space-1.16.0.tgz",
+ "integrity": "sha512-A6WMiFzunQ8KEPFmj02OnnoUnqhmSaHaZ/0LVFcPTdlvm8+3aMJ5x1HRHy3bDHPkovkf4sS0f4wsVvwk71fKkg==",
+ "requires": {
+ "hsluv": "^0.0.3",
+ "mumath": "^3.3.4"
+ }
+ },
+ "color-string": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz",
+ "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.0.0"
+ }
+ },
+ "colormap": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/colormap/-/colormap-2.3.1.tgz",
+ "integrity": "sha512-TEzNlo/qYp6pBoR2SK9JiV+DG1cmUcVO/+DEJqVPSHIKNlWh5L5L4FYog7b/h0bAnhKhpOAvx/c1dFp2QE9sFw==",
+ "requires": {
+ "lerp": "^1.0.3"
+ }
+ },
+ "colormin": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz",
+ "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=",
+ "dev": true,
+ "requires": {
+ "color": "^0.11.0",
+ "css-color-names": "0.0.4",
+ "has": "^1.0.1"
+ }
+ },
+ "colors": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
+ "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
+ "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
+ "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+ "dev": true
+ },
+ "compare-angle": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/compare-angle/-/compare-angle-1.0.1.tgz",
+ "integrity": "sha1-pOtjQW6jx0f8a9bItjZotN5PoSk=",
+ "requires": {
+ "robust-orientation": "^1.0.2",
+ "robust-product": "^1.0.0",
+ "robust-sum": "^1.0.0",
+ "signum": "^0.0.0",
+ "two-sum": "^1.0.0"
+ }
+ },
+ "compare-cell": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/compare-cell/-/compare-cell-1.0.0.tgz",
+ "integrity": "sha1-qetwj24OQa73qlZrEw8ZaNyeGqo="
+ },
+ "compare-oriented-cell": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/compare-oriented-cell/-/compare-oriented-cell-1.0.1.tgz",
+ "integrity": "sha1-ahSf7vnfxPj8YjWOUd1C7/u9w54=",
+ "requires": {
+ "cell-orientation": "^1.0.1",
+ "compare-cell": "^1.0.0"
+ }
+ },
+ "compare-versions": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.4.0.tgz",
+ "integrity": "sha512-tK69D7oNXXqUW3ZNo/z7NXTEz22TCF0pTE+YF9cxvaAM9XnkLo1fV621xCLrRR6aevJlKxExkss0vWqUCUpqdg==",
+ "dev": true
+ },
+ "component-classes": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz",
+ "integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=",
+ "requires": {
+ "component-indexof": "0.0.3"
+ }
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "component-indexof": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz",
+ "integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ="
+ },
+ "compressible": {
+ "version": "2.0.16",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.16.tgz",
+ "integrity": "sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA==",
+ "dev": true,
+ "requires": {
+ "mime-db": ">= 1.38.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.3.tgz",
+ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.14",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.1",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "compute-dims": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/compute-dims/-/compute-dims-1.1.0.tgz",
+ "integrity": "sha512-YHMiIKjH/8Eom8zATk3g8/lH3HxGCZcVQyEfEoVrfWI7od/WRpTgRGShnei3jArYSx77mQqPxZNokjGHCdLfxg==",
+ "requires": {
+ "utils-copy": "^1.0.0",
+ "validate.io-array": "^1.0.6",
+ "validate.io-matrix-like": "^1.0.2",
+ "validate.io-ndarray-like": "^1.0.0",
+ "validate.io-positive-integer": "^1.0.0"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "confusing-browser-globals": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz",
+ "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==",
+ "dev": true
+ },
+ "connect-history-api-fallback": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+ "dev": true
+ },
+ "console-browserify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true,
+ "requires": {
+ "date-now": "^0.1.4"
+ }
+ },
+ "const-max-uint32": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/const-max-uint32/-/const-max-uint32-1.0.2.tgz",
+ "integrity": "sha1-8Am7YjDmeO2HTdLWqc2ePL+rtnY="
+ },
+ "const-pinf-float64": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/const-pinf-float64/-/const-pinf-float64-1.0.0.tgz",
+ "integrity": "sha1-9u+w15+cCYbT558pI6v5twtj1yY="
+ },
+ "constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+ "dev": true
+ },
+ "contains-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "dev": true
+ },
+ "content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
+ "dev": true
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
+ "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "convex-hull": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/convex-hull/-/convex-hull-1.0.3.tgz",
+ "integrity": "sha1-IKOqbOh/St6i/30XlxyfwcZ+H/8=",
+ "requires": {
+ "affine-hull": "^1.0.0",
+ "incremental-convex-hull": "^1.0.1",
+ "monotone-convex-hull-2d": "^1.0.1"
+ }
+ },
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+ "dev": true
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+ "dev": true
+ },
+ "copy-concurrently": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1",
+ "fs-write-stream-atomic": "^1.0.8",
+ "iferr": "^0.1.5",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.0"
+ }
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "copy-to-clipboard": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz",
+ "integrity": "sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==",
+ "requires": {
+ "toggle-selection": "^1.0.6"
+ }
+ },
+ "copy-webpack-plugin": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz",
+ "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==",
+ "dev": true,
+ "requires": {
+ "cacache": "^10.0.4",
+ "find-cache-dir": "^1.0.0",
+ "globby": "^7.1.1",
+ "is-glob": "^4.0.0",
+ "loader-utils": "^1.1.0",
+ "minimatch": "^3.0.4",
+ "p-limit": "^1.0.0",
+ "serialize-javascript": "^1.4.0"
+ },
+ "dependencies": {
+ "find-cache-dir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz",
+ "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^1.0.0",
+ "pkg-dir": "^2.0.0"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ }
+ }
+ },
+ "core-js": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.4.tgz",
+ "integrity": "sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A=="
+ },
+ "core-js-pure": {
+ "version": "3.4.7",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.4.7.tgz",
+ "integrity": "sha512-Am3uRS8WCdTFA3lP7LtKR0PxgqYzjAMGKXaZKSNSC/8sqU0Wfq8R/YzoRs2rqtOVEunfgH+0q3O0BKOg0AvjPw==",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "country-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/country-regex/-/country-regex-1.1.0.tgz",
+ "integrity": "sha1-UcMz3N8Sknt+XuucEKyBEqYSCJY="
+ },
+ "create-ecdh": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
+ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.0.0"
+ }
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "create-react-class": {
+ "version": "15.6.3",
+ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz",
+ "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==",
+ "requires": {
+ "fbjs": "^0.8.9",
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "requires": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ }
+ },
+ "css-animation": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/css-animation/-/css-animation-1.6.1.tgz",
+ "integrity": "sha512-/48+/BaEaHRY6kNQ2OIPzKf9A6g8WjZYjhiNDNuIVbsm5tXCGIAsHDjB4Xu1C4vXJtUWZo26O68OQkDpNBaPog==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "component-classes": "^1.2.5"
+ }
+ },
+ "css-color-names": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+ "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
+ "dev": true
+ },
+ "css-font": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/css-font/-/css-font-1.2.0.tgz",
+ "integrity": "sha512-V4U4Wps4dPDACJ4WpgofJ2RT5Yqwe1lEH6wlOOaIxMi0gTjdIijsc5FmxQlZ7ZZyKQkkutqqvULOp07l9c7ssA==",
+ "requires": {
+ "css-font-size-keywords": "^1.0.0",
+ "css-font-stretch-keywords": "^1.0.1",
+ "css-font-style-keywords": "^1.0.1",
+ "css-font-weight-keywords": "^1.0.0",
+ "css-global-keywords": "^1.0.1",
+ "css-system-font-keywords": "^1.0.0",
+ "pick-by-alias": "^1.2.0",
+ "string-split-by": "^1.0.0",
+ "unquote": "^1.1.0"
+ }
+ },
+ "css-font-size-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-font-size-keywords/-/css-font-size-keywords-1.0.0.tgz",
+ "integrity": "sha1-hUh1rOmspqjS7g00WkSq6btttss="
+ },
+ "css-font-stretch-keywords": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/css-font-stretch-keywords/-/css-font-stretch-keywords-1.0.1.tgz",
+ "integrity": "sha1-UM7puboDH7XJUtRyMTnx4Qe1SxA="
+ },
+ "css-font-style-keywords": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/css-font-style-keywords/-/css-font-style-keywords-1.0.1.tgz",
+ "integrity": "sha1-XDUygT9jtKHelU0TzqhqtDM0CeQ="
+ },
+ "css-font-weight-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-font-weight-keywords/-/css-font-weight-keywords-1.0.0.tgz",
+ "integrity": "sha1-m8BGcayFvHJLV07106yWsNYE/Zc="
+ },
+ "css-global-keywords": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/css-global-keywords/-/css-global-keywords-1.0.1.tgz",
+ "integrity": "sha1-cqmupyeW0Bmx0qMlLeTlqqN+Smk="
+ },
+ "css-loader": {
+ "version": "0.28.11",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz",
+ "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==",
+ "dev": true,
+ "requires": {
+ "babel-code-frame": "^6.26.0",
+ "css-selector-tokenizer": "^0.7.0",
+ "cssnano": "^3.10.0",
+ "icss-utils": "^2.1.0",
+ "loader-utils": "^1.0.2",
+ "lodash.camelcase": "^4.3.0",
+ "object-assign": "^4.1.1",
+ "postcss": "^5.0.6",
+ "postcss-modules-extract-imports": "^1.2.0",
+ "postcss-modules-local-by-default": "^1.2.0",
+ "postcss-modules-scope": "^1.1.0",
+ "postcss-modules-values": "^1.3.0",
+ "postcss-value-parser": "^3.3.0",
+ "source-list-map": "^2.0.0"
+ }
+ },
+ "css-select": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
+ "dev": true,
+ "requires": {
+ "boolbase": "~1.0.0",
+ "css-what": "2.1",
+ "domutils": "1.5.1",
+ "nth-check": "~1.0.1"
+ }
+ },
+ "css-selector-tokenizer": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz",
+ "integrity": "sha512-xYL0AMZJ4gFzJQsHUKa5jiWWi2vH77WVNg7JYRyewwj6oPh4yb/y6Y9ZCw9dsj/9UauMhtuxR+ogQd//EdEVNA==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^0.1.0",
+ "fastparse": "^1.1.1",
+ "regexpu-core": "^1.0.0"
+ },
+ "dependencies": {
+ "regexpu-core": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz",
+ "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.2.1",
+ "regjsgen": "^0.2.0",
+ "regjsparser": "^0.1.4"
+ }
+ },
+ "regjsgen": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+ "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ }
+ }
+ }
+ },
+ "css-system-font-keywords": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz",
+ "integrity": "sha1-hcbwhquk6zLFcaMIav/ENLhII+0="
+ },
+ "css-what": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
+ "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
+ "dev": true
+ },
+ "csscolorparser": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz",
+ "integrity": "sha1-s085HupNqPPpgjHizNjfnAQfFxs="
+ },
+ "cssesc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
+ "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=",
+ "dev": true
+ },
+ "cssnano": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz",
+ "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=",
+ "dev": true,
+ "requires": {
+ "autoprefixer": "^6.3.1",
+ "decamelize": "^1.1.2",
+ "defined": "^1.0.0",
+ "has": "^1.0.1",
+ "object-assign": "^4.0.1",
+ "postcss": "^5.0.14",
+ "postcss-calc": "^5.2.0",
+ "postcss-colormin": "^2.1.8",
+ "postcss-convert-values": "^2.3.4",
+ "postcss-discard-comments": "^2.0.4",
+ "postcss-discard-duplicates": "^2.0.1",
+ "postcss-discard-empty": "^2.0.1",
+ "postcss-discard-overridden": "^0.1.1",
+ "postcss-discard-unused": "^2.2.1",
+ "postcss-filter-plugins": "^2.0.0",
+ "postcss-merge-idents": "^2.1.5",
+ "postcss-merge-longhand": "^2.0.1",
+ "postcss-merge-rules": "^2.0.3",
+ "postcss-minify-font-values": "^1.0.2",
+ "postcss-minify-gradients": "^1.0.1",
+ "postcss-minify-params": "^1.0.4",
+ "postcss-minify-selectors": "^2.0.4",
+ "postcss-normalize-charset": "^1.1.0",
+ "postcss-normalize-url": "^3.0.7",
+ "postcss-ordered-values": "^2.1.0",
+ "postcss-reduce-idents": "^2.2.2",
+ "postcss-reduce-initial": "^1.0.0",
+ "postcss-reduce-transforms": "^1.0.3",
+ "postcss-svgo": "^2.1.1",
+ "postcss-unique-selectors": "^2.0.2",
+ "postcss-value-parser": "^3.2.3",
+ "postcss-zindex": "^2.0.1"
+ }
+ },
+ "csso": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz",
+ "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=",
+ "dev": true,
+ "requires": {
+ "clap": "^1.0.9",
+ "source-map": "^0.5.3"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "cssom": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.6.tgz",
+ "integrity": "sha512-DtUeseGk9/GBW0hl0vVPpU22iHL6YB5BUX7ml1hB+GMpo0NX5G4voX3kdWiMSEguFtcW3Vh3djqNF4aIe6ne0A==",
+ "dev": true
+ },
+ "cssstyle": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.2.1.tgz",
+ "integrity": "sha512-7DYm8qe+gPx/h77QlCyFmX80+fGaE/6A/Ekl0zaszYOubvySO2saYFdQ78P29D0UsULxFKCetDGNaNRUdSF+2A==",
+ "dev": true,
+ "requires": {
+ "cssom": "0.3.x"
+ }
+ },
+ "csstype": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.2.tgz",
+ "integrity": "sha512-Rl7PvTae0pflc1YtxtKbiSqq20Ts6vpIYOD5WBafl4y123DyHUeLrRdQP66sQW8/6gmX8jrYJLXwNeMqYVJcow=="
+ },
+ "cubic-hermite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/cubic-hermite/-/cubic-hermite-1.0.0.tgz",
+ "integrity": "sha1-hOOy8nKzFFToOTuZu2rtRRaMFOU="
+ },
+ "cwise": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/cwise/-/cwise-1.0.10.tgz",
+ "integrity": "sha1-JO7mBy69/WuMb12tsXCQtkmxK+8=",
+ "requires": {
+ "cwise-compiler": "^1.1.1",
+ "cwise-parser": "^1.0.0",
+ "static-module": "^1.0.0",
+ "uglify-js": "^2.6.0"
+ }
+ },
+ "cwise-compiler": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz",
+ "integrity": "sha1-9NZnQQ6FDToxOn0tt7HlBbsDTMU=",
+ "requires": {
+ "uniq": "^1.0.0"
+ }
+ },
+ "cwise-parser": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/cwise-parser/-/cwise-parser-1.0.3.tgz",
+ "integrity": "sha1-jkk8F9VPl8sDCp6YVLyGyd+zVP4=",
+ "requires": {
+ "esprima": "^1.0.3",
+ "uniq": "^1.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz",
+ "integrity": "sha1-CZNQL+r2aBODJXVvMPmlH+7sEek="
+ }
+ }
+ },
+ "cyclist": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
+ "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
+ "dev": true
+ },
+ "d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "requires": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
+ "d3": {
+ "version": "3.5.17",
+ "resolved": "https://registry.npmjs.org/d3/-/d3-3.5.17.tgz",
+ "integrity": "sha1-vEZ0gAQ3iyGjYMn8fPUjF5B2L7g="
+ },
+ "d3-array": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz",
+ "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="
+ },
+ "d3-cloud": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/d3-cloud/-/d3-cloud-1.2.5.tgz",
+ "integrity": "sha512-4s2hXZgvs0CoUIw31oBAGrHt9Kt/7P9Ik5HIVzISFiWkD0Ga2VLAuO/emO/z1tYIpE7KG2smB4PhMPfFMJpahw==",
+ "requires": {
+ "d3-dispatch": "^1.0.3"
+ }
+ },
+ "d3-collection": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz",
+ "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="
+ },
+ "d3-color": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz",
+ "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q=="
+ },
+ "d3-dispatch": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz",
+ "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA=="
+ },
+ "d3-force": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz",
+ "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==",
+ "requires": {
+ "d3-collection": "1",
+ "d3-dispatch": "1",
+ "d3-quadtree": "1",
+ "d3-timer": "1"
+ }
+ },
+ "d3-hierarchy": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz",
+ "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ=="
+ },
+ "d3-interpolate": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz",
+ "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==",
+ "requires": {
+ "d3-color": "1"
+ }
+ },
+ "d3-path": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
+ "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
+ },
+ "d3-quadtree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz",
+ "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA=="
+ },
+ "d3-shape": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
+ "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
+ "requires": {
+ "d3-path": "1"
+ }
+ },
+ "d3-timer": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz",
+ "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw=="
+ },
+ "damerau-levenshtein": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.5.tgz",
+ "integrity": "sha512-CBCRqFnpu715iPmw1KrdOrzRqbdFwQTwAWyyyYS42+iAgHCuXZ+/TdMgQkUENPomxEz9z1BEzuQU2Xw0kUuAgA==",
+ "dev": true
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "data-urls": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+ "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.0",
+ "whatwg-mimetype": "^2.2.0",
+ "whatwg-url": "^7.0.0"
+ },
+ "dependencies": {
+ "whatwg-url": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz",
+ "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==",
+ "dev": true,
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ }
+ }
+ },
+ "date-now": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
+ },
+ "deep-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
+ "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
+ "dev": true
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
+ },
+ "default-gateway": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.1.2.tgz",
+ "integrity": "sha512-xhJUAp3u02JsBGovj0V6B6uYhKCUOmiNc8xGmReUwGu77NmvcpxPVB0pCielxMFumO7CmXBG02XjM8HB97k8Hw==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "ip-regex": "^2.1.0"
+ }
+ },
+ "default-require-extensions": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz",
+ "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=",
+ "dev": true,
+ "requires": {
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ }
+ }
+ },
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
+ },
+ "del": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz",
+ "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=",
+ "dev": true,
+ "requires": {
+ "globby": "^6.1.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "p-map": "^1.1.1",
+ "pify": "^3.0.0",
+ "rimraf": "^2.2.8"
+ },
+ "dependencies": {
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "delaunay-triangulate": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/delaunay-triangulate/-/delaunay-triangulate-1.1.6.tgz",
+ "integrity": "sha1-W7yiGweBmNS8PHV5ajXLuYwllUw=",
+ "requires": {
+ "incremental-convex-hull": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "des.js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+ "dev": true
+ },
+ "detect-file": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+ "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+ "dev": true
+ },
+ "detect-kerning": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-kerning/-/detect-kerning-2.1.2.tgz",
+ "integrity": "sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw=="
+ },
+ "detect-newline": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz",
+ "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
+ "dev": true
+ },
+ "detect-node": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
+ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
+ "dev": true
+ },
+ "diff-match-patch": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
+ "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="
+ },
+ "diff-sequences": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.0.0.tgz",
+ "integrity": "sha512-46OkIuVGBBnrC0soO/4LHu5LHGHx0uhP65OVz8XOrAJpqiCB2aVIuESvjI1F9oqebuvY8lekS1pt6TN7vt7qsw==",
+ "dev": true
+ },
+ "diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "dir-glob": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
+ "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==",
+ "dev": true,
+ "requires": {
+ "path-type": "^3.0.0"
+ }
+ },
+ "discontinuous-range": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+ "integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
+ "dev": true
+ },
+ "dns-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+ "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+ "dev": true
+ },
+ "dns-packet": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
+ "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
+ "dev": true,
+ "requires": {
+ "ip": "^1.1.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "dns-txt": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+ "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+ "dev": true,
+ "requires": {
+ "buffer-indexof": "^1.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dom-align": {
+ "version": "1.12.0",
+ "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.0.tgz",
+ "integrity": "sha512-YkoezQuhp3SLFGdOlr5xkqZ640iXrnHAwVYcDg8ZKRUtO7mSzSC2BA5V0VuyAwPSJA4CLIc6EDDJh4bEsD2+zA=="
+ },
+ "dom-closest": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz",
+ "integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=",
+ "requires": {
+ "dom-matches": ">=1.0.1"
+ }
+ },
+ "dom-converter": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
+ "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
+ "dev": true,
+ "requires": {
+ "utila": "~0.4"
+ }
+ },
+ "dom-helpers": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
+ "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
+ "requires": {
+ "@babel/runtime": "^7.6.3",
+ "csstype": "^2.6.7"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.7.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.6.tgz",
+ "integrity": "sha512-BWAJxpNVa0QlE5gZdWjSxXtemZyZ9RmrmVozxt3NUXeZhVIJ5ANyqmMc0JDrivBZyxUuQvFxlvH4OWWOogGfUw==",
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ }
+ },
+ "csstype": {
+ "version": "2.6.7",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
+ "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ=="
+ },
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
+ }
+ }
+ },
+ "dom-matches": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz",
+ "integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw="
+ },
+ "dom-scroll-into-view": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz",
+ "integrity": "sha1-6PNnMt0ImwIBqI14Fdw/iObWbH4="
+ },
+ "dom-serializer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
+ "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.0",
+ "entities": "^1.1.1"
+ }
+ },
+ "domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domexception": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+ "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+ "dev": true,
+ "requires": {
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "domhandler": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
+ "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "1"
+ }
+ },
+ "dompurify": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.7.tgz",
+ "integrity": "sha512-S3O0lk6rFJtO01ZTzMollCOGg+WAtCwS3U5E2WSDY/x/sy7q70RjEC4Dmrih5/UqzLLB9XoKJ8KqwBxaNvBu4A=="
+ },
+ "domutils": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "dotignore": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz",
+ "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==",
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "double-bits": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/double-bits/-/double-bits-1.1.1.tgz",
+ "integrity": "sha1-WKu6RUlNpND6Nrc60RoobJGEscY="
+ },
+ "draft-js": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/draft-js/-/draft-js-0.10.5.tgz",
+ "integrity": "sha512-LE6jSCV9nkPhfVX2ggcRLA4FKs6zWq9ceuO/88BpXdNCS7mjRTgs0NsV6piUCJX9YxMsB9An33wnkMmU2sD2Zg==",
+ "requires": {
+ "fbjs": "^0.8.15",
+ "immutable": "~3.7.4",
+ "object-assign": "^4.1.0"
+ }
+ },
+ "draw-svg-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/draw-svg-path/-/draw-svg-path-1.0.0.tgz",
+ "integrity": "sha1-bxFtli3TFLmepTTW9Y3WbNvWk3k=",
+ "requires": {
+ "abs-svg-path": "~0.1.1",
+ "normalize-svg-path": "~0.1.0"
+ }
+ },
+ "dtype": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dtype/-/dtype-2.0.0.tgz",
+ "integrity": "sha1-zQUjI84GFETs0uj1dI9popvihDQ="
+ },
+ "dup": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dup/-/dup-1.0.0.tgz",
+ "integrity": "sha1-UfxaxoX4GWRp3wuQXpNLIK9bQCk="
+ },
+ "duplexer": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+ "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
+ "dev": true
+ },
+ "duplexer2": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz",
+ "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=",
+ "requires": {
+ "readable-stream": "~1.1.9"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ }
+ }
+ },
+ "duplexify": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+ "requires": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "earcut": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.2.tgz",
+ "integrity": "sha512-eZoZPPJcUHnfRZ0PjLvx2qBordSiO8ofC3vt+qACLM95u+4DovnbYNpQtJh0DNsWj8RnxrQytD4WA8gj5cRIaQ=="
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "edges-to-adjacency-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/edges-to-adjacency-list/-/edges-to-adjacency-list-1.0.0.tgz",
+ "integrity": "sha1-wUbS4ISt37p0pRKTxuAZmkn3V/E=",
+ "requires": {
+ "uniq": "^1.0.0"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "ejs": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz",
+ "integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.3.113",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.113.tgz",
+ "integrity": "sha512-De+lPAxEcpxvqPTyZAXELNpRZXABRxf+uL/rSykstQhzj/B0l1150G/ExIIxKc16lI89Hgz81J0BHAcbTqK49g==",
+ "dev": true
+ },
+ "element-size": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/element-size/-/element-size-1.1.1.tgz",
+ "integrity": "sha1-ZOXxWdlxIWMYRby67K8nnDm1404="
+ },
+ "elementary-circuits-directed-graph": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/elementary-circuits-directed-graph/-/elementary-circuits-directed-graph-1.2.0.tgz",
+ "integrity": "sha512-eOQofnrNqebPtC29PvyNMGUBdMrIw5i8nOoC/2VOlSF84tf5+ZXnRkIk7TgdT22jFXK68CC7aA881KRmNYf/Pg==",
+ "requires": {
+ "strongly-connected-components": "^1.0.1"
+ }
+ },
+ "elliptic": {
+ "version": "6.4.1",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
+ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.4.0",
+ "brorand": "^1.0.1",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.0"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "encoding": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "requires": {
+ "iconv-lite": "~0.4.13"
+ }
+ },
+ "end-of-stream": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
+ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "enquire.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/enquire.js/-/enquire.js-2.1.6.tgz",
+ "integrity": "sha1-PoeAybi4NQhMP2DhZtvDwqPImBQ="
+ },
+ "entities": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
+ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
+ "dev": true
+ },
+ "enzyme": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.9.0.tgz",
+ "integrity": "sha512-JqxI2BRFHbmiP7/UFqvsjxTirWoM1HfeaJrmVSZ9a1EADKkZgdPcAuISPMpoUiHlac9J4dYt81MC5BBIrbJGMg==",
+ "dev": true,
+ "requires": {
+ "array.prototype.flat": "^1.2.1",
+ "cheerio": "^1.0.0-rc.2",
+ "function.prototype.name": "^1.1.0",
+ "has": "^1.0.3",
+ "html-element-map": "^1.0.0",
+ "is-boolean-object": "^1.0.0",
+ "is-callable": "^1.1.4",
+ "is-number-object": "^1.0.3",
+ "is-regex": "^1.0.4",
+ "is-string": "^1.0.4",
+ "is-subset": "^0.1.1",
+ "lodash.escape": "^4.0.1",
+ "lodash.isequal": "^4.5.0",
+ "object-inspect": "^1.6.0",
+ "object-is": "^1.0.1",
+ "object.assign": "^4.1.0",
+ "object.entries": "^1.0.4",
+ "object.values": "^1.0.4",
+ "raf": "^3.4.0",
+ "rst-selector-parser": "^2.2.3",
+ "string.prototype.trim": "^1.1.2"
+ }
+ },
+ "enzyme-adapter-react-16": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.10.0.tgz",
+ "integrity": "sha512-0QqwEZcBv1xEEla+a3H7FMci+y4ybLia9cZzsdIrId7qcig4MK0kqqf6iiCILH1lsKS6c6AVqL3wGPhCevv5aQ==",
+ "dev": true,
+ "requires": {
+ "enzyme-adapter-utils": "^1.10.0",
+ "object.assign": "^4.1.0",
+ "object.values": "^1.1.0",
+ "prop-types": "^15.6.2",
+ "react-is": "^16.7.0",
+ "react-test-renderer": "^16.0.0-0"
+ }
+ },
+ "enzyme-adapter-utils": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz",
+ "integrity": "sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA==",
+ "dev": true,
+ "requires": {
+ "function.prototype.name": "^1.1.0",
+ "object.assign": "^4.1.0",
+ "object.fromentries": "^2.0.0",
+ "prop-types": "^15.6.2",
+ "semver": "^5.6.0"
+ }
+ },
+ "enzyme-to-json": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/enzyme-to-json/-/enzyme-to-json-3.3.5.tgz",
+ "integrity": "sha512-DmH1wJ68HyPqKSYXdQqB33ZotwfUhwQZW3IGXaNXgR69Iodaoj8TF/D9RjLdz4pEhGq2Tx2zwNUIjBuqoZeTgA==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.4"
+ }
+ },
+ "errno": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+ "dev": true,
+ "requires": {
+ "prr": "~1.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz",
+ "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "is-callable": "^1.1.4",
+ "is-regex": "^1.0.4",
+ "object-keys": "^1.0.12"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
+ "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "es5-ext": {
+ "version": "0.10.53",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+ "requires": {
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.3",
+ "next-tick": "~1.0.0"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "es6-promise": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
+ "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM="
+ },
+ "es6-symbol": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+ "requires": {
+ "d": "^1.0.1",
+ "ext": "^1.1.2"
+ }
+ },
+ "es6-weak-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+ "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.46",
+ "es6-iterator": "^2.0.3",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
+ "escodegen": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.1.tgz",
+ "integrity": "sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw==",
+ "requires": {
+ "esprima": "^3.1.3",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ }
+ },
+ "eslint": {
+ "version": "6.7.2",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.7.2.tgz",
+ "integrity": "sha512-qMlSWJaCSxDFr8fBPvJM9kJwbazrhNcBU3+DszDW1OlEwKBBRWsJc7NJFelvwQpanHCR14cOLD41x8Eqvo3Nng==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.10.0",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^3.0.0",
+ "eslint-scope": "^5.0.0",
+ "eslint-utils": "^1.4.3",
+ "eslint-visitor-keys": "^1.1.0",
+ "espree": "^6.1.2",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^5.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^5.0.0",
+ "globals": "^12.1.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^7.0.0",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^3.13.1",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.14",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.3",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "semver": "^6.1.2",
+ "strip-ansi": "^5.2.0",
+ "strip-json-comments": "^3.0.1",
+ "table": "^5.2.3",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+ "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "glob-parent": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
+ "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ }
+ }
+ },
+ "globals": {
+ "version": "12.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz",
+ "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "regexpp": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "v8-compile-cache": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz",
+ "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-config-prettier": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.7.0.tgz",
+ "integrity": "sha512-FamQVKM3jjUVwhG4hEMnbtsq7xOIDm+SY5iBPfR8gKsJoAB2IQnNF+bk1+8Fy44Nq7PPJaLvkRxILYdJWoguKQ==",
+ "dev": true,
+ "requires": {
+ "get-stdin": "^6.0.0"
+ }
+ },
+ "eslint-config-react-app": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-5.1.0.tgz",
+ "integrity": "sha512-hBaxisHC6HXRVvxX+/t1n8mOdmCVIKgkXsf2WoUkJi7upHJTwYTsdCmx01QPOjKNT34QMQQ9sL0tVBlbiMFjxA==",
+ "dev": true,
+ "requires": {
+ "confusing-browser-globals": "^1.0.9"
+ }
+ },
+ "eslint-import-resolver-node": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz",
+ "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.9",
+ "resolve": "^1.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-loader": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-3.0.3.tgz",
+ "integrity": "sha512-+YRqB95PnNvxNp1HEjQmvf9KNvCin5HXYYseOXVC2U0KEcw4IkQ2IQEBG46j7+gW39bMzeu0GsUhVbBY3Votpw==",
+ "dev": true,
+ "requires": {
+ "fs-extra": "^8.1.0",
+ "loader-fs-cache": "^1.0.2",
+ "loader-utils": "^1.2.3",
+ "object-hash": "^2.0.1",
+ "schema-utils": "^2.6.1"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+ "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-keywords": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
+ "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz",
+ "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "ajv-keywords": "^3.4.1"
+ }
+ }
+ }
+ },
+ "eslint-module-utils": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.4.1.tgz",
+ "integrity": "sha512-H6DOj+ejw7Tesdgbfs4jeS4YMFrT8uI8xwd1gtQqXssaR0EQ26L+2O/w6wkYFy2MymON0fTwHmXBvvfLNZVZEw==",
+ "dev": true,
+ "requires": {
+ "debug": "^2.6.8",
+ "pkg-dir": "^2.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
+ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.1.0"
+ }
+ }
+ }
+ },
+ "eslint-plugin-chai-friendly": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-chai-friendly/-/eslint-plugin-chai-friendly-0.5.0.tgz",
+ "integrity": "sha512-Pxe6z8C9fP0pn2X2nGFU/b3GBOCM/5FVus1hsMwJsXP3R7RiXFl7g0ksJbsc0GxiLyidTW4mEFk77qsNn7Tk7g==",
+ "dev": true
+ },
+ "eslint-plugin-compat": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-compat/-/eslint-plugin-compat-3.3.0.tgz",
+ "integrity": "sha512-QCgYy3pZ+zH10dkBJus1xER0359h1UhJjufhQRqp9Owm6BEoLZeSqxf2zINwL1OGao9Yc96xPYIW3nQj5HUryg==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.4.5",
+ "ast-metadata-inferer": "^0.1.1",
+ "browserslist": "^4.6.3",
+ "caniuse-db": "^1.0.30000977",
+ "lodash.memoize": "4.1.2",
+ "mdn-browser-compat-data": "^0.0.84",
+ "semver": "^6.1.2"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
+ "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ }
+ },
+ "browserslist": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.8.1.tgz",
+ "integrity": "sha512-X/lIDboA5bvFg9SOhHN7OBgHHlaZQWcwTXBLBGgrB8+6Iy1dL0wmUfHRe7OdETRTuD2d6f5JPa7iTEcbVyVf6Q==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001015",
+ "electron-to-chromium": "^1.3.322",
+ "node-releases": "^1.1.42"
+ }
+ },
+ "caniuse-db": {
+ "version": "1.0.30001015",
+ "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30001015.tgz",
+ "integrity": "sha512-+Fqdu7tOxSD0HDs922QPxseHlwnUzdjLC1Oq32HZ1/LWrMfYNt8JaUWnoiIAR+G4fRzM3tdgc4Lrqo5Nx76UsQ==",
+ "dev": true
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001015",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001015.tgz",
+ "integrity": "sha512-/xL2AbW/XWHNu1gnIrO8UitBGoFthcsDgU9VLK1/dpsoxbaD5LscHozKze05R6WLsBvLhqv78dAPozMFQBYLbQ==",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.3.322",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.322.tgz",
+ "integrity": "sha512-Tc8JQEfGQ1MzfSzI/bTlSr7btJv/FFO7Yh6tanqVmIWOuNCu6/D1MilIEgLtmWqIrsv+o4IjpLAhgMBr/ncNAA==",
+ "dev": true
+ },
+ "node-releases": {
+ "version": "1.1.42",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.42.tgz",
+ "integrity": "sha512-OQ/ESmUqGawI2PRX+XIRao44qWYBBfN54ImQYdWVTQqUckuejOg76ysSqDBK8NG3zwySRVnX36JwDQ6x+9GxzA==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.3.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-cypress": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.2.1.tgz",
+ "integrity": "sha512-WkH81MEALKhnpeRo/wWHBHR883LdkS8aFzbGAGFxiwRwik2IKBZxb/JrxbiA6+SZskXGcmdEi6rwL7xmiqo9MA==",
+ "dev": true,
+ "requires": {
+ "globals": "^11.0.1"
+ }
+ },
+ "eslint-plugin-eslint-plugin": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-2.1.0.tgz",
+ "integrity": "sha512-kT3A/ZJftt28gbl/Cv04qezb/NQ1dwYIbi8lyf806XMxkus7DvOVCLIfTXMrorp322Pnoez7+zabXH29tADIDg==",
+ "dev": true
+ },
+ "eslint-plugin-flowtype": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-3.13.0.tgz",
+ "integrity": "sha512-bhewp36P+t7cEV0b6OdmoRWJCBYRiHFlqPZAG1oS3SF+Y0LQkeDvFSM4oxoxvczD1OdONCXMlJfQFiWLcV9urw==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.15"
+ },
+ "dependencies": {
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "2.18.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz",
+ "integrity": "sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.0.3",
+ "contains-path": "^0.1.0",
+ "debug": "^2.6.9",
+ "doctrine": "1.5.0",
+ "eslint-import-resolver-node": "^0.3.2",
+ "eslint-module-utils": "^2.4.0",
+ "has": "^1.0.3",
+ "minimatch": "^3.0.4",
+ "object.values": "^1.1.0",
+ "read-pkg-up": "^2.0.0",
+ "resolve": "^1.11.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2",
+ "isarray": "^1.0.0"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "load-json-file": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^2.2.0",
+ "pify": "^2.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.2.0"
+ }
+ },
+ "path-type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.0.0"
+ }
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^2.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^2.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "requires": {
+ "find-up": "^2.0.0",
+ "read-pkg": "^2.0.0"
+ }
+ },
+ "resolve": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz",
+ "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ }
+ }
+ },
+ "eslint-plugin-jest": {
+ "version": "22.3.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-22.3.0.tgz",
+ "integrity": "sha512-P1mYVRNlOEoO5T9yTqOfucjOYf1ktmJ26NjwjH8sxpCFQa6IhBGr5TpKl3hcAAT29hOsRJVuMWmTsHoUVo9FoA==",
+ "dev": true
+ },
+ "eslint-plugin-jsx-a11y": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz",
+ "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.4.5",
+ "aria-query": "^3.0.0",
+ "array-includes": "^3.0.3",
+ "ast-types-flow": "^0.0.7",
+ "axobject-query": "^2.0.2",
+ "damerau-levenshtein": "^1.0.4",
+ "emoji-regex": "^7.0.2",
+ "has": "^1.0.3",
+ "jsx-ast-utils": "^2.2.1"
+ },
+ "dependencies": {
+ "@babel/runtime": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
+ "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "regenerator-runtime": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
+ "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-no-only-tests": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-2.4.0.tgz",
+ "integrity": "sha512-azP9PwQYfGtXJjW273nIxQH9Ygr+5/UyeW2wEjYoDtVYPI+WPKwbj0+qcAKYUXFZLRumq4HKkFaoDBAwBoXImQ==",
+ "dev": true
+ },
+ "eslint-plugin-react": {
+ "version": "7.17.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.17.0.tgz",
+ "integrity": "sha512-ODB7yg6lxhBVMeiH1c7E95FLD4E/TwmFjltiU+ethv7KPdCwgiFuOZg9zNRHyufStTDLl/dEFqI2Q1VPmCd78A==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.0.3",
+ "doctrine": "^2.1.0",
+ "eslint-plugin-eslint-plugin": "^2.1.0",
+ "has": "^1.0.3",
+ "jsx-ast-utils": "^2.2.3",
+ "object.entries": "^1.1.0",
+ "object.fromentries": "^2.0.1",
+ "object.values": "^1.1.0",
+ "prop-types": "^15.7.2",
+ "resolve": "^1.13.1"
+ },
+ "dependencies": {
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "es-abstract": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.16.3.tgz",
+ "integrity": "sha512-WtY7Fx5LiOnSYgF5eg/1T+GONaGmpvpPdCpSnYij+U2gDTL0UPfWrhDw7b2IYb+9NQJsYpCA0wOQvZfsd6YwRw==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.1.4",
+ "is-regex": "^1.0.4",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "string.prototype.trimleft": "^2.1.0",
+ "string.prototype.trimright": "^2.1.0"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+ "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object.fromentries": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.1.tgz",
+ "integrity": "sha512-PUQv8Hbg3j2QX0IQYv3iAGCbGcu4yY4KQ92/dhA4sFSixBmSmp13UpDLs6jGK8rBtbmhNNIK99LD2k293jpiGA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.15.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
+ "resolve": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.13.1.tgz",
+ "integrity": "sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ }
+ }
+ },
+ "eslint-plugin-react-hooks": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz",
+ "integrity": "sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==",
+ "dev": true
+ },
+ "eslint-scope": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
+ "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+ "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
+ "dev": true,
+ "requires": {
+ "eslint-visitor-keys": "^1.1.0"
+ }
+ },
+ "eslint-visitor-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz",
+ "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==",
+ "dev": true
+ },
+ "espree": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz",
+ "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==",
+ "dev": true,
+ "requires": {
+ "acorn": "^7.1.0",
+ "acorn-jsx": "^5.1.0",
+ "eslint-visitor-keys": "^1.1.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.0.tgz",
+ "integrity": "sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==",
+ "dev": true
+ }
+ }
+ },
+ "esprima": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
+ },
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM="
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
+ "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==",
+ "dev": true
+ },
+ "eventlistener": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/eventlistener/-/eventlistener-0.0.1.tgz",
+ "integrity": "sha1-7Suqu4UiJ68rz4iRUscsY8pTLrg="
+ },
+ "events": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ="
+ },
+ "eventsource": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
+ "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
+ "dev": true,
+ "requires": {
+ "original": "^1.0.0"
+ }
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "exec-sh": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz",
+ "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==",
+ "dev": true,
+ "requires": {
+ "merge": "^1.2.0"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "expand-tilde": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+ "dev": true,
+ "requires": {
+ "homedir-polyfill": "^1.0.1"
+ }
+ },
+ "expect": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/expect/-/expect-24.1.0.tgz",
+ "integrity": "sha512-lVcAPhaYkQcIyMS+F8RVwzbm1jro20IG8OkvxQ6f1JfqhVZyyudCwYogQ7wnktlf14iF3ii7ArIUO/mqvrW9Gw==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "jest-get-type": "^24.0.0",
+ "jest-matcher-utils": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-regex-util": "^24.0.0"
+ }
+ },
+ "express": {
+ "version": "4.16.4",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
+ "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.18.3",
+ "content-disposition": "0.5.2",
+ "content-type": "~1.0.4",
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.1.1",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.4",
+ "qs": "6.5.2",
+ "range-parser": "~1.2.0",
+ "safe-buffer": "5.1.2",
+ "send": "0.16.2",
+ "serve-static": "1.13.2",
+ "setprototypeof": "1.1.0",
+ "statuses": "~1.4.0",
+ "type-is": "~1.6.16",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ }
+ }
+ },
+ "ext": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+ "requires": {
+ "type": "^2.0.0"
+ },
+ "dependencies": {
+ "type": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
+ "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow=="
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ }
+ }
+ },
+ "extract-frustum-planes": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/extract-frustum-planes/-/extract-frustum-planes-1.0.0.tgz",
+ "integrity": "sha1-l9VwP/BWTIw8aDjKxF+ee8UsnvU="
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "falafel": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/falafel/-/falafel-2.2.4.tgz",
+ "integrity": "sha512-0HXjo8XASWRmsS0X1EkhwEMZaD3Qvp7FfURwjLKjG1ghfRm/MGZl2r4cWUTv41KdNghTw4OUMmVtdGQp3+H+uQ==",
+ "requires": {
+ "acorn": "^7.1.1",
+ "foreach": "^2.0.5",
+ "isarray": "^2.0.1",
+ "object-keys": "^1.0.6"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
+ "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg=="
+ },
+ "isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
+ }
+ }
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-isnumeric": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/fast-isnumeric/-/fast-isnumeric-1.1.4.tgz",
+ "integrity": "sha512-1mM8qOr2LYz8zGaUdmiqRDiuue00Dxjgcb1NQR7TnhLVh6sQyngP9xvLo7Sl7LZpP/sk5eb+bcyWXw530NTBZw==",
+ "requires": {
+ "is-string-blank": "^1.0.1"
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
+ },
+ "fastparse": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
+ "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
+ "dev": true
+ },
+ "faye-websocket": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
+ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "fb-watchman": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz",
+ "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=",
+ "dev": true,
+ "requires": {
+ "bser": "^2.0.0"
+ }
+ },
+ "fbjs": {
+ "version": "0.8.17",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz",
+ "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=",
+ "requires": {
+ "core-js": "^1.0.0",
+ "isomorphic-fetch": "^2.1.1",
+ "loose-envify": "^1.0.0",
+ "object-assign": "^4.1.0",
+ "promise": "^7.1.1",
+ "setimmediate": "^1.0.5",
+ "ua-parser-js": "^0.7.18"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+ }
+ }
+ },
+ "figgy-pudding": {
+ "version": "3.5.1",
+ "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz",
+ "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
+ "dev": true
+ },
+ "file-entry-cache": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+ "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^2.0.1"
+ }
+ },
+ "file-loader": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-2.0.0.tgz",
+ "integrity": "sha512-YCsBfd1ZGCyonOKLxPiKPdu+8ld9HAaMEvJewzz+b2eTF7uL5Zm/HdBF6FjCrpCMRq25Mi0U1gl4pwn2TlH7hQ==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.0.2",
+ "schema-utils": "^1.0.0"
+ }
+ },
+ "fileset": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/fileset/-/fileset-2.0.3.tgz",
+ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=",
+ "dev": true,
+ "requires": {
+ "glob": "^7.0.3",
+ "minimatch": "^3.0.3"
+ }
+ },
+ "filesize": {
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.6.1.tgz",
+ "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ }
+ },
+ "filtered-vector": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/filtered-vector/-/filtered-vector-1.2.4.tgz",
+ "integrity": "sha1-VkU8A030MC0pPKjs3qw/kKvGeNM=",
+ "requires": {
+ "binary-search-bounds": "^1.0.0",
+ "cubic-hermite": "^1.0.0"
+ },
+ "dependencies": {
+ "binary-search-bounds": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz",
+ "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k="
+ }
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+ "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.2",
+ "statuses": "~1.4.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "find-cache-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz",
+ "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^1.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "findup-sync": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+ "dev": true,
+ "requires": {
+ "detect-file": "^1.0.0",
+ "is-glob": "^3.1.0",
+ "micromatch": "^3.0.4",
+ "resolve-dir": "^1.0.1"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "flat-cache": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+ "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+ "dev": true,
+ "requires": {
+ "flatted": "^2.0.0",
+ "rimraf": "2.6.3",
+ "write": "1.0.3"
+ }
+ },
+ "flatted": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
+ "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+ "dev": true
+ },
+ "flatten": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz",
+ "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=",
+ "dev": true
+ },
+ "flatten-vertex-data": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/flatten-vertex-data/-/flatten-vertex-data-1.0.2.tgz",
+ "integrity": "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw==",
+ "requires": {
+ "dtype": "^2.0.0"
+ }
+ },
+ "flip-pixels": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/flip-pixels/-/flip-pixels-1.0.2.tgz",
+ "integrity": "sha512-oXbJGbjDnfJRWPC7Va38EFhd+A8JWE5/hCiKcK8qjCdbLj9DTpsq6MEudwpRTH+V4qq+Jw7d3pUgQdSr3x3mTA=="
+ },
+ "flush-write-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.3.6"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "follow-redirects": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
+ "integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.6"
+ }
+ },
+ "font-atlas": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/font-atlas/-/font-atlas-2.1.0.tgz",
+ "integrity": "sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg==",
+ "requires": {
+ "css-font": "^1.0.0"
+ }
+ },
+ "font-awesome": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz",
+ "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM="
+ },
+ "font-measure": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/font-measure/-/font-measure-1.2.2.tgz",
+ "integrity": "sha512-mRLEpdrWzKe9hbfaF3Qpr06TAjquuBVP5cHy4b3hyeNdjc9i0PO6HniGsX5vjL5OWv7+Bd++NiooNpT/s8BvIA==",
+ "requires": {
+ "css-font": "^1.2.0"
+ }
+ },
+ "for-each": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
+ "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
+ "requires": {
+ "is-callable": "^1.1.3"
+ }
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "foreach": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
+ "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
+ "dev": true
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "dev": true
+ },
+ "from2": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+ "requires": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fs-write-stream-atomic": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "iferr": "^0.1.5",
+ "imurmurhash": "^0.1.4",
+ "readable-stream": "1 || 2"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "fsevents": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
+ "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
+ "optional": true,
+ "requires": {
+ "nan": "^2.12.1",
+ "node-pre-gyp": "^0.12.0"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true,
+ "optional": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true,
+ "optional": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "bundled": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true,
+ "optional": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true,
+ "optional": true
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.2.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "needle": {
+ "version": "2.3.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.12.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.6",
+ "bundled": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.4.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true,
+ "optional": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "bundled": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.8",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "bundled": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true,
+ "optional": true
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "bundled": true,
+ "optional": true
+ }
+ }
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
+ },
+ "function.prototype.name": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz",
+ "integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "is-callable": "^1.1.3"
+ }
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc="
+ },
+ "gamma": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/gamma/-/gamma-0.1.0.tgz",
+ "integrity": "sha1-MxVkNAO/J5BsqAqzfDbs6UQO8zA="
+ },
+ "geojson-vt": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
+ "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg=="
+ },
+ "get-caller-file": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+ "dev": true
+ },
+ "get-canvas-context": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/get-canvas-context/-/get-canvas-context-1.0.2.tgz",
+ "integrity": "sha1-1ue1C8TkyGNXzTnyJkeoS3NgHpM="
+ },
+ "get-stdin": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
+ "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+ "dev": true
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ },
+ "dependencies": {
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ }
+ }
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "gl-axes3d": {
+ "version": "1.5.3",
+ "resolved": "https://registry.npmjs.org/gl-axes3d/-/gl-axes3d-1.5.3.tgz",
+ "integrity": "sha512-KRYbguKQcDQ6PcB9g1pgqB8Ly4TY1DQODpPKiDTasyWJ8PxQk0t2Q7XoQQijNqvsguITCpVVCzNb5GVtIWiVlQ==",
+ "requires": {
+ "bit-twiddle": "^1.0.2",
+ "dup": "^1.0.0",
+ "extract-frustum-planes": "^1.0.0",
+ "gl-buffer": "^2.1.2",
+ "gl-mat4": "^1.2.0",
+ "gl-shader": "^4.2.1",
+ "gl-state": "^1.0.0",
+ "gl-vao": "^1.3.0",
+ "gl-vec4": "^1.0.1",
+ "glslify": "^7.0.0",
+ "robust-orientation": "^1.1.3",
+ "split-polygon": "^1.0.0",
+ "vectorize-text": "^3.2.1"
+ }
+ },
+ "gl-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/gl-buffer/-/gl-buffer-2.1.2.tgz",
+ "integrity": "sha1-LbjZwaVSf7oM25EonCBuiCuInNs=",
+ "requires": {
+ "ndarray": "^1.0.15",
+ "ndarray-ops": "^1.1.0",
+ "typedarray-pool": "^1.0.0"
+ }
+ },
+ "gl-cone3d": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/gl-cone3d/-/gl-cone3d-1.5.2.tgz",
+ "integrity": "sha512-1JNeHH4sUtUmDA4ZK7Om8/kShwb8IZVAsnxaaB7IPRJsNGciLj1sTpODrJGeMl41RNkex5kXD2SQFrzyEAR2Rw==",
+ "requires": {
+ "colormap": "^2.3.1",
+ "gl-buffer": "^2.1.2",
+ "gl-mat4": "^1.2.0",
+ "gl-shader": "^4.2.1",
+ "gl-texture2d": "^2.1.0",
+ "gl-vao": "^1.3.0",
+ "gl-vec3": "^1.1.3",
+ "glsl-inverse": "^1.0.0",
+ "glsl-out-of-range": "^1.0.4",
+ "glsl-specular-cook-torrance": "^2.0.1",
+ "glslify": "^7.0.0",
+ "ndarray": "^1.0.18"
+ }
+ },
+ "gl-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gl-constants/-/gl-constants-1.0.0.tgz",
+ "integrity": "sha1-WXpQTjZHUP9QJTqjX43qevSl0jM="
+ },
+ "gl-contour2d": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/gl-contour2d/-/gl-contour2d-1.1.7.tgz",
+ "integrity": "sha512-GdebvJ9DtT3pJDpoE+eU2q+Wo9S3MijPpPz5arZbhK85w2bARmpFpVfPaDlZqWkB644W3BlH8TVyvAo1KE4Bhw==",
+ "requires": {
+ "binary-search-bounds": "^2.0.4",
+ "cdt2d": "^1.0.0",
+ "clean-pslg": "^1.1.2",
+ "gl-buffer": "^2.1.2",
+ "gl-shader": "^4.2.1",
+ "glslify": "^7.0.0",
+ "iota-array": "^1.0.0",
+ "ndarray": "^1.0.18",
+ "surface-nets": "^1.0.2"
+ }
+ },
+ "gl-error3d": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/gl-error3d/-/gl-error3d-1.0.16.tgz",
+ "integrity": "sha512-TGJewnKSp7ZnqGgG3XCF9ldrDbxZrO+OWlx6oIet4OdOM//n8xJ5isArnIV/sdPJnFbhfoLxWrW9f5fxHFRQ1A==",
+ "requires": {
+ "gl-buffer": "^2.1.2",
+ "gl-shader": "^4.2.1",
+ "gl-vao": "^1.3.0",
+ "glsl-out-of-range": "^1.0.4",
+ "glslify": "^7.0.0"
+ }
+ },
+ "gl-fbo": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/gl-fbo/-/gl-fbo-2.0.5.tgz",
+ "integrity": "sha1-D6daSXz3h2lVMGkcjwSrtvtV+iI=",
+ "requires": {
+ "gl-texture2d": "^2.0.0"
+ }
+ },
+ "gl-format-compiler-error": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/gl-format-compiler-error/-/gl-format-compiler-error-1.0.3.tgz",
+ "integrity": "sha1-DHmxdRiZzpcy6GJA8JCqQemEcag=",
+ "requires": {
+ "add-line-numbers": "^1.0.1",
+ "gl-constants": "^1.0.0",
+ "glsl-shader-name": "^1.0.0",
+ "sprintf-js": "^1.0.3"
+ }
+ },
+ "gl-heatmap2d": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/gl-heatmap2d/-/gl-heatmap2d-1.0.6.tgz",
+ "integrity": "sha512-+agzSv4R5vsaH+AGYVz5RVzBK10amqAa+Bwj205F13JjNSGS91M1L9Yb8zssCv2FIjpP+1Mp73cFBYrQFfS1Jg==",
+ "requires": {
+ "binary-search-bounds": "^2.0.4",
+ "gl-buffer": "^2.1.2",
+ "gl-shader": "^4.2.1",
+ "glslify": "^7.0.0",
+ "iota-array": "^1.0.0",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "gl-line3d": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gl-line3d/-/gl-line3d-1.2.0.tgz",
+ "integrity": "sha512-du9GDF87DMfllND2pBjySyHhFaza9upw4t2GMoXn11/I38atO6+saiznuhKmfxuDnyxGdmmZF6/HPauk0owKDA==",
+ "requires": {
+ "binary-search-bounds": "^2.0.4",
+ "gl-buffer": "^2.1.2",
+ "gl-shader": "^4.2.1",
+ "gl-texture2d": "^2.1.0",
+ "gl-vao": "^1.3.0",
+ "glsl-out-of-range": "^1.0.4",
+ "glslify": "^7.0.0",
+ "ndarray": "^1.0.18"
+ }
+ },
+ "gl-mat2": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gl-mat2/-/gl-mat2-1.0.1.tgz",
+ "integrity": "sha1-FCUFcwpcL+Hp8l2ezj0NbMJxCjA="
+ },
+ "gl-mat3": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gl-mat3/-/gl-mat3-1.0.0.tgz",
+ "integrity": "sha1-iWMyGcpCk3mha5GF2V1BcTRTuRI="
+ },
+ "gl-mat4": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gl-mat4/-/gl-mat4-1.2.0.tgz",
+ "integrity": "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA=="
+ },
+ "gl-matrix": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.3.0.tgz",
+ "integrity": "sha512-COb7LDz+SXaHtl/h4LeaFcNdJdAQSDeVqjiIihSXNrkWObZLhDI4hIkZC11Aeqp7bcE72clzB0BnDXr2SmslRA=="
+ },
+ "gl-matrix-invert": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gl-matrix-invert/-/gl-matrix-invert-1.0.0.tgz",
+ "integrity": "sha1-o2173jZUxFkKEn7nxo9uE/6oxj0=",
+ "requires": {
+ "gl-mat2": "^1.0.0",
+ "gl-mat3": "^1.0.0",
+ "gl-mat4": "^1.0.0"
+ }
+ },
+ "gl-mesh3d": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/gl-mesh3d/-/gl-mesh3d-2.3.1.tgz",
+ "integrity": "sha512-pXECamyGgu4/9HeAQSE5OEUuLBGS1aq9V4BCsTcxsND4fNLaajEkYKUz/WY2QSYElqKdsMBVsldGiKRKwlybqA==",
+ "requires": {
+ "barycentric": "^1.0.1",
+ "colormap": "^2.3.1",
+ "gl-buffer": "^2.1.2",
+ "gl-mat4": "^1.2.0",
+ "gl-shader": "^4.2.1",
+ "gl-texture2d": "^2.1.0",
+ "gl-vao": "^1.3.0",
+ "glsl-out-of-range": "^1.0.4",
+ "glsl-specular-cook-torrance": "^2.0.1",
+ "glslify": "^7.0.0",
+ "ndarray": "^1.0.18",
+ "normals": "^1.1.0",
+ "polytope-closest-point": "^1.0.0",
+ "simplicial-complex-contour": "^1.0.2",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "gl-plot2d": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/gl-plot2d/-/gl-plot2d-1.4.4.tgz",
+ "integrity": "sha512-0UhKiiqeampLtydv6NMNrKEilc0Ui5oaJtvHLbLZ5u/1ttT1XjOY5Yk8LzfqozA/No4a9omxjSKnH+tvSn+rQQ==",
+ "requires": {
+ "binary-search-bounds": "^2.0.4",
+ "gl-buffer": "^2.1.2",
+ "gl-select-static": "^2.0.6",
+ "gl-shader": "^4.2.1",
+ "glsl-inverse": "^1.0.0",
+ "glslify": "^7.0.0",
+ "text-cache": "^4.2.2"
+ }
+ },
+ "gl-plot3d": {
+ "version": "2.4.5",
+ "resolved": "https://registry.npmjs.org/gl-plot3d/-/gl-plot3d-2.4.5.tgz",
+ "integrity": "sha512-cKAqMXFRHTCFxH8r1/ACdk5hyfnA9djfiAM8zVQrqu0qLEttUu0i1fq0pr+d5m0HPuNcK8wEc4F3VjL2hrDcGQ==",
+ "requires": {
+ "3d-view": "^2.0.0",
+ "a-big-triangle": "^1.0.3",
+ "gl-axes3d": "^1.5.3",
+ "gl-fbo": "^2.0.5",
+ "gl-mat4": "^1.2.0",
+ "gl-select-static": "^2.0.6",
+ "gl-shader": "^4.2.1",
+ "gl-spikes3d": "^1.0.10",
+ "glslify": "^7.0.0",
+ "has-passive-events": "^1.0.0",
+ "is-mobile": "^2.2.1",
+ "mouse-change": "^1.4.0",
+ "mouse-event-offset": "^3.0.2",
+ "mouse-wheel": "^1.2.0",
+ "ndarray": "^1.0.18",
+ "right-now": "^1.0.0"
+ }
+ },
+ "gl-pointcloud2d": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/gl-pointcloud2d/-/gl-pointcloud2d-1.0.3.tgz",
+ "integrity": "sha512-OS2e1irvJXVRpg/GziXj10xrFJm9kkRfFoB6BLUvkjCQV7ZRNNcs2CD+YSK1r0gvMwTg2T3lfLM3UPwNtz+4Xw==",
+ "requires": {
+ "gl-buffer": "^2.1.2",
+ "gl-shader": "^4.2.1",
+ "glslify": "^7.0.0",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "gl-quat": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gl-quat/-/gl-quat-1.0.0.tgz",
+ "integrity": "sha1-CUXskjOG9FMpvl3DV7HIwtR1hsU=",
+ "requires": {
+ "gl-mat3": "^1.0.0",
+ "gl-vec3": "^1.0.3",
+ "gl-vec4": "^1.0.0"
+ }
+ },
+ "gl-scatter3d": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/gl-scatter3d/-/gl-scatter3d-1.2.3.tgz",
+ "integrity": "sha512-nXqPlT1w5Qt51dTksj+DUqrZqwWAEWg0PocsKcoDnVNv0X8sGA+LBZ0Y+zrA+KNXUL0PPCX9WR9cF2uJAZl1Sw==",
+ "requires": {
+ "gl-buffer": "^2.1.2",
+ "gl-mat4": "^1.2.0",
+ "gl-shader": "^4.2.1",
+ "gl-vao": "^1.3.0",
+ "glsl-out-of-range": "^1.0.4",
+ "glslify": "^7.0.0",
+ "is-string-blank": "^1.0.1",
+ "typedarray-pool": "^1.1.0",
+ "vectorize-text": "^3.2.1"
+ }
+ },
+ "gl-select-box": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/gl-select-box/-/gl-select-box-1.0.4.tgz",
+ "integrity": "sha512-mKsCnglraSKyBbQiGq0Ila0WF+m6Tr+EWT2yfaMn/Sh9aMHq5Wt0F/l6Cf/Ed3CdERq5jHWAY5yxLviZteYu2w==",
+ "requires": {
+ "gl-buffer": "^2.1.2",
+ "gl-shader": "^4.2.1",
+ "glslify": "^7.0.0"
+ }
+ },
+ "gl-select-static": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/gl-select-static/-/gl-select-static-2.0.6.tgz",
+ "integrity": "sha512-p4DmBG1DMo/47/fV3oqPcU6uTqHy0eI1vATH1fm8OVDqlzWnLv3786tdEunZWG6Br7DUdH6NgWhuy4gAlt+TAQ==",
+ "requires": {
+ "bit-twiddle": "^1.0.2",
+ "cwise": "^1.0.10",
+ "gl-fbo": "^2.0.5",
+ "ndarray": "^1.0.18",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "gl-shader": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/gl-shader/-/gl-shader-4.2.1.tgz",
+ "integrity": "sha1-vJuAjpKTxRtmjojeYVsMETcI3C8=",
+ "requires": {
+ "gl-format-compiler-error": "^1.0.2",
+ "weakmap-shim": "^1.1.0"
+ }
+ },
+ "gl-spikes2d": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/gl-spikes2d/-/gl-spikes2d-1.0.2.tgz",
+ "integrity": "sha512-QVeOZsi9nQuJJl7NB3132CCv5KA10BWxAY2QgJNsKqbLsG53B/TrGJpjIAohnJftdZ4fT6b3ZojWgeaXk8bOOA=="
+ },
+ "gl-spikes3d": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/gl-spikes3d/-/gl-spikes3d-1.0.10.tgz",
+ "integrity": "sha512-lT3xroowOFxMvlhT5Mof76B2TE02l5zt/NIWljhczV2FFHgIVhA4jMrd5dIv1so1RXMBDJIKu0uJI3QKliDVLg==",
+ "requires": {
+ "gl-buffer": "^2.1.2",
+ "gl-shader": "^4.2.1",
+ "gl-vao": "^1.3.0",
+ "glslify": "^7.0.0"
+ }
+ },
+ "gl-state": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gl-state/-/gl-state-1.0.0.tgz",
+ "integrity": "sha1-Ji+qdYNbC5xTLBLzitxCXR0wzRc=",
+ "requires": {
+ "uniq": "^1.0.0"
+ }
+ },
+ "gl-streamtube3d": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/gl-streamtube3d/-/gl-streamtube3d-1.4.1.tgz",
+ "integrity": "sha512-rH02v00kgwgdpkXVo7KsSoPp38bIAYR9TE1iONjcQ4cQAlDhrGRauqT/P5sUaOIzs17A2DxWGcXM+EpNQs9pUA==",
+ "requires": {
+ "gl-cone3d": "^1.5.2",
+ "gl-vec3": "^1.1.3",
+ "gl-vec4": "^1.0.1",
+ "glsl-inverse": "^1.0.0",
+ "glsl-out-of-range": "^1.0.4",
+ "glsl-specular-cook-torrance": "^2.0.1",
+ "glslify": "^7.0.0"
+ }
+ },
+ "gl-surface3d": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/gl-surface3d/-/gl-surface3d-1.5.2.tgz",
+ "integrity": "sha512-rWSQwEQDkB0T5CDEDFJwJc4VgwwJaAyFRSJ92NJlrTSwDlsEsWdzG9+APx6FWJMwkOpIoZGWqv+csswK2kMMLQ==",
+ "requires": {
+ "binary-search-bounds": "^2.0.4",
+ "bit-twiddle": "^1.0.2",
+ "colormap": "^2.3.1",
+ "dup": "^1.0.0",
+ "gl-buffer": "^2.1.2",
+ "gl-mat4": "^1.2.0",
+ "gl-shader": "^4.2.1",
+ "gl-texture2d": "^2.1.0",
+ "gl-vao": "^1.3.0",
+ "glsl-out-of-range": "^1.0.4",
+ "glsl-specular-beckmann": "^1.1.2",
+ "glslify": "^7.0.0",
+ "ndarray": "^1.0.18",
+ "ndarray-gradient": "^1.0.0",
+ "ndarray-ops": "^1.2.2",
+ "ndarray-pack": "^1.2.1",
+ "ndarray-scratch": "^1.2.0",
+ "surface-nets": "^1.0.2",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "gl-text": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/gl-text/-/gl-text-1.1.8.tgz",
+ "integrity": "sha512-whnq9DEFYbW92C4ONwk2eT0YkzmVPHoADnEtuzMOmit87XhgAhBrNs3lK9EgGjU/MoWYvlF6RkI8Kl7Yuo1hUw==",
+ "requires": {
+ "bit-twiddle": "^1.0.2",
+ "color-normalize": "^1.5.0",
+ "css-font": "^1.2.0",
+ "detect-kerning": "^2.1.2",
+ "es6-weak-map": "^2.0.3",
+ "flatten-vertex-data": "^1.0.2",
+ "font-atlas": "^2.1.0",
+ "font-measure": "^1.2.2",
+ "gl-util": "^3.1.2",
+ "is-plain-obj": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "parse-rect": "^1.2.0",
+ "parse-unit": "^1.0.1",
+ "pick-by-alias": "^1.2.0",
+ "regl": "^1.3.11",
+ "to-px": "^1.0.1",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "gl-texture2d": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/gl-texture2d/-/gl-texture2d-2.1.0.tgz",
+ "integrity": "sha1-/2gk5+fDGoum/c2+nlxpXX4hh8c=",
+ "requires": {
+ "ndarray": "^1.0.15",
+ "ndarray-ops": "^1.2.2",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "gl-util": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/gl-util/-/gl-util-3.1.2.tgz",
+ "integrity": "sha512-8czWhGTGp/H4S35X1UxGbFlJ1hjtTFhm2mc85GcymEi1CDf633WJgtkCddEiSjIa4BnNxBrqOIhj6jlF6naPqw==",
+ "requires": {
+ "is-browser": "^2.0.1",
+ "is-firefox": "^1.0.3",
+ "is-plain-obj": "^1.1.0",
+ "number-is-integer": "^1.0.1",
+ "object-assign": "^4.1.0",
+ "pick-by-alias": "^1.2.0",
+ "weak-map": "^1.0.5"
+ }
+ },
+ "gl-vao": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/gl-vao/-/gl-vao-1.3.0.tgz",
+ "integrity": "sha1-6ekqqVWIyrnVwvBLaTRAw99pGSM="
+ },
+ "gl-vec3": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/gl-vec3/-/gl-vec3-1.1.3.tgz",
+ "integrity": "sha512-jduKUqT0SGH02l8Yl+mV1yVsDfYgQAJyXGxkJQGyxPLHRiW25DwVIRPt6uvhrEMHftJfqhqKthRcyZqNEl9Xdw=="
+ },
+ "gl-vec4": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/gl-vec4/-/gl-vec4-1.0.1.tgz",
+ "integrity": "sha1-l9loeCgbFLUyy84QF4Xf0cs0CWQ="
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "global-modules": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+ "dev": true,
+ "requires": {
+ "global-prefix": "^1.0.1",
+ "is-windows": "^1.0.1",
+ "resolve-dir": "^1.0.0"
+ },
+ "dependencies": {
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ }
+ }
+ },
+ "global-prefix": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.2",
+ "homedir-polyfill": "^1.0.1",
+ "ini": "^1.3.4",
+ "is-windows": "^1.0.1",
+ "which": "^1.2.14"
+ },
+ "dependencies": {
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ }
+ }
+ },
+ "globals": {
+ "version": "11.11.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz",
+ "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==",
+ "dev": true
+ },
+ "globby": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
+ "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "dir-glob": "^2.0.0",
+ "glob": "^7.1.2",
+ "ignore": "^3.3.5",
+ "pify": "^3.0.0",
+ "slash": "^1.0.0"
+ },
+ "dependencies": {
+ "slash": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+ "dev": true
+ }
+ }
+ },
+ "glsl-inject-defines": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/glsl-inject-defines/-/glsl-inject-defines-1.0.3.tgz",
+ "integrity": "sha1-3RqswsF/yyvT/DJBHGYz0Ne2D9Q=",
+ "requires": {
+ "glsl-token-inject-block": "^1.0.0",
+ "glsl-token-string": "^1.0.1",
+ "glsl-tokenizer": "^2.0.2"
+ }
+ },
+ "glsl-inverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/glsl-inverse/-/glsl-inverse-1.0.0.tgz",
+ "integrity": "sha1-EsCx0GX1WERNHm/q95td34qRiuY="
+ },
+ "glsl-out-of-range": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/glsl-out-of-range/-/glsl-out-of-range-1.0.4.tgz",
+ "integrity": "sha512-fCcDu2LCQ39VBvfe1FbhuazXEf0CqMZI9OYXrYlL6uUARG48CTAbL04+tZBtVM0zo1Ljx4OLu2AxNquq++lxWQ=="
+ },
+ "glsl-resolve": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/glsl-resolve/-/glsl-resolve-0.0.1.tgz",
+ "integrity": "sha1-iUvvc5ENeSyBtRQxgANdCnivdtM=",
+ "requires": {
+ "resolve": "^0.6.1",
+ "xtend": "^2.1.2"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-0.6.3.tgz",
+ "integrity": "sha1-3ZV5gufnNt699TtYpN2RdUV13UY="
+ },
+ "xtend": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.2.0.tgz",
+ "integrity": "sha1-7vax8ZjByN6vrYsXZaBNrUoBxak="
+ }
+ }
+ },
+ "glsl-shader-name": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/glsl-shader-name/-/glsl-shader-name-1.0.0.tgz",
+ "integrity": "sha1-osMLO6c0mb77DMcYTXx3M91LSH0=",
+ "requires": {
+ "atob-lite": "^1.0.0",
+ "glsl-tokenizer": "^2.0.2"
+ }
+ },
+ "glsl-specular-beckmann": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/glsl-specular-beckmann/-/glsl-specular-beckmann-1.1.2.tgz",
+ "integrity": "sha1-/OkFaTPs3yRWJ4N2pU0IKJPndfE="
+ },
+ "glsl-specular-cook-torrance": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/glsl-specular-cook-torrance/-/glsl-specular-cook-torrance-2.0.1.tgz",
+ "integrity": "sha1-qJHMBsjHtPRyhwK0gk/ay7ln148=",
+ "requires": {
+ "glsl-specular-beckmann": "^1.1.1"
+ }
+ },
+ "glsl-token-assignments": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/glsl-token-assignments/-/glsl-token-assignments-2.0.2.tgz",
+ "integrity": "sha1-pdgqt4SZwuimuDy2lJXm5mXOAZ8="
+ },
+ "glsl-token-defines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/glsl-token-defines/-/glsl-token-defines-1.0.0.tgz",
+ "integrity": "sha1-y4kqqVmTYjFyhHDU90AySJaX+p0=",
+ "requires": {
+ "glsl-tokenizer": "^2.0.0"
+ }
+ },
+ "glsl-token-depth": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/glsl-token-depth/-/glsl-token-depth-1.1.2.tgz",
+ "integrity": "sha1-I8XjDuK9JViEtKKLyFC495HpXYQ="
+ },
+ "glsl-token-descope": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/glsl-token-descope/-/glsl-token-descope-1.0.2.tgz",
+ "integrity": "sha1-D8kKsyYYa4L1l7LnfcniHvzTIHY=",
+ "requires": {
+ "glsl-token-assignments": "^2.0.0",
+ "glsl-token-depth": "^1.1.0",
+ "glsl-token-properties": "^1.0.0",
+ "glsl-token-scope": "^1.1.0"
+ }
+ },
+ "glsl-token-inject-block": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/glsl-token-inject-block/-/glsl-token-inject-block-1.1.0.tgz",
+ "integrity": "sha1-4QFfWYDBCRgkraomJfHf3ovQADQ="
+ },
+ "glsl-token-properties": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/glsl-token-properties/-/glsl-token-properties-1.0.1.tgz",
+ "integrity": "sha1-SD3D2Dnw1LXGFx0VkfJJvlPCip4="
+ },
+ "glsl-token-scope": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/glsl-token-scope/-/glsl-token-scope-1.1.2.tgz",
+ "integrity": "sha1-oXKOeN8kRE+cuT/RjvD3VQOmQ7E="
+ },
+ "glsl-token-string": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/glsl-token-string/-/glsl-token-string-1.0.1.tgz",
+ "integrity": "sha1-WUQdL4V958NEnJRWZgIezjWOSOw="
+ },
+ "glsl-token-whitespace-trim": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/glsl-token-whitespace-trim/-/glsl-token-whitespace-trim-1.0.0.tgz",
+ "integrity": "sha1-RtHf6Yx1vX1QTAXX0RsbPpzJOxA="
+ },
+ "glsl-tokenizer": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/glsl-tokenizer/-/glsl-tokenizer-2.1.5.tgz",
+ "integrity": "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA==",
+ "requires": {
+ "through2": "^0.6.3"
+ }
+ },
+ "glslify": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/glslify/-/glslify-7.0.0.tgz",
+ "integrity": "sha512-yw8jDQIe9FlSH5NiZEqSAsCPj9HI7nhXgXLAgSv2Nm9eBPsFJmyN9+rNwbiozJapcj9xtc/71rMYlN9cxp1B8Q==",
+ "requires": {
+ "bl": "^1.0.0",
+ "concat-stream": "^1.5.2",
+ "duplexify": "^3.4.5",
+ "falafel": "^2.1.0",
+ "from2": "^2.3.0",
+ "glsl-resolve": "0.0.1",
+ "glsl-token-whitespace-trim": "^1.0.0",
+ "glslify-bundle": "^5.0.0",
+ "glslify-deps": "^1.2.5",
+ "minimist": "^1.2.0",
+ "resolve": "^1.1.5",
+ "stack-trace": "0.0.9",
+ "static-eval": "^2.0.0",
+ "through2": "^2.0.1",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ }
+ }
+ },
+ "glslify-bundle": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glslify-bundle/-/glslify-bundle-5.1.1.tgz",
+ "integrity": "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A==",
+ "requires": {
+ "glsl-inject-defines": "^1.0.1",
+ "glsl-token-defines": "^1.0.0",
+ "glsl-token-depth": "^1.1.1",
+ "glsl-token-descope": "^1.0.2",
+ "glsl-token-scope": "^1.1.1",
+ "glsl-token-string": "^1.0.1",
+ "glsl-token-whitespace-trim": "^1.0.0",
+ "glsl-tokenizer": "^2.0.2",
+ "murmurhash-js": "^1.0.0",
+ "shallow-copy": "0.0.1"
+ }
+ },
+ "glslify-deps": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/glslify-deps/-/glslify-deps-1.3.1.tgz",
+ "integrity": "sha512-Ogm179MCazwIRyEqs3g3EOY4Y3XIAa0yl8J5RE9rJC6QH1w8weVOp2RZu0mvnYy/2xIas1w166YR2eZdDkWQxg==",
+ "requires": {
+ "@choojs/findup": "^0.2.0",
+ "events": "^1.0.2",
+ "glsl-resolve": "0.0.1",
+ "glsl-tokenizer": "^2.0.0",
+ "graceful-fs": "^4.1.2",
+ "inherits": "^2.0.1",
+ "map-limit": "0.0.1",
+ "resolve": "^1.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
+ },
+ "grid-index": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz",
+ "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA=="
+ },
+ "growly": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz",
+ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=",
+ "dev": true
+ },
+ "gud": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz",
+ "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw=="
+ },
+ "gzip-size": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-4.1.0.tgz",
+ "integrity": "sha1-iuCWJX6r59acRb4rZ8RIEk/7UXw=",
+ "dev": true,
+ "requires": {
+ "duplexer": "^0.1.1",
+ "pify": "^3.0.0"
+ }
+ },
+ "hammerjs": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
+ "integrity": "sha1-BO93hiz/K7edMPdpIJWTAiK/YPE="
+ },
+ "handle-thing": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz",
+ "integrity": "sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==",
+ "dev": true
+ },
+ "handlebars": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz",
+ "integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==",
+ "dev": true,
+ "requires": {
+ "async": "^2.5.0",
+ "optimist": "^0.6.1",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "dev": true,
+ "optional": true
+ },
+ "uglify-js": {
+ "version": "3.4.9",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
+ "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "commander": "~2.17.1",
+ "source-map": "~0.6.1"
+ }
+ }
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
+ "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.5.5",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "harmony-reflect": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.1.tgz",
+ "integrity": "sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==",
+ "dev": true
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ },
+ "has-hover": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-hover/-/has-hover-1.0.1.tgz",
+ "integrity": "sha1-PZdDeusZnGK4rAisvcU9O8UsF/c=",
+ "requires": {
+ "is-browser": "^2.0.1"
+ }
+ },
+ "has-passive-events": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-passive-events/-/has-passive-events-1.0.0.tgz",
+ "integrity": "sha512-2vSj6IeIsgvsRMyeQ0JaCX5Q3lX4zMn5HpoVc7MEhQ6pv8Iq9rsXjsp+E5ZwaT7T0xhMT0KmU8gtt1EFVdbJiw==",
+ "requires": {
+ "is-browser": "^2.0.1"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
+ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hash-base": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "history": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz",
+ "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==",
+ "requires": {
+ "@babel/runtime": "^7.1.2",
+ "loose-envify": "^1.2.0",
+ "resolve-pathname": "^3.0.0",
+ "tiny-invariant": "^1.0.2",
+ "tiny-warning": "^1.0.0",
+ "value-equal": "^1.0.1"
+ }
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "hoist-non-react-statics": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz",
+ "integrity": "sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "homedir-polyfill": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+ "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+ "dev": true,
+ "requires": {
+ "parse-passwd": "^1.0.0"
+ }
+ },
+ "hosted-git-info": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
+ "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
+ "dev": true
+ },
+ "hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "hsluv": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/hsluv/-/hsluv-0.0.3.tgz",
+ "integrity": "sha1-gpEH2vtKn4tSoYCe0C4JHq3mdUw="
+ },
+ "html-comment-regex": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
+ "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
+ "dev": true
+ },
+ "html-element-map": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/html-element-map/-/html-element-map-1.0.0.tgz",
+ "integrity": "sha512-/SP6aOiM5Ai9zALvCxDubIeez0LvG3qP7R9GcRDnJEP/HBmv0A8A9K0o8+HFudcFt46+i921ANjzKsjPjb7Enw==",
+ "dev": true,
+ "requires": {
+ "array-filter": "^1.0.0"
+ }
+ },
+ "html-encoding-sniffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+ "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+ "dev": true,
+ "requires": {
+ "whatwg-encoding": "^1.0.1"
+ }
+ },
+ "html-entities": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
+ "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=",
+ "dev": true
+ },
+ "html-minifier": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz",
+ "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==",
+ "dev": true,
+ "requires": {
+ "camel-case": "3.0.x",
+ "clean-css": "4.2.x",
+ "commander": "2.17.x",
+ "he": "1.2.x",
+ "param-case": "2.1.x",
+ "relateurl": "0.2.x",
+ "uglify-js": "3.4.x"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "dev": true
+ },
+ "uglify-js": {
+ "version": "3.4.9",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
+ "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
+ "dev": true,
+ "requires": {
+ "commander": "~2.17.1",
+ "source-map": "~0.6.1"
+ }
+ }
+ }
+ },
+ "html-webpack-plugin": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
+ "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
+ "dev": true,
+ "requires": {
+ "html-minifier": "^3.2.3",
+ "loader-utils": "^0.2.16",
+ "lodash": "^4.17.3",
+ "pretty-error": "^2.0.2",
+ "tapable": "^1.0.0",
+ "toposort": "^1.0.0",
+ "util.promisify": "1.0.0"
+ },
+ "dependencies": {
+ "big.js": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
+ "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
+ "dev": true
+ },
+ "json5": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
+ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
+ "dev": true,
+ "requires": {
+ "big.js": "^3.1.3",
+ "emojis-list": "^2.0.0",
+ "json5": "^0.5.0",
+ "object-assign": "^4.0.1"
+ }
+ },
+ "tapable": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
+ "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==",
+ "dev": true
+ }
+ }
+ },
+ "htmlparser2": {
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
+ "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^1.3.1",
+ "domhandler": "^2.3.0",
+ "domutils": "^1.5.1",
+ "entities": "^1.1.1",
+ "inherits": "^2.0.1",
+ "readable-stream": "^3.1.1"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz",
+ "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
+ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "http-deceiver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "http-parser-js": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.0.tgz",
+ "integrity": "sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w==",
+ "dev": true
+ },
+ "http-proxy": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
+ "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^3.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "http-proxy-middleware": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
+ "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
+ "dev": true,
+ "requires": {
+ "http-proxy": "^1.17.0",
+ "is-glob": "^4.0.0",
+ "lodash": "^4.17.11",
+ "micromatch": "^3.1.10"
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "icss-replace-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
+ "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
+ "dev": true
+ },
+ "icss-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz",
+ "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=",
+ "dev": true,
+ "requires": {
+ "postcss": "^6.0.1"
+ },
+ "dependencies": {
+ "postcss": {
+ "version": "6.0.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
+ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "source-map": "^0.6.1",
+ "supports-color": "^5.4.0"
+ }
+ }
+ }
+ },
+ "identity-obj-proxy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz",
+ "integrity": "sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=",
+ "dev": true,
+ "requires": {
+ "harmony-reflect": "^1.4.6"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz",
+ "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA=="
+ },
+ "iferr": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+ "dev": true
+ },
+ "ignore": {
+ "version": "3.3.10",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz",
+ "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==",
+ "dev": true
+ },
+ "image-palette": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/image-palette/-/image-palette-2.1.0.tgz",
+ "integrity": "sha512-3ImSEWD26+xuQFdP0RWR4WSXadZwvgrFhjGNpMEapTG1tf2XrBFS2dlKK5hNgH4UIaSQlSUFRn1NeA+zULIWbQ==",
+ "requires": {
+ "color-id": "^1.1.0",
+ "pxls": "^2.0.0",
+ "quantize": "^1.0.2"
+ }
+ },
+ "image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+ "dev": true,
+ "optional": true
+ },
+ "immutability-helper": {
+ "version": "2.9.1",
+ "resolved": "https://registry.npmjs.org/immutability-helper/-/immutability-helper-2.9.1.tgz",
+ "integrity": "sha512-r/RmRG8xO06s/k+PIaif2r5rGc3j4Yhc01jSBfwPCXDLYZwp/yxralI37Df1mwmuzcCsen/E/ITKcTEvc1PQmQ==",
+ "requires": {
+ "invariant": "^2.2.0"
+ }
+ },
+ "immutable": {
+ "version": "3.7.6",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
+ "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks="
+ },
+ "import-fresh": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
+ "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "import-local": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+ "dev": true,
+ "requires": {
+ "pkg-dir": "^3.0.0",
+ "resolve-cwd": "^2.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "incremental-convex-hull": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz",
+ "integrity": "sha1-UUKMFMudmmFEv+abKFH7N3M0vh4=",
+ "requires": {
+ "robust-orientation": "^1.1.2",
+ "simplicial-complex": "^1.0.0"
+ }
+ },
+ "indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+ "dev": true
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz",
+ "integrity": "sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^2.4.2",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.15",
+ "mute-stream": "0.0.8",
+ "run-async": "^2.2.0",
+ "rxjs": "^6.4.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^5.1.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-escapes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz",
+ "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.8.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "figures": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz",
+ "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "onetime": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz",
+ "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "internal-ip": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.2.0.tgz",
+ "integrity": "sha512-ZY8Rk+hlvFeuMmG5uH1MXhhdeMntmIaxaInvAmzMq/SHV8rv4Kh+6GiQNNDQd0wZFrcO+FiTBo8lui/osKOyJw==",
+ "dev": true,
+ "requires": {
+ "default-gateway": "^4.0.1",
+ "ipaddr.js": "^1.9.0"
+ },
+ "dependencies": {
+ "ipaddr.js": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
+ "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==",
+ "dev": true
+ }
+ }
+ },
+ "interpret": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
+ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
+ "dev": true
+ },
+ "interval-tree-1d": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.3.tgz",
+ "integrity": "sha1-j9veArayx9verWNry+2OCHENhcE=",
+ "requires": {
+ "binary-search-bounds": "^1.0.0"
+ },
+ "dependencies": {
+ "binary-search-bounds": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz",
+ "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k="
+ }
+ }
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "invert-kv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
+ "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
+ "dev": true
+ },
+ "invert-permutation": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/invert-permutation/-/invert-permutation-1.0.0.tgz",
+ "integrity": "sha1-oKeAQurbNrwXVR54fv0UOa3VSTM="
+ },
+ "iota-array": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz",
+ "integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc="
+ },
+ "ip": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
+ "dev": true
+ },
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.8.0.tgz",
+ "integrity": "sha1-6qM9bd16zo9/b+DJygRA5wZzix4=",
+ "dev": true
+ },
+ "is-absolute-url": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
+ "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
+ "dev": true
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-arguments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA=="
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-base64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-base64/-/is-base64-0.1.0.tgz",
+ "integrity": "sha512-WRRyllsGXJM7ZN7gPTCCQ/6wNPTRDwiWdPK66l5sJzcU/oOzcIcRRf0Rux8bkpox/1yjt0F6VJRsQOIG2qz5sg=="
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-blob": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-blob/-/is-blob-2.1.0.tgz",
+ "integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw=="
+ },
+ "is-boolean-object": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz",
+ "integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=",
+ "dev": true
+ },
+ "is-browser": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-browser/-/is-browser-2.1.0.tgz",
+ "integrity": "sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ=="
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+ },
+ "is-callable": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
+ "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA=="
+ },
+ "is-ci": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
+ "dev": true,
+ "requires": {
+ "ci-info": "^2.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-date-object": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
+ "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY="
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
+ "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="
+ },
+ "is-firefox": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-firefox/-/is-firefox-1.0.3.tgz",
+ "integrity": "sha1-KioVZ3g6QX9uFYMjEI84YbCRhWI="
+ },
+ "is-float-array": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-float-array/-/is-float-array-1.0.0.tgz",
+ "integrity": "sha512-4ew1Sx6B6kEAl3T3NOM0yB94J3NZnBdNt4paw0e8nY73yHHTeTEhyQ3Lj7EQEnv5LD+GxNTaT4L46jcKjjpLiQ=="
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-generator-fn": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.0.0.tgz",
+ "integrity": "sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz",
+ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-iexplorer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-iexplorer/-/is-iexplorer-1.0.0.tgz",
+ "integrity": "sha1-HXK8ZtP+Iur2Fw3ajPEJQySM/HY="
+ },
+ "is-mobile": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.2.1.tgz",
+ "integrity": "sha512-6zELsfVFr326eq2CI53yvqq6YBanOxKBybwDT+MbMS2laBnK6Ez8m5XHSuTQQbnKRfpDzCod1CMWW5q3wZYMvA=="
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "is-number-object": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz",
+ "integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=",
+ "dev": true
+ },
+ "is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
+ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.1"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ },
+ "is-string": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz",
+ "integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=",
+ "dev": true
+ },
+ "is-string-blank": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz",
+ "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw=="
+ },
+ "is-subset": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
+ "integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=",
+ "dev": true
+ },
+ "is-svg": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz",
+ "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=",
+ "dev": true,
+ "requires": {
+ "html-comment-regex": "^1.1.0"
+ }
+ },
+ "is-svg-path": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-svg-path/-/is-svg-path-1.0.2.tgz",
+ "integrity": "sha1-d6tZDBKz0gNI5cehPQBAyHeE3aA="
+ },
+ "is-symbol": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
+ "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
+ "requires": {
+ "has-symbols": "^1.0.0"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "isomorphic-fetch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+ "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "requires": {
+ "node-fetch": "^1.0.1",
+ "whatwg-fetch": ">=0.10.0"
+ }
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "istanbul-api": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/istanbul-api/-/istanbul-api-2.1.1.tgz",
+ "integrity": "sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.1",
+ "compare-versions": "^3.2.1",
+ "fileset": "^2.0.3",
+ "istanbul-lib-coverage": "^2.0.3",
+ "istanbul-lib-hook": "^2.0.3",
+ "istanbul-lib-instrument": "^3.1.0",
+ "istanbul-lib-report": "^2.0.4",
+ "istanbul-lib-source-maps": "^3.0.2",
+ "istanbul-reports": "^2.1.1",
+ "js-yaml": "^3.12.0",
+ "make-dir": "^1.3.0",
+ "minimatch": "^3.0.4",
+ "once": "^1.4.0"
+ }
+ },
+ "istanbul-lib-coverage": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
+ "integrity": "sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==",
+ "dev": true
+ },
+ "istanbul-lib-hook": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz",
+ "integrity": "sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==",
+ "dev": true,
+ "requires": {
+ "append-transform": "^1.0.0"
+ }
+ },
+ "istanbul-lib-instrument": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz",
+ "integrity": "sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==",
+ "dev": true,
+ "requires": {
+ "@babel/generator": "^7.0.0",
+ "@babel/parser": "^7.0.0",
+ "@babel/template": "^7.0.0",
+ "@babel/traverse": "^7.0.0",
+ "@babel/types": "^7.0.0",
+ "istanbul-lib-coverage": "^2.0.3",
+ "semver": "^5.5.0"
+ }
+ },
+ "istanbul-lib-report": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz",
+ "integrity": "sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^2.0.3",
+ "make-dir": "^1.3.0",
+ "supports-color": "^6.0.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "istanbul-lib-source-maps": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz",
+ "integrity": "sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^2.0.3",
+ "make-dir": "^1.3.0",
+ "rimraf": "^2.6.2",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "istanbul-reports": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.1.1.tgz",
+ "integrity": "sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==",
+ "dev": true,
+ "requires": {
+ "handlebars": "^4.1.0"
+ }
+ },
+ "jest": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest/-/jest-24.1.0.tgz",
+ "integrity": "sha512-+q91L65kypqklvlRFfXfdzUKyngQLOcwGhXQaLmVHv+d09LkNXuBuGxlofTFW42XMzu3giIcChchTsCNUjQ78A==",
+ "dev": true,
+ "requires": {
+ "import-local": "^2.0.0",
+ "jest-cli": "^24.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0",
+ "wrap-ansi": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "jest-cli": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.1.0.tgz",
+ "integrity": "sha512-U/iyWPwOI0T1CIxVLtk/2uviOTJ/OiSWJSe8qt6X1VkbbgP+nrtLJlmT9lPBe4lK78VNFJtrJ7pttcNv/s7yCw==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.1",
+ "exit": "^0.1.2",
+ "glob": "^7.1.2",
+ "graceful-fs": "^4.1.15",
+ "import-local": "^2.0.0",
+ "is-ci": "^2.0.0",
+ "istanbul-api": "^2.0.8",
+ "istanbul-lib-coverage": "^2.0.2",
+ "istanbul-lib-instrument": "^3.0.1",
+ "istanbul-lib-source-maps": "^3.0.1",
+ "jest-changed-files": "^24.0.0",
+ "jest-config": "^24.1.0",
+ "jest-environment-jsdom": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "jest-haste-map": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-regex-util": "^24.0.0",
+ "jest-resolve-dependencies": "^24.1.0",
+ "jest-runner": "^24.1.0",
+ "jest-runtime": "^24.1.0",
+ "jest-snapshot": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-validate": "^24.0.0",
+ "jest-watcher": "^24.0.0",
+ "jest-worker": "^24.0.0",
+ "micromatch": "^3.1.10",
+ "node-notifier": "^5.2.1",
+ "p-each-series": "^1.0.0",
+ "pirates": "^4.0.0",
+ "prompts": "^2.0.1",
+ "realpath-native": "^1.0.0",
+ "rimraf": "^2.5.4",
+ "slash": "^2.0.0",
+ "string-length": "^2.0.0",
+ "strip-ansi": "^5.0.0",
+ "which": "^1.2.12",
+ "yargs": "^12.0.2"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+ "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0"
+ }
+ },
+ "yargs": {
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^11.1.1"
+ }
+ }
+ }
+ },
+ "jest-changed-files": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.0.0.tgz",
+ "integrity": "sha512-nnuU510R9U+UX0WNb5XFEcsrMqriSiRLeO9KWDFgPrpToaQm60prfQYpxsXigdClpvNot5bekDY440x9dNGnsQ==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "throat": "^4.0.0"
+ }
+ },
+ "jest-config": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.1.0.tgz",
+ "integrity": "sha512-FbbRzRqtFC6eGjG5VwsbW4E5dW3zqJKLWYiZWhB0/4E5fgsMw8GODLbGSrY5t17kKOtCWb/Z7nsIThRoDpuVyg==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.1.0",
+ "babel-jest": "^24.1.0",
+ "chalk": "^2.0.1",
+ "glob": "^7.1.1",
+ "jest-environment-jsdom": "^24.0.0",
+ "jest-environment-node": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "jest-jasmine2": "^24.1.0",
+ "jest-regex-util": "^24.0.0",
+ "jest-resolve": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-validate": "^24.0.0",
+ "micromatch": "^3.1.10",
+ "pretty-format": "^24.0.0",
+ "realpath-native": "^1.0.2"
+ }
+ },
+ "jest-diff": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.0.0.tgz",
+ "integrity": "sha512-XY5wMpRaTsuMoU+1/B2zQSKQ9RdE9gsLkGydx3nvApeyPijLA8GtEvIcPwISRCer+VDf9W1mStTYYq6fPt8ryA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1",
+ "diff-sequences": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "pretty-format": "^24.0.0"
+ }
+ },
+ "jest-docblock": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.0.0.tgz",
+ "integrity": "sha512-KfAKZ4SN7CFOZpWg4i7g7MSlY0M+mq7K0aMqENaG2vHuhC9fc3vkpU/iNN9sOus7v3h3Y48uEjqz3+Gdn2iptA==",
+ "dev": true,
+ "requires": {
+ "detect-newline": "^2.1.0"
+ }
+ },
+ "jest-each": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.0.0.tgz",
+ "integrity": "sha512-gFcbY4Cu55yxExXMkjrnLXov3bWO3dbPAW7HXb31h/DNWdNc/6X8MtxGff8nh3/MjkF9DpVqnj0KsPKuPK0cpA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1",
+ "jest-get-type": "^24.0.0",
+ "jest-util": "^24.0.0",
+ "pretty-format": "^24.0.0"
+ }
+ },
+ "jest-environment-jsdom": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.0.0.tgz",
+ "integrity": "sha512-1YNp7xtxajTRaxbylDc2pWvFnfDTH5BJJGyVzyGAKNt/lEULohwEV9zFqTgG4bXRcq7xzdd+sGFws+LxThXXOw==",
+ "dev": true,
+ "requires": {
+ "jest-mock": "^24.0.0",
+ "jest-util": "^24.0.0",
+ "jsdom": "^11.5.1"
+ }
+ },
+ "jest-environment-node": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.0.0.tgz",
+ "integrity": "sha512-62fOFcaEdU0VLaq8JL90TqwI7hLn0cOKOl8vY2n477vRkCJRojiRRtJVRzzCcgFvs6gqU97DNqX5R0BrBP6Rxg==",
+ "dev": true,
+ "requires": {
+ "jest-mock": "^24.0.0",
+ "jest-util": "^24.0.0"
+ }
+ },
+ "jest-get-type": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.0.0.tgz",
+ "integrity": "sha512-z6/Eyf6s9ZDGz7eOvl+fzpuJmN9i0KyTt1no37/dHu8galssxz5ZEgnc1KaV8R31q1khxyhB4ui/X5ZjjPk77w==",
+ "dev": true
+ },
+ "jest-haste-map": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.0.0.tgz",
+ "integrity": "sha512-CcViJyUo41IQqttLxXVdI41YErkzBKbE6cS6dRAploCeutePYfUimWd3C9rQEWhX0YBOQzvNsC0O9nYxK2nnxQ==",
+ "dev": true,
+ "requires": {
+ "fb-watchman": "^2.0.0",
+ "graceful-fs": "^4.1.15",
+ "invariant": "^2.2.4",
+ "jest-serializer": "^24.0.0",
+ "jest-util": "^24.0.0",
+ "jest-worker": "^24.0.0",
+ "micromatch": "^3.1.10",
+ "sane": "^3.0.0"
+ }
+ },
+ "jest-jasmine2": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.1.0.tgz",
+ "integrity": "sha512-H+o76SdSNyCh9fM5K8upK45YTo/DiFx5w2YAzblQebSQmukDcoVBVeXynyr7DDnxh+0NTHYRCLwJVf3tC518wg==",
+ "dev": true,
+ "requires": {
+ "@babel/traverse": "^7.1.0",
+ "chalk": "^2.0.1",
+ "co": "^4.6.0",
+ "expect": "^24.1.0",
+ "is-generator-fn": "^2.0.0",
+ "jest-each": "^24.0.0",
+ "jest-matcher-utils": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-snapshot": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "pretty-format": "^24.0.0",
+ "throat": "^4.0.0"
+ }
+ },
+ "jest-leak-detector": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.0.0.tgz",
+ "integrity": "sha512-ZYHJYFeibxfsDSKowjDP332pStuiFT2xfc5R67Rjm/l+HFJWJgNIOCOlQGeXLCtyUn3A23+VVDdiCcnB6dTTrg==",
+ "dev": true,
+ "requires": {
+ "pretty-format": "^24.0.0"
+ }
+ },
+ "jest-matcher-utils": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.0.0.tgz",
+ "integrity": "sha512-LQTDmO+aWRz1Tf9HJg+HlPHhDh1E1c65kVwRFo5mwCVp5aQDzlkz4+vCvXhOKFjitV2f0kMdHxnODrXVoi+rlA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1",
+ "jest-diff": "^24.0.0",
+ "jest-get-type": "^24.0.0",
+ "pretty-format": "^24.0.0"
+ }
+ },
+ "jest-message-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.0.0.tgz",
+ "integrity": "sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "chalk": "^2.0.1",
+ "micromatch": "^3.1.10",
+ "slash": "^2.0.0",
+ "stack-utils": "^1.0.1"
+ }
+ },
+ "jest-mock": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.0.0.tgz",
+ "integrity": "sha512-sQp0Hu5fcf5NZEh1U9eIW2qD0BwJZjb63Yqd98PQJFvf/zzUTBoUAwv/Dc/HFeNHIw1f3hl/48vNn+j3STaI7A==",
+ "dev": true
+ },
+ "jest-regex-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.0.0.tgz",
+ "integrity": "sha512-Jv/uOTCuC+PY7WpJl2mpoI+WbY2ut73qwwO9ByJJNwOCwr1qWhEW2Lyi2S9ZewUdJqeVpEBisdEVZSI+Zxo58Q==",
+ "dev": true
+ },
+ "jest-resolve": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.1.0.tgz",
+ "integrity": "sha512-TPiAIVp3TG6zAxH28u/6eogbwrvZjBMWroSLBDkwkHKrqxB/RIdwkWDye4uqPlZIXWIaHtifY3L0/eO5Z0f2wg==",
+ "dev": true,
+ "requires": {
+ "browser-resolve": "^1.11.3",
+ "chalk": "^2.0.1",
+ "realpath-native": "^1.0.0"
+ }
+ },
+ "jest-resolve-dependencies": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.1.0.tgz",
+ "integrity": "sha512-2VwPsjd3kRPu7qe2cpytAgowCObk5AKeizfXuuiwgm1a9sijJDZe8Kh1sFj6FKvSaNEfCPlBVkZEJa2482m/Uw==",
+ "dev": true,
+ "requires": {
+ "jest-regex-util": "^24.0.0",
+ "jest-snapshot": "^24.1.0"
+ }
+ },
+ "jest-runner": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.1.0.tgz",
+ "integrity": "sha512-CDGOkT3AIFl16BLL/OdbtYgYvbAprwJ+ExKuLZmGSCSldwsuU2dEGauqkpvd9nphVdAnJUcP12e/EIlnTX0QXg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "exit": "^0.1.2",
+ "graceful-fs": "^4.1.15",
+ "jest-config": "^24.1.0",
+ "jest-docblock": "^24.0.0",
+ "jest-haste-map": "^24.0.0",
+ "jest-jasmine2": "^24.1.0",
+ "jest-leak-detector": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-runtime": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-worker": "^24.0.0",
+ "source-map-support": "^0.5.6",
+ "throat": "^4.0.0"
+ }
+ },
+ "jest-runtime": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.1.0.tgz",
+ "integrity": "sha512-59/BY6OCuTXxGeDhEMU7+N33dpMQyXq7MLK07cNSIY/QYt2QZgJ7Tjx+rykBI0skAoigFl0A5tmT8UdwX92YuQ==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.1.0",
+ "babel-plugin-istanbul": "^5.1.0",
+ "chalk": "^2.0.1",
+ "convert-source-map": "^1.4.0",
+ "exit": "^0.1.2",
+ "fast-json-stable-stringify": "^2.0.0",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.1.15",
+ "jest-config": "^24.1.0",
+ "jest-haste-map": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-regex-util": "^24.0.0",
+ "jest-resolve": "^24.1.0",
+ "jest-snapshot": "^24.1.0",
+ "jest-util": "^24.0.0",
+ "jest-validate": "^24.0.0",
+ "micromatch": "^3.1.10",
+ "realpath-native": "^1.0.0",
+ "slash": "^2.0.0",
+ "strip-bom": "^3.0.0",
+ "write-file-atomic": "2.4.1",
+ "yargs": "^12.0.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "yargs": {
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^11.1.1"
+ }
+ }
+ }
+ },
+ "jest-serializer": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.0.0.tgz",
+ "integrity": "sha512-9FKxQyrFgHtx3ozU+1a8v938ILBE7S8Ko3uiAVjT8Yfi2o91j/fj81jacCQZ/Ihjiff/VsUCXVgQ+iF1XdImOw==",
+ "dev": true
+ },
+ "jest-snapshot": {
+ "version": "24.1.0",
+ "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.1.0.tgz",
+ "integrity": "sha512-th6TDfFqEmXvuViacU1ikD7xFb7lQsPn2rJl7OEmnfIVpnrx3QNY2t3PE88meeg0u/mQ0nkyvmC05PBqO4USFA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.0.0",
+ "chalk": "^2.0.1",
+ "jest-diff": "^24.0.0",
+ "jest-matcher-utils": "^24.0.0",
+ "jest-message-util": "^24.0.0",
+ "jest-resolve": "^24.1.0",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "pretty-format": "^24.0.0",
+ "semver": "^5.5.0"
+ }
+ },
+ "jest-util": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.0.0.tgz",
+ "integrity": "sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0",
+ "chalk": "^2.0.1",
+ "graceful-fs": "^4.1.15",
+ "is-ci": "^2.0.0",
+ "jest-message-util": "^24.0.0",
+ "mkdirp": "^0.5.1",
+ "slash": "^2.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "jest-validate": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.0.0.tgz",
+ "integrity": "sha512-vMrKrTOP4BBFIeOWsjpsDgVXATxCspC9S1gqvbJ3Tnn/b9ACsJmteYeVx9830UMV28Cob1RX55x96Qq3Tfad4g==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "chalk": "^2.0.1",
+ "jest-get-type": "^24.0.0",
+ "leven": "^2.1.0",
+ "pretty-format": "^24.0.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true
+ }
+ }
+ },
+ "jest-watcher": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.0.0.tgz",
+ "integrity": "sha512-GxkW2QrZ4YxmW1GUWER05McjVDunBlKMFfExu+VsGmXJmpej1saTEKvONdx5RJBlVdpPI5x6E3+EDQSIGgl53g==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.0.0",
+ "chalk": "^2.0.1",
+ "jest-util": "^24.0.0",
+ "string-length": "^2.0.0"
+ }
+ },
+ "jest-worker": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.0.0.tgz",
+ "integrity": "sha512-s64/OThpfQvoCeHG963MiEZOAAxu8kHsaL/rCMF7lpdzo7vgF0CtPml9hfguOMgykgH/eOm4jFP4ibfHLruytg==",
+ "dev": true,
+ "requires": {
+ "merge-stream": "^1.0.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "js-base64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.1.tgz",
+ "integrity": "sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==",
+ "dev": true
+ },
+ "js-levenshtein": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
+ "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "js-yaml": {
+ "version": "3.12.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.2.tgz",
+ "integrity": "sha512-QHn/Lh/7HhZ/Twc7vJYQTkjuCa0kaCcDcjK5Zlk2rvnUpy7DxMJ23+Jc2dcyvltwQVg1nygAVlB2oRDFHoRS5Q==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ }
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "jsdom": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
+ "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.0",
+ "acorn": "^5.5.3",
+ "acorn-globals": "^4.1.0",
+ "array-equal": "^1.0.0",
+ "cssom": ">= 0.3.2 < 0.4.0",
+ "cssstyle": "^1.0.0",
+ "data-urls": "^1.0.0",
+ "domexception": "^1.0.1",
+ "escodegen": "^1.9.1",
+ "html-encoding-sniffer": "^1.0.2",
+ "left-pad": "^1.3.0",
+ "nwsapi": "^2.0.7",
+ "parse5": "4.0.0",
+ "pn": "^1.1.0",
+ "request": "^2.87.0",
+ "request-promise-native": "^1.0.5",
+ "sax": "^1.2.4",
+ "symbol-tree": "^3.2.2",
+ "tough-cookie": "^2.3.4",
+ "w3c-hr-time": "^1.0.1",
+ "webidl-conversions": "^4.0.2",
+ "whatwg-encoding": "^1.0.3",
+ "whatwg-mimetype": "^2.1.0",
+ "whatwg-url": "^6.4.1",
+ "ws": "^5.2.0",
+ "xml-name-validator": "^3.0.0"
+ },
+ "dependencies": {
+ "parse5": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
+ "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==",
+ "dev": true
+ }
+ }
+ },
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "json2mq": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
+ "integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=",
+ "requires": {
+ "string-convert": "^0.2.0"
+ }
+ },
+ "json3": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz",
+ "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz",
+ "integrity": "sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "jsx-ast-utils": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz",
+ "integrity": "sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==",
+ "dev": true,
+ "requires": {
+ "array-includes": "^3.0.3",
+ "object.assign": "^4.1.0"
+ }
+ },
+ "kdbush": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz",
+ "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="
+ },
+ "killable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
+ "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ },
+ "kleur": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.2.tgz",
+ "integrity": "sha512-3h7B2WRT5LNXOtQiAaWonilegHcPSf9nLVXlSTci8lu1dZUuui61+EsPEZqSVxY7rXYmB2DVKMQILxaO5WL61Q==",
+ "dev": true
+ },
+ "lazy-cache": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
+ "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
+ },
+ "lcid": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
+ "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
+ "dev": true,
+ "requires": {
+ "invert-kv": "^2.0.0"
+ }
+ },
+ "leaflet": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.6.0.tgz",
+ "integrity": "sha512-CPkhyqWUKZKFJ6K8umN5/D2wrJ2+/8UIpXppY7QDnUZW5bZL5+SEI2J7GBpwh4LIupOKqbNSQXgqmrEJopHVNQ=="
+ },
+ "leaflet-fullscreen": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/leaflet-fullscreen/-/leaflet-fullscreen-1.0.2.tgz",
+ "integrity": "sha1-CcYcS6xF9jsu4Sav2H5c2XZQ/Bs="
+ },
+ "leaflet.markercluster": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.4.1.tgz",
+ "integrity": "sha512-ZSEpE/EFApR0bJ1w/dUGwTSUvWlpalKqIzkaYdYB7jaftQA/Y2Jav+eT4CMtEYFj+ZK4mswP13Q2acnPBnhGOw=="
+ },
+ "left-pad": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
+ "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="
+ },
+ "lerp": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/lerp/-/lerp-1.0.3.tgz",
+ "integrity": "sha1-oYyJaPkXiW3hXM/MKNVaa3Med24="
+ },
+ "less": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/less/-/less-3.9.0.tgz",
+ "integrity": "sha512-31CmtPEZraNUtuUREYjSqRkeETFdyEHSEPAGq4erDlUXtda7pzNmctdljdIagSb589d/qXGWiiP31R5JVf+v0w==",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.2",
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "mime": "^1.4.1",
+ "mkdirp": "^0.5.0",
+ "promise": "^7.1.1",
+ "request": "^2.83.0",
+ "source-map": "~0.6.0"
+ },
+ "dependencies": {
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "dev": true
+ }
+ }
+ },
+ "less-loader": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-4.1.0.tgz",
+ "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.1",
+ "loader-utils": "^1.1.0",
+ "pify": "^3.0.0"
+ },
+ "dependencies": {
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "dev": true
+ }
+ }
+ },
+ "less-plugin-autoprefix": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/less-plugin-autoprefix/-/less-plugin-autoprefix-1.5.1.tgz",
+ "integrity": "sha1-vKTlsuSMrGlloXgxQuOzLDwAzgc=",
+ "dev": true,
+ "requires": {
+ "autoprefixer": "^6.0.0",
+ "postcss": "^5.0.0"
+ }
+ },
+ "leven": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz",
+ "integrity": "sha1-wuep93IJTe6dNCAq6KzORoeHVYA=",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "load-json-file": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+ "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "loader-fs-cache": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.2.tgz",
+ "integrity": "sha512-70IzT/0/L+M20jUlEqZhZyArTU6VKLRTYRDAYN26g4jfzpJqjipLL3/hgYpySqI9PwsVRHHFja0LfEmsx9X2Cw==",
+ "dev": true,
+ "requires": {
+ "find-cache-dir": "^0.1.1",
+ "mkdirp": "0.5.1"
+ },
+ "dependencies": {
+ "find-cache-dir": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz",
+ "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "mkdirp": "^0.5.1",
+ "pkg-dir": "^1.0.0"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true,
+ "requires": {
+ "path-exists": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true,
+ "requires": {
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
+ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+ "dev": true,
+ "requires": {
+ "find-up": "^1.0.0"
+ }
+ }
+ }
+ },
+ "loader-runner": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+ "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+ "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^2.0.0",
+ "json5": "^1.0.1"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ }
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg=="
+ },
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
+ "dev": true
+ },
+ "lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
+ },
+ "lodash.escape": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
+ "integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=",
+ "dev": true
+ },
+ "lodash.flattendeep": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
+ "integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
+ "dev": true
+ },
+ "lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ=="
+ },
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+ },
+ "lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
+ "dev": true
+ },
+ "lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+ "dev": true
+ },
+ "lodash.throttle": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+ "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
+ },
+ "lodash.unescape": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
+ "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=",
+ "dev": true
+ },
+ "lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+ "dev": true
+ },
+ "loglevel": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz",
+ "integrity": "sha1-4PyVEztu8nbNyIh82vJKpvFW+Po=",
+ "dev": true
+ },
+ "longest": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
+ "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc="
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lower-case": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
+ "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
+ "dev": true,
+ "requires": {
+ "pseudomap": "^1.0.2",
+ "yallist": "^2.1.2"
+ }
+ },
+ "magic-string": {
+ "version": "0.25.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
+ "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
+ "requires": {
+ "sourcemap-codec": "^1.4.4"
+ }
+ },
+ "make-dir": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz",
+ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "makeerror": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz",
+ "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=",
+ "dev": true,
+ "requires": {
+ "tmpl": "1.0.x"
+ }
+ },
+ "mamacro": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz",
+ "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==",
+ "dev": true
+ },
+ "map-age-cleaner": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
+ "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
+ "dev": true,
+ "requires": {
+ "p-defer": "^1.0.0"
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-limit": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
+ "integrity": "sha1-63lhAxwPDo0AG/LVb6toXViCLzg=",
+ "requires": {
+ "once": "~1.3.0"
+ },
+ "dependencies": {
+ "once": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz",
+ "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=",
+ "requires": {
+ "wrappy": "1"
+ }
+ }
+ }
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "mapbox-gl": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.3.2.tgz",
+ "integrity": "sha512-6Ro7GbTMWxcbc836m6rbBNkesgTncbE1yXWeuHlr89esSqaItKr0+ntOu8rZie3fv+GtitkbODysXzIGCA7G+w==",
+ "requires": {
+ "@mapbox/geojson-rewind": "^0.4.0",
+ "@mapbox/geojson-types": "^1.0.2",
+ "@mapbox/jsonlint-lines-primitives": "^2.0.2",
+ "@mapbox/mapbox-gl-supported": "^1.4.0",
+ "@mapbox/point-geometry": "^0.1.0",
+ "@mapbox/tiny-sdf": "^1.1.0",
+ "@mapbox/unitbezier": "^0.0.0",
+ "@mapbox/vector-tile": "^1.3.1",
+ "@mapbox/whoots-js": "^3.1.0",
+ "csscolorparser": "~1.0.2",
+ "earcut": "^2.1.5",
+ "geojson-vt": "^3.2.1",
+ "gl-matrix": "^3.0.0",
+ "grid-index": "^1.1.0",
+ "minimist": "0.0.8",
+ "murmurhash-js": "^1.0.0",
+ "pbf": "^3.0.5",
+ "potpack": "^1.0.1",
+ "quickselect": "^2.0.0",
+ "rw": "^1.3.3",
+ "supercluster": "^6.0.1",
+ "tinyqueue": "^2.0.0",
+ "vt-pbf": "^3.1.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ }
+ }
+ },
+ "marching-simplex-table": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/marching-simplex-table/-/marching-simplex-table-1.0.0.tgz",
+ "integrity": "sha1-vBYlbg+Pm1WKqbKHL4gy2UM/Uuo=",
+ "requires": {
+ "convex-hull": "^1.0.3"
+ }
+ },
+ "markdown": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/markdown/-/markdown-0.5.0.tgz",
+ "integrity": "sha1-KCBbVlqK51kt4gdGPWY33BgnIrI=",
+ "requires": {
+ "nopt": "~2.1.1"
+ }
+ },
+ "mat4-decompose": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mat4-decompose/-/mat4-decompose-1.0.4.tgz",
+ "integrity": "sha1-ZetP451wh496RE60Yk1S9+frL68=",
+ "requires": {
+ "gl-mat4": "^1.0.1",
+ "gl-vec3": "^1.0.2"
+ }
+ },
+ "mat4-interpolate": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mat4-interpolate/-/mat4-interpolate-1.0.4.tgz",
+ "integrity": "sha1-Vf/p6zw1KV4sDVqfdyXZBoqJ/3Q=",
+ "requires": {
+ "gl-mat4": "^1.0.1",
+ "gl-vec3": "^1.0.2",
+ "mat4-decompose": "^1.0.3",
+ "mat4-recompose": "^1.0.3",
+ "quat-slerp": "^1.0.0"
+ }
+ },
+ "mat4-recompose": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mat4-recompose/-/mat4-recompose-1.0.4.tgz",
+ "integrity": "sha1-OVPCMP8kc9x3LuAUpSySXPgbDk0=",
+ "requires": {
+ "gl-mat4": "^1.0.1"
+ }
+ },
+ "material-design-iconic-font": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/material-design-iconic-font/-/material-design-iconic-font-2.2.0.tgz",
+ "integrity": "sha1-ZsOxyIDvDLh8AsoqD5AP8P5/SA0="
+ },
+ "math-expression-evaluator": {
+ "version": "1.2.17",
+ "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
+ "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=",
+ "dev": true
+ },
+ "math-log2": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/math-log2/-/math-log2-1.0.1.tgz",
+ "integrity": "sha1-+4lBvl9evol55xjmJzsXjlhpRWU="
+ },
+ "matrix-camera-controller": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/matrix-camera-controller/-/matrix-camera-controller-2.1.3.tgz",
+ "integrity": "sha1-NeUmDMHNVQliunmfLY1OlLGjk3A=",
+ "requires": {
+ "binary-search-bounds": "^1.0.0",
+ "gl-mat4": "^1.1.2",
+ "gl-vec3": "^1.0.3",
+ "mat4-interpolate": "^1.0.3"
+ },
+ "dependencies": {
+ "binary-search-bounds": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz",
+ "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k="
+ }
+ }
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "mdn-browser-compat-data": {
+ "version": "0.0.84",
+ "resolved": "https://registry.npmjs.org/mdn-browser-compat-data/-/mdn-browser-compat-data-0.0.84.tgz",
+ "integrity": "sha512-fAznuGNaQMQiWLVf+gyp33FaABTglYWqMT7JqvH+4RZn2UQPD12gbMqxwP9m0lj8AAbNpu5/kD6n4Ox1SOffpw==",
+ "dev": true,
+ "requires": {
+ "extend": "3.0.2"
+ }
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "dev": true
+ },
+ "mem": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz",
+ "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==",
+ "dev": true,
+ "requires": {
+ "map-age-cleaner": "^0.1.1",
+ "mimic-fn": "^1.0.0",
+ "p-is-promise": "^2.0.0"
+ }
+ },
+ "memorystream": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+ "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
+ "dev": true
+ },
+ "merge": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
+ "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==",
+ "dev": true
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "dev": true
+ },
+ "merge-stream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz",
+ "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=",
+ "dev": true,
+ "requires": {
+ "readable-stream": "^2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ }
+ }
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "optional": true
+ },
+ "mime-db": {
+ "version": "1.38.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz",
+ "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.22",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz",
+ "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==",
+ "dev": true,
+ "requires": {
+ "mime-db": "~1.38.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+ "dev": true
+ },
+ "mini-css-extract-plugin": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.4.5.tgz",
+ "integrity": "sha512-dqBanNfktnp2hwL2YguV9Jh91PFX7gu7nRLs4TGsbAfAG6WOtlynFRYzwDwmmeSb5uIwHo9nx1ta0f7vAZVp2w==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "schema-utils": "^1.0.0",
+ "webpack-sources": "^1.1.0"
+ }
+ },
+ "mini-store": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mini-store/-/mini-store-2.0.0.tgz",
+ "integrity": "sha512-EG0CuwpQmX+XL4QVS0kxNwHW5ftSbhygu1qxQH0pipugjnPkbvkalCdQbEihMwtQY6d3MTN+MS0q+aurs+RfLQ==",
+ "requires": {
+ "hoist-non-react-statics": "^2.3.1",
+ "prop-types": "^15.6.0",
+ "react-lifecycles-compat": "^3.0.4",
+ "shallowequal": "^1.0.2"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
+ "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
+ }
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "mississippi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz",
+ "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^2.0.1",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ }
+ }
+ },
+ "mixin-deep": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
+ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ }
+ }
+ },
+ "mockdate": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mockdate/-/mockdate-2.0.2.tgz",
+ "integrity": "sha1-WuDA6vj+I+AJzQH5iJtCxPY0rxI=",
+ "dev": true
+ },
+ "moment": {
+ "version": "2.24.0",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
+ "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
+ },
+ "monotone-convex-hull-2d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/monotone-convex-hull-2d/-/monotone-convex-hull-2d-1.0.1.tgz",
+ "integrity": "sha1-R/Xa6t88Sv03dkuqGqh4ekDu4Iw=",
+ "requires": {
+ "robust-orientation": "^1.1.3"
+ }
+ },
+ "moo": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz",
+ "integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==",
+ "dev": true
+ },
+ "mouse-change": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/mouse-change/-/mouse-change-1.4.0.tgz",
+ "integrity": "sha1-wrd+W/o0pDzhRFyBV6Tk3JiVwU8=",
+ "requires": {
+ "mouse-event": "^1.0.0"
+ }
+ },
+ "mouse-event": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/mouse-event/-/mouse-event-1.0.5.tgz",
+ "integrity": "sha1-s3ie23EJmX1aky0dAdqhVDpQFzI="
+ },
+ "mouse-event-offset": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mouse-event-offset/-/mouse-event-offset-3.0.2.tgz",
+ "integrity": "sha1-39hqbiSMa6jK1TuQXVA3ogY+mYQ="
+ },
+ "mouse-wheel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mouse-wheel/-/mouse-wheel-1.2.0.tgz",
+ "integrity": "sha1-bSkDseqPtI5h8bU7kDZ3PwQs21w=",
+ "requires": {
+ "right-now": "^1.0.0",
+ "signum": "^1.0.0",
+ "to-px": "^1.0.1"
+ },
+ "dependencies": {
+ "signum": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/signum/-/signum-1.0.0.tgz",
+ "integrity": "sha1-dKfSvyogtA66FqkrFSEk8dVZ+nc="
+ }
+ }
+ },
+ "mousetrap": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.2.tgz",
+ "integrity": "sha512-jDjhi7wlHwdO6q6DS7YRmSHcuI+RVxadBkLt3KHrhd3C2b+w5pKefg3oj5beTcHZyVFA9Aksf+yEE1y5jxUjVA=="
+ },
+ "move-concurrently": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+ "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1",
+ "copy-concurrently": "^1.0.0",
+ "fs-write-stream-atomic": "^1.0.8",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.3"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ },
+ "multicast-dns": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+ "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+ "dev": true,
+ "requires": {
+ "dns-packet": "^1.3.1",
+ "thunky": "^1.0.2"
+ }
+ },
+ "multicast-dns-service-types": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+ "dev": true
+ },
+ "mumath": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/mumath/-/mumath-3.3.4.tgz",
+ "integrity": "sha1-SNSg8P2MrU57Mglu6JsWGmPTC78=",
+ "requires": {
+ "almost-equal": "^1.1.0"
+ }
+ },
+ "murmurhash-js": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz",
+ "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E="
+ },
+ "mustache": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.2.tgz",
+ "integrity": "sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ=="
+ },
+ "mutationobserver-shim": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/mutationobserver-shim/-/mutationobserver-shim-0.3.5.tgz",
+ "integrity": "sha512-YAMuSp4Oi19SYQF04dGnRajyFp4Wyam+jKKWzm5roPcNh1Rip8dnHPxls5F/xBgY0H2gV+3IzWuIvYQPDAvmBQ=="
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.14.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+ "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ }
+ }
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "ndarray": {
+ "version": "1.0.19",
+ "resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz",
+ "integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==",
+ "requires": {
+ "iota-array": "^1.0.0",
+ "is-buffer": "^1.0.2"
+ }
+ },
+ "ndarray-extract-contour": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ndarray-extract-contour/-/ndarray-extract-contour-1.0.1.tgz",
+ "integrity": "sha1-Cu4ROjozsia5DEiIz4d79HUTBeQ=",
+ "requires": {
+ "typedarray-pool": "^1.0.0"
+ }
+ },
+ "ndarray-fill": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/ndarray-fill/-/ndarray-fill-1.0.2.tgz",
+ "integrity": "sha1-owpg9xiODJWC/N1YiWrNy1IqHtY=",
+ "requires": {
+ "cwise": "^1.0.10"
+ }
+ },
+ "ndarray-gradient": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ndarray-gradient/-/ndarray-gradient-1.0.0.tgz",
+ "integrity": "sha1-t0kaUVxqZJ8ZpiMk//byf8jCU5M=",
+ "requires": {
+ "cwise-compiler": "^1.0.0",
+ "dup": "^1.0.0"
+ }
+ },
+ "ndarray-homography": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ndarray-homography/-/ndarray-homography-1.0.0.tgz",
+ "integrity": "sha1-w1UW6oa8KGK06ASiNqJwcwn+KWs=",
+ "requires": {
+ "gl-matrix-invert": "^1.0.0",
+ "ndarray-warp": "^1.0.0"
+ }
+ },
+ "ndarray-linear-interpolate": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ndarray-linear-interpolate/-/ndarray-linear-interpolate-1.0.0.tgz",
+ "integrity": "sha1-eLySuFuavBW25n7mWCj54hN65ys="
+ },
+ "ndarray-ops": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/ndarray-ops/-/ndarray-ops-1.2.2.tgz",
+ "integrity": "sha1-WeiNLDKn7ryxvGkPrhQVeVV6YU4=",
+ "requires": {
+ "cwise-compiler": "^1.0.0"
+ }
+ },
+ "ndarray-pack": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz",
+ "integrity": "sha1-jK6+qqJNXs9w/4YCBjeXfajuWFo=",
+ "requires": {
+ "cwise-compiler": "^1.1.2",
+ "ndarray": "^1.0.13"
+ }
+ },
+ "ndarray-scratch": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/ndarray-scratch/-/ndarray-scratch-1.2.0.tgz",
+ "integrity": "sha1-YwRjbWLrqT20cnrBPGkzQdulDgE=",
+ "requires": {
+ "ndarray": "^1.0.14",
+ "ndarray-ops": "^1.2.1",
+ "typedarray-pool": "^1.0.2"
+ }
+ },
+ "ndarray-sort": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ndarray-sort/-/ndarray-sort-1.0.1.tgz",
+ "integrity": "sha1-/qBbTLg0x/TgIWo1TzynUTAN/Wo=",
+ "requires": {
+ "typedarray-pool": "^1.0.0"
+ }
+ },
+ "ndarray-warp": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ndarray-warp/-/ndarray-warp-1.0.1.tgz",
+ "integrity": "sha1-qKElqqu6C+v5O9bKg+ar1oIqNOA=",
+ "requires": {
+ "cwise": "^1.0.4",
+ "ndarray-linear-interpolate": "^1.0.0"
+ }
+ },
+ "nearley": {
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.16.0.tgz",
+ "integrity": "sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.19.0",
+ "moo": "^0.4.3",
+ "railroad-diagrams": "^1.0.0",
+ "randexp": "0.4.6",
+ "semver": "^5.4.1"
+ }
+ },
+ "negotiator": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
+ "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
+ "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
+ "dev": true
+ },
+ "next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw="
+ },
+ "nextafter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/nextafter/-/nextafter-1.0.0.tgz",
+ "integrity": "sha1-t9d7U1MQ4+CX5gJauwqQNHfsGjo=",
+ "requires": {
+ "double-bits": "^1.1.0"
+ }
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "no-case": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
+ "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==",
+ "dev": true,
+ "requires": {
+ "lower-case": "^1.1.1"
+ }
+ },
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "requires": {
+ "encoding": "^0.1.11",
+ "is-stream": "^1.0.1"
+ }
+ },
+ "node-forge": {
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.5.tgz",
+ "integrity": "sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ==",
+ "dev": true
+ },
+ "node-int64": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
+ "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=",
+ "dev": true
+ },
+ "node-libs-browser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz",
+ "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==",
+ "dev": true,
+ "requires": {
+ "assert": "^1.1.1",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^4.3.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "^1.0.0",
+ "crypto-browserify": "^3.11.0",
+ "domain-browser": "^1.1.1",
+ "events": "^3.0.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "0.0.0",
+ "process": "^0.11.10",
+ "punycode": "^1.2.4",
+ "querystring-es3": "^0.2.0",
+ "readable-stream": "^2.3.3",
+ "stream-browserify": "^2.0.1",
+ "stream-http": "^2.7.2",
+ "string_decoder": "^1.0.0",
+ "timers-browserify": "^2.0.4",
+ "tty-browserify": "0.0.0",
+ "url": "^0.11.0",
+ "util": "^0.11.0",
+ "vm-browserify": "0.0.4"
+ },
+ "dependencies": {
+ "events": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
+ "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA==",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ },
+ "dependencies": {
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
+ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "node-modules-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz",
+ "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=",
+ "dev": true
+ },
+ "node-notifier": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.0.tgz",
+ "integrity": "sha512-SUDEb+o71XR5lXSTyivXd9J7fCloE3SyP4lSgt3lU2oSANiox+SxlNRGPjDKrwU1YN3ix2KN/VGGCg0t01rttQ==",
+ "dev": true,
+ "requires": {
+ "growly": "^1.3.0",
+ "is-wsl": "^1.1.0",
+ "semver": "^5.5.0",
+ "shellwords": "^0.1.1",
+ "which": "^1.3.0"
+ }
+ },
+ "node-releases": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.8.tgz",
+ "integrity": "sha512-gQm+K9mGCiT/NXHy+V/ZZS1N/LOaGGqRAAJJs3X9Ah1g+CIbRcBgNyoNYQ+SEtcyAtB9KqDruu+fF7nWjsqRaA==",
+ "dev": true,
+ "requires": {
+ "semver": "^5.3.0"
+ }
+ },
+ "nopt": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.2.tgz",
+ "integrity": "sha1-bMzZd7gBMqB3MdbozljCyDA8+a8=",
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+ "dev": true
+ },
+ "normalize-svg-path": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-0.1.0.tgz",
+ "integrity": "sha1-RWNg5g7Odfvve11+FgSA5//Rb+U="
+ },
+ "normalize-url": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
+ "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1",
+ "prepend-http": "^1.0.0",
+ "query-string": "^4.1.0",
+ "sort-keys": "^1.0.0"
+ },
+ "dependencies": {
+ "query-string": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.1.0",
+ "strict-uri-encode": "^1.0.0"
+ }
+ },
+ "strict-uri-encode": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
+ "dev": true
+ }
+ }
+ },
+ "normals": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/normals/-/normals-1.1.0.tgz",
+ "integrity": "sha1-MltZXtNK/kZ6bFWhT9kIV4f/WcA="
+ },
+ "npm-run-all": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+ "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "chalk": "^2.4.1",
+ "cross-spawn": "^6.0.5",
+ "memorystream": "^0.3.1",
+ "minimatch": "^3.0.4",
+ "pidtree": "^0.3.0",
+ "read-pkg": "^3.0.0",
+ "shell-quote": "^1.6.1",
+ "string.prototype.padend": "^3.0.0"
+ }
+ },
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "dev": true,
+ "requires": {
+ "path-key": "^2.0.0"
+ }
+ },
+ "nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "dev": true,
+ "requires": {
+ "boolbase": "~1.0.0"
+ }
+ },
+ "num2fraction": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+ "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
+ "dev": true
+ },
+ "number-is-integer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-integer/-/number-is-integer-1.0.1.tgz",
+ "integrity": "sha1-5ZvKFy/+0nMY55x862y3LAlbIVI=",
+ "requires": {
+ "is-finite": "^1.0.1"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "numeral": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/numeral/-/numeral-2.0.6.tgz",
+ "integrity": "sha1-StCAk21EPCVhrtnyGX7//iX05QY="
+ },
+ "numeric": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/numeric/-/numeric-1.2.6.tgz",
+ "integrity": "sha1-dlsCvvl5iPz4gNTrPza4D6MTNao="
+ },
+ "nwsapi": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.1.tgz",
+ "integrity": "sha512-T5GaA1J/d34AC8mkrFD2O0DR17kwJ702ZOtJOsS8RpbsQZVOC2/xYFb1i/cw+xdM54JIlMuojjDOYct8GIWtwg==",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "object-hash": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.1.tgz",
+ "integrity": "sha512-HgcGMooY4JC2PBt9sdUdJ6PMzpin+YtY3r/7wg0uTifP+HJWW8rammseSEHuyt0UeShI183UGssCJqm1bJR7QA==",
+ "dev": true
+ },
+ "object-inspect": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
+ "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==",
+ "dev": true
+ },
+ "object-is": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz",
+ "integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY="
+ },
+ "object-keys": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz",
+ "integrity": "sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg=="
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "object.entries": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
+ "integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
+ "object.fromentries": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz",
+ "integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.11.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.1"
+ }
+ },
+ "object.getownpropertydescriptors": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
+ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.5.1"
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "object.values": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
+ "integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.12.0",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ }
+ },
+ "obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
+ "omit.js": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/omit.js/-/omit.js-1.0.2.tgz",
+ "integrity": "sha512-/QPc6G2NS+8d4L/cQhbk6Yit1WTB6Us2g84A7A/1+w9d/eRGHyEqC5kkQtHVoHZ5NFWGG7tUGgrhVZwgZanKrQ==",
+ "requires": {
+ "babel-runtime": "^6.23.0"
+ }
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "opener": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.1.tgz",
+ "integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
+ "dev": true
+ },
+ "opn": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-5.4.0.tgz",
+ "integrity": "sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw==",
+ "dev": true,
+ "requires": {
+ "is-wsl": "^1.1.0"
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
+ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
+ "dev": true,
+ "requires": {
+ "minimist": "~0.0.1",
+ "wordwrap": "~0.0.2"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.10",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+ "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+ "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
+ "dev": true
+ }
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ }
+ },
+ "orbit-camera-controller": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/orbit-camera-controller/-/orbit-camera-controller-4.0.0.tgz",
+ "integrity": "sha1-bis28OeHhmPDMPUNqbfOaGwncAU=",
+ "requires": {
+ "filtered-vector": "^1.2.1",
+ "gl-mat4": "^1.0.3"
+ }
+ },
+ "original": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+ "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+ "dev": true,
+ "requires": {
+ "url-parse": "^1.4.3"
+ }
+ },
+ "os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
+ "dev": true
+ },
+ "os-homedir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-2.0.0.tgz",
+ "integrity": "sha512-saRNz0DSC5C/I++gFIaJTXoFJMRwiP5zHar5vV3xQ2TkgEw6hDCcU5F272JjUylpiVgBrZNQHnfjkLabTfb92Q=="
+ },
+ "os-locale": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
+ "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "lcid": "^2.0.0",
+ "mem": "^4.0.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "p-defer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+ "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
+ "dev": true
+ },
+ "p-each-series": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz",
+ "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=",
+ "dev": true,
+ "requires": {
+ "p-reduce": "^1.0.0"
+ }
+ },
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+ "dev": true
+ },
+ "p-is-promise": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz",
+ "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
+ "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-map": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz",
+ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==",
+ "dev": true
+ },
+ "p-reduce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz",
+ "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=",
+ "dev": true
+ },
+ "p-try": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+ "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+ "dev": true
+ },
+ "pad-left": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pad-left/-/pad-left-1.0.2.tgz",
+ "integrity": "sha1-GeVzXqmDlaJs7carkm6tEPMQDUw=",
+ "requires": {
+ "repeat-string": "^1.3.0"
+ }
+ },
+ "pako": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.8.tgz",
+ "integrity": "sha512-6i0HVbUfcKaTv+EG8ZTr75az7GFXcLYk9UyLEg7Notv/Ma+z/UG3TCoz6GiNeOrn1E/e63I0X/Hpw18jHOTUnA==",
+ "dev": true
+ },
+ "parallel-transform": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
+ "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
+ "dev": true,
+ "requires": {
+ "cyclist": "~0.2.2",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.1.5"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "param-case": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
+ "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
+ "dev": true,
+ "requires": {
+ "no-case": "^2.2.0"
+ }
+ },
+ "parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "parenthesis": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/parenthesis/-/parenthesis-3.1.7.tgz",
+ "integrity": "sha512-iMtu+HCbLXVrpf6Ys/4YKhcFxbux3xK4ZVB9r+a2kMSqeeQWQoDNYlXIsOjwlT2ldYXZ3k5PVeBnYn7fbAo/Bg=="
+ },
+ "parse-asn1": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
+ "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^4.0.0",
+ "browserify-aes": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "parse-passwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+ "dev": true
+ },
+ "parse-rect": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/parse-rect/-/parse-rect-1.2.0.tgz",
+ "integrity": "sha512-4QZ6KYbnE6RTwg9E0HpLchUM9EZt6DnDxajFZZDSV4p/12ZJEvPO702DZpGvRYEPo00yKDys7jASi+/w7aO8LA==",
+ "requires": {
+ "pick-by-alias": "^1.2.0"
+ }
+ },
+ "parse-svg-path": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
+ "integrity": "sha1-en7A0esG+lMlx9PgCbhZoJtdSes="
+ },
+ "parse-unit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-unit/-/parse-unit-1.0.1.tgz",
+ "integrity": "sha1-fhu21b7zh0wo45JSaiVBFwKR7s8="
+ },
+ "parse5": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
+ "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+ "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+ "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+ },
+ "path-to-regexp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.2.0.tgz",
+ "integrity": "sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA=="
+ },
+ "path-type": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
+ "dev": true,
+ "requires": {
+ "pify": "^3.0.0"
+ }
+ },
+ "pbf": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz",
+ "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==",
+ "requires": {
+ "ieee754": "^1.1.12",
+ "resolve-protobuf-schema": "^2.1.0"
+ }
+ },
+ "pbkdf2": {
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
+ "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
+ "dev": true,
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ },
+ "permutation-parity": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/permutation-parity/-/permutation-parity-1.0.0.tgz",
+ "integrity": "sha1-AXTVH8pwSxG5pLFSsj1Tf9xrXvQ=",
+ "requires": {
+ "typedarray-pool": "^1.0.0"
+ }
+ },
+ "permutation-rank": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/permutation-rank/-/permutation-rank-1.0.0.tgz",
+ "integrity": "sha1-n9mLvOzwj79ZlLXq3JSmLmeUg7U=",
+ "requires": {
+ "invert-permutation": "^1.0.0",
+ "typedarray-pool": "^1.0.0"
+ }
+ },
+ "pick-by-alias": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pick-by-alias/-/pick-by-alias-1.2.0.tgz",
+ "integrity": "sha1-X3yysfIabh6ISgyHhVqko3NhEHs="
+ },
+ "pidtree": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+ "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+ "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pirates": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz",
+ "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==",
+ "dev": true,
+ "requires": {
+ "node-modules-regexp": "^1.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0"
+ }
+ },
+ "planar-dual": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/planar-dual/-/planar-dual-1.0.2.tgz",
+ "integrity": "sha1-tqQjVSOxsMt55fkm+OozXdmC1WM=",
+ "requires": {
+ "compare-angle": "^1.0.0",
+ "dup": "^1.0.0"
+ }
+ },
+ "planar-graph-to-polyline": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/planar-graph-to-polyline/-/planar-graph-to-polyline-1.0.5.tgz",
+ "integrity": "sha1-iCuGBRmbqIv9RkyVUzA1VsUrmIo=",
+ "requires": {
+ "edges-to-adjacency-list": "^1.0.0",
+ "planar-dual": "^1.0.0",
+ "point-in-big-polygon": "^2.0.0",
+ "robust-orientation": "^1.0.1",
+ "robust-sum": "^1.0.0",
+ "two-product": "^1.0.0",
+ "uniq": "^1.0.0"
+ }
+ },
+ "plotly.js": {
+ "version": "1.52.3",
+ "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-1.52.3.tgz",
+ "integrity": "sha512-7szNqbVuhqn4ZgaTpJ9h4+9PzjoXJnSdzjnY5QwHddp/j0xu5kpHCGvkg+WmeF3brK3y8qwEHF/MIFBBa7i0ng==",
+ "requires": {
+ "@plotly/d3-sankey": "0.7.2",
+ "@plotly/d3-sankey-circular": "0.33.1",
+ "@turf/area": "^6.0.1",
+ "@turf/bbox": "^6.0.1",
+ "@turf/centroid": "^6.0.2",
+ "alpha-shape": "^1.0.0",
+ "canvas-fit": "^1.5.0",
+ "color-normalize": "^1.5.0",
+ "color-rgba": "^2.1.1",
+ "convex-hull": "^1.0.3",
+ "country-regex": "^1.1.0",
+ "d3": "^3.5.12",
+ "d3-force": "^1.0.6",
+ "d3-hierarchy": "^1.1.9",
+ "d3-interpolate": "^1.4.0",
+ "delaunay-triangulate": "^1.1.6",
+ "es6-promise": "^3.0.2",
+ "fast-isnumeric": "^1.1.3",
+ "gl-cone3d": "^1.5.1",
+ "gl-contour2d": "^1.1.6",
+ "gl-error3d": "^1.0.15",
+ "gl-heatmap2d": "^1.0.5",
+ "gl-line3d": "1.2.0",
+ "gl-mat4": "^1.2.0",
+ "gl-mesh3d": "^2.3.0",
+ "gl-plot2d": "^1.4.3",
+ "gl-plot3d": "^2.4.4",
+ "gl-pointcloud2d": "^1.0.2",
+ "gl-scatter3d": "^1.2.2",
+ "gl-select-box": "^1.0.3",
+ "gl-spikes2d": "^1.0.2",
+ "gl-streamtube3d": "^1.4.0",
+ "gl-surface3d": "^1.4.6",
+ "gl-text": "^1.1.8",
+ "glslify": "^7.0.0",
+ "has-hover": "^1.0.1",
+ "has-passive-events": "^1.0.0",
+ "is-mobile": "^2.2.0",
+ "mapbox-gl": "1.3.2",
+ "matrix-camera-controller": "^2.1.3",
+ "mouse-change": "^1.4.0",
+ "mouse-event-offset": "^3.0.2",
+ "mouse-wheel": "^1.2.0",
+ "ndarray": "^1.0.18",
+ "ndarray-fill": "^1.0.2",
+ "ndarray-homography": "^1.0.0",
+ "point-cluster": "^3.1.8",
+ "polybooljs": "^1.2.0",
+ "regl": "^1.3.11",
+ "regl-error2d": "^2.0.8",
+ "regl-line2d": "^3.0.15",
+ "regl-scatter2d": "^3.1.7",
+ "regl-splom": "^1.0.8",
+ "right-now": "^1.0.0",
+ "robust-orientation": "^1.1.3",
+ "sane-topojson": "^4.0.0",
+ "strongly-connected-components": "^1.0.1",
+ "superscript-text": "^1.0.0",
+ "svg-path-sdf": "^1.1.3",
+ "tinycolor2": "^1.4.1",
+ "topojson-client": "^2.1.0",
+ "webgl-context": "^2.2.0",
+ "world-calendars": "^1.0.3"
+ }
+ },
+ "pn": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+ "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
+ "dev": true
+ },
+ "point-cluster": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/point-cluster/-/point-cluster-3.1.8.tgz",
+ "integrity": "sha512-7klIr45dpMeZuqjIK9+qBg3m2IhyZJNJkdqjJFw0Olq75FM8ojrTMjClVUrMjNYRVqtwztxCHH71Fyjhg+YwyQ==",
+ "requires": {
+ "array-bounds": "^1.0.1",
+ "array-normalize": "^1.1.4",
+ "binary-search-bounds": "^2.0.4",
+ "bubleify": "^1.1.0",
+ "clamp": "^1.0.1",
+ "defined": "^1.0.0",
+ "dtype": "^2.0.0",
+ "flatten-vertex-data": "^1.0.2",
+ "is-obj": "^1.0.1",
+ "math-log2": "^1.0.1",
+ "parse-rect": "^1.2.0",
+ "pick-by-alias": "^1.2.0"
+ }
+ },
+ "point-in-big-polygon": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/point-in-big-polygon/-/point-in-big-polygon-2.0.0.tgz",
+ "integrity": "sha1-ObYT6mzxfWtD4Yj3fzTETGszulU=",
+ "requires": {
+ "binary-search-bounds": "^1.0.0",
+ "interval-tree-1d": "^1.0.1",
+ "robust-orientation": "^1.1.3",
+ "slab-decomposition": "^1.0.1"
+ },
+ "dependencies": {
+ "binary-search-bounds": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz",
+ "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k="
+ }
+ }
+ },
+ "polybooljs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/polybooljs/-/polybooljs-1.2.0.tgz",
+ "integrity": "sha1-tDkMLgedTCYtOyUExiiNlbp6R1g="
+ },
+ "polytope-closest-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/polytope-closest-point/-/polytope-closest-point-1.0.0.tgz",
+ "integrity": "sha1-5uV/QIGrXox3i4Ee8G4sSK4zjD8=",
+ "requires": {
+ "numeric": "^1.2.6"
+ }
+ },
+ "portfinder": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.20.tgz",
+ "integrity": "sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw==",
+ "dev": true,
+ "requires": {
+ "async": "^1.5.2",
+ "debug": "^2.2.0",
+ "mkdirp": "0.5.x"
+ },
+ "dependencies": {
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "postcss": {
+ "version": "5.2.18",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
+ "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^1.1.3",
+ "js-base64": "^2.1.9",
+ "source-map": "^0.5.6",
+ "supports-color": "^3.2.3"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ }
+ }
+ },
+ "has-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+ "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+ "dev": true,
+ "requires": {
+ "has-flag": "^1.0.0"
+ }
+ }
+ }
+ },
+ "postcss-calc": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz",
+ "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.2",
+ "postcss-message-helpers": "^2.0.0",
+ "reduce-css-calc": "^1.2.6"
+ }
+ },
+ "postcss-colormin": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz",
+ "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=",
+ "dev": true,
+ "requires": {
+ "colormin": "^1.0.5",
+ "postcss": "^5.0.13",
+ "postcss-value-parser": "^3.2.3"
+ }
+ },
+ "postcss-convert-values": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz",
+ "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.11",
+ "postcss-value-parser": "^3.1.2"
+ }
+ },
+ "postcss-discard-comments": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz",
+ "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.14"
+ }
+ },
+ "postcss-discard-duplicates": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz",
+ "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.4"
+ }
+ },
+ "postcss-discard-empty": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz",
+ "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.14"
+ }
+ },
+ "postcss-discard-overridden": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz",
+ "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.16"
+ }
+ },
+ "postcss-discard-unused": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz",
+ "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.14",
+ "uniqs": "^2.0.0"
+ }
+ },
+ "postcss-filter-plugins": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz",
+ "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.4"
+ }
+ },
+ "postcss-merge-idents": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz",
+ "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.1",
+ "postcss": "^5.0.10",
+ "postcss-value-parser": "^3.1.1"
+ }
+ },
+ "postcss-merge-longhand": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz",
+ "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.4"
+ }
+ },
+ "postcss-merge-rules": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz",
+ "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=",
+ "dev": true,
+ "requires": {
+ "browserslist": "^1.5.2",
+ "caniuse-api": "^1.5.2",
+ "postcss": "^5.0.4",
+ "postcss-selector-parser": "^2.2.2",
+ "vendors": "^1.0.0"
+ },
+ "dependencies": {
+ "browserslist": {
+ "version": "1.7.7",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
+ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
+ "dev": true,
+ "requires": {
+ "caniuse-db": "^1.0.30000639",
+ "electron-to-chromium": "^1.2.7"
+ }
+ }
+ }
+ },
+ "postcss-message-helpers": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz",
+ "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=",
+ "dev": true
+ },
+ "postcss-minify-font-values": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz",
+ "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1",
+ "postcss": "^5.0.4",
+ "postcss-value-parser": "^3.0.2"
+ }
+ },
+ "postcss-minify-gradients": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz",
+ "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.12",
+ "postcss-value-parser": "^3.3.0"
+ }
+ },
+ "postcss-minify-params": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz",
+ "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=",
+ "dev": true,
+ "requires": {
+ "alphanum-sort": "^1.0.1",
+ "postcss": "^5.0.2",
+ "postcss-value-parser": "^3.0.2",
+ "uniqs": "^2.0.0"
+ }
+ },
+ "postcss-minify-selectors": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz",
+ "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=",
+ "dev": true,
+ "requires": {
+ "alphanum-sort": "^1.0.2",
+ "has": "^1.0.1",
+ "postcss": "^5.0.14",
+ "postcss-selector-parser": "^2.0.0"
+ }
+ },
+ "postcss-modules-extract-imports": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz",
+ "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==",
+ "dev": true,
+ "requires": {
+ "postcss": "^6.0.1"
+ },
+ "dependencies": {
+ "postcss": {
+ "version": "6.0.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
+ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "source-map": "^0.6.1",
+ "supports-color": "^5.4.0"
+ }
+ }
+ }
+ },
+ "postcss-modules-local-by-default": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz",
+ "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=",
+ "dev": true,
+ "requires": {
+ "css-selector-tokenizer": "^0.7.0",
+ "postcss": "^6.0.1"
+ },
+ "dependencies": {
+ "postcss": {
+ "version": "6.0.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
+ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "source-map": "^0.6.1",
+ "supports-color": "^5.4.0"
+ }
+ }
+ }
+ },
+ "postcss-modules-scope": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz",
+ "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=",
+ "dev": true,
+ "requires": {
+ "css-selector-tokenizer": "^0.7.0",
+ "postcss": "^6.0.1"
+ },
+ "dependencies": {
+ "postcss": {
+ "version": "6.0.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
+ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "source-map": "^0.6.1",
+ "supports-color": "^5.4.0"
+ }
+ }
+ }
+ },
+ "postcss-modules-values": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz",
+ "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=",
+ "dev": true,
+ "requires": {
+ "icss-replace-symbols": "^1.1.0",
+ "postcss": "^6.0.1"
+ },
+ "dependencies": {
+ "postcss": {
+ "version": "6.0.23",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
+ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "source-map": "^0.6.1",
+ "supports-color": "^5.4.0"
+ }
+ }
+ }
+ },
+ "postcss-normalize-charset": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz",
+ "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.5"
+ }
+ },
+ "postcss-normalize-url": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz",
+ "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=",
+ "dev": true,
+ "requires": {
+ "is-absolute-url": "^2.0.0",
+ "normalize-url": "^1.4.0",
+ "postcss": "^5.0.14",
+ "postcss-value-parser": "^3.2.3"
+ }
+ },
+ "postcss-ordered-values": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz",
+ "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.4",
+ "postcss-value-parser": "^3.0.1"
+ }
+ },
+ "postcss-reduce-idents": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz",
+ "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.4",
+ "postcss-value-parser": "^3.0.2"
+ }
+ },
+ "postcss-reduce-initial": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz",
+ "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=",
+ "dev": true,
+ "requires": {
+ "postcss": "^5.0.4"
+ }
+ },
+ "postcss-reduce-transforms": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz",
+ "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.1",
+ "postcss": "^5.0.8",
+ "postcss-value-parser": "^3.0.1"
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz",
+ "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=",
+ "dev": true,
+ "requires": {
+ "flatten": "^1.0.2",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ },
+ "postcss-svgo": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz",
+ "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=",
+ "dev": true,
+ "requires": {
+ "is-svg": "^2.0.0",
+ "postcss": "^5.0.14",
+ "postcss-value-parser": "^3.2.3",
+ "svgo": "^0.7.0"
+ }
+ },
+ "postcss-unique-selectors": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz",
+ "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=",
+ "dev": true,
+ "requires": {
+ "alphanum-sort": "^1.0.1",
+ "postcss": "^5.0.4",
+ "uniqs": "^2.0.0"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ },
+ "postcss-zindex": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz",
+ "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.1",
+ "postcss": "^5.0.4",
+ "uniqs": "^2.0.0"
+ }
+ },
+ "potpack": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.1.tgz",
+ "integrity": "sha512-15vItUAbViaYrmaB/Pbw7z6qX2xENbFSTA7Ii4tgbPtasxm5v6ryKhKtL91tpWovDJzTiZqdwzhcFBCwiMVdVw=="
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
+ },
+ "prepend-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+ "dev": true
+ },
+ "prettier": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz",
+ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==",
+ "dev": true
+ },
+ "pretty-error": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz",
+ "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=",
+ "dev": true,
+ "requires": {
+ "renderkid": "^2.0.1",
+ "utila": "~0.4"
+ }
+ },
+ "pretty-format": {
+ "version": "24.0.0",
+ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.0.0.tgz",
+ "integrity": "sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0",
+ "ansi-styles": "^3.2.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ }
+ }
+ },
+ "private": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
+ "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
+ "dev": true
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
+ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "requires": {
+ "asap": "~2.0.3"
+ }
+ },
+ "promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+ "dev": true
+ },
+ "prompts": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.0.3.tgz",
+ "integrity": "sha512-H8oWEoRZpybm6NV4to9/1limhttEo13xK62pNvn2JzY0MA03p7s0OjtmhXyon3uJmxiJJVSuUwEJFFssI3eBiQ==",
+ "dev": true,
+ "requires": {
+ "kleur": "^3.0.2",
+ "sisteransi": "^1.0.0"
+ }
+ },
+ "prop-types": {
+ "version": "15.7.2",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
+ "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
+ "requires": {
+ "loose-envify": "^1.4.0",
+ "object-assign": "^4.1.1",
+ "react-is": "^16.8.1"
+ }
+ },
+ "protocol-buffers-schema": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.4.0.tgz",
+ "integrity": "sha512-G/2kcamPF2S49W5yaMGdIpkG6+5wZF0fzBteLKgEHjbNzqjZQ85aAs1iJGto31EJaSTkNvHs5IXuHSaTLWBAiA=="
+ },
+ "proxy-addr": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.4.tgz",
+ "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==",
+ "dev": true,
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.8.0"
+ }
+ },
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+ "dev": true
+ },
+ "pseudomap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+ "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.1.31",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz",
+ "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==",
+ "dev": true
+ },
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "dev": true,
+ "requires": {
+ "duplexify": "^3.6.0",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "pxls": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/pxls/-/pxls-2.3.2.tgz",
+ "integrity": "sha512-pQkwgbLqWPcuES5iEmGa10OlCf5xG0blkIF3dg7PpRZShbTYcvAdfFfGL03SMrkaSUaa/V0UpN9HWg40O2AIIw==",
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "compute-dims": "^1.1.0",
+ "flip-pixels": "^1.0.2",
+ "is-browser": "^2.1.0",
+ "is-buffer": "^2.0.3",
+ "to-uint8": "^1.4.1"
+ },
+ "dependencies": {
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A=="
+ }
+ }
+ },
+ "q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+ "dev": true
+ },
+ "quantize": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/quantize/-/quantize-1.0.2.tgz",
+ "integrity": "sha1-0lrCAKd7bXD0ASfKFxoQ4zyFRt4="
+ },
+ "quat-slerp": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/quat-slerp/-/quat-slerp-1.0.1.tgz",
+ "integrity": "sha1-K6oVzjprvcMkHZcusXKDE57Wnyk=",
+ "requires": {
+ "gl-quat": "^1.0.0"
+ }
+ },
+ "query-string": {
+ "version": "6.9.0",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.9.0.tgz",
+ "integrity": "sha512-KG4bhCFYapExLsUHrFt+kQVEegF2agm4cpF/VNc6pZVthIfCc/GK8t8VyNIE3nyXG9DK3Tf2EGkxjR6/uRdYsA==",
+ "requires": {
+ "decode-uri-component": "^0.2.0",
+ "split-on-first": "^1.0.0",
+ "strict-uri-encode": "^2.0.0"
+ }
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+ "dev": true
+ },
+ "querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "dev": true
+ },
+ "querystringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.0.tgz",
+ "integrity": "sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==",
+ "dev": true
+ },
+ "quickselect": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz",
+ "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="
+ },
+ "quote-stream": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/quote-stream/-/quote-stream-0.0.0.tgz",
+ "integrity": "sha1-zeKelMQJsW4Z3HCYuJtmWPlyHTs=",
+ "requires": {
+ "minimist": "0.0.8",
+ "through2": "~0.4.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "object-keys": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
+ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY="
+ },
+ "through2": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz",
+ "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=",
+ "requires": {
+ "readable-stream": "~1.0.17",
+ "xtend": "~2.1.1"
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
+ "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
+ "requires": {
+ "object-keys": "~0.4.0"
+ }
+ }
+ }
+ },
+ "raf": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
+ "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
+ "requires": {
+ "performance-now": "^2.1.0"
+ }
+ },
+ "railroad-diagrams": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+ "integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
+ "dev": true
+ },
+ "randexp": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
+ "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+ "dev": true,
+ "requires": {
+ "discontinuous-range": "1.0.0",
+ "ret": "~0.1.10"
+ }
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+ "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
+ "dev": true
+ },
+ "rat-vec": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/rat-vec/-/rat-vec-1.1.1.tgz",
+ "integrity": "sha1-Dd4rZrezS7G80qI4BerIBth/0X8=",
+ "requires": {
+ "big-rat": "^1.0.3"
+ }
+ },
+ "raw-body": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+ "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.0.0",
+ "http-errors": "1.6.3",
+ "iconv-lite": "0.4.23",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "raw-loader": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
+ "integrity": "sha1-DD0L6u2KAclm2Xh793goElKpeao=",
+ "dev": true
+ },
+ "rc-align": {
+ "version": "2.4.5",
+ "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-2.4.5.tgz",
+ "integrity": "sha512-nv9wYUYdfyfK+qskThf4BQUSIadeI/dCsfaMZfNEoxm9HwOIioQ+LyqmMK6jWHAZQgOzMLaqawhuBXlF63vgjw==",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "dom-align": "^1.7.0",
+ "prop-types": "^15.5.8",
+ "rc-util": "^4.0.4"
+ }
+ },
+ "rc-animate": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.11.1.tgz",
+ "integrity": "sha512-1NyuCGFJG/0Y+9RKh5y/i/AalUCA51opyyS/jO2seELpgymZm2u9QV3xwODwEuzkmeQ1BDPxMLmYLcTJedPlkQ==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.6",
+ "css-animation": "^1.3.2",
+ "prop-types": "15.x",
+ "raf": "^3.4.0",
+ "rc-util": "^4.15.3",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-calendar": {
+ "version": "9.15.10",
+ "resolved": "https://registry.npmjs.org/rc-calendar/-/rc-calendar-9.15.10.tgz",
+ "integrity": "sha512-xh1A3rYejKskAvkjnd9BcHXFbBnAYsHMGHBdtoAkbwp43B6yEieNL0g0Tzz8s1gApDZV2j5vF1jJ9IIpPYFNLw==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "2.x",
+ "moment": "2.x",
+ "prop-types": "^15.5.8",
+ "rc-trigger": "^2.2.0",
+ "rc-util": "^4.1.1",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-cascader": {
+ "version": "0.17.5",
+ "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-0.17.5.tgz",
+ "integrity": "sha512-WYMVcxU0+Lj+xLr4YYH0+yXODumvNXDcVEs5i7L1mtpWwYkubPV/zbQpn+jGKFCIW/hOhjkU4J1db8/P/UKE7A==",
+ "requires": {
+ "array-tree-filter": "^2.1.0",
+ "prop-types": "^15.5.8",
+ "rc-trigger": "^2.2.0",
+ "rc-util": "^4.0.4",
+ "react-lifecycles-compat": "^3.0.4",
+ "shallow-equal": "^1.0.0",
+ "warning": "^4.0.1"
+ }
+ },
+ "rc-checkbox": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/rc-checkbox/-/rc-checkbox-2.1.8.tgz",
+ "integrity": "sha512-6qOgh0/by0nVNASx6LZnhRTy17Etcgav+IrI7kL9V9kcDZ/g7K14JFlqrtJ3NjDq/Kyn+BPI1st1XvbkhfaJeg==",
+ "requires": {
+ "babel-runtime": "^6.23.0",
+ "classnames": "2.x",
+ "prop-types": "15.x",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-collapse": {
+ "version": "1.11.8",
+ "resolved": "https://registry.npmjs.org/rc-collapse/-/rc-collapse-1.11.8.tgz",
+ "integrity": "sha512-8EhfPyScTYljkbRuIoHniSwZagD5UPpZ3CToYgoNYWC85L2qCbPYF7+OaC713FOrIkp6NbfNqXsITNxmDAmxog==",
+ "requires": {
+ "classnames": "2.x",
+ "css-animation": "1.x",
+ "prop-types": "^15.5.6",
+ "rc-animate": "2.x",
+ "react-is": "^16.7.0",
+ "react-lifecycles-compat": "^3.0.4",
+ "shallowequal": "^1.1.0"
+ }
+ },
+ "rc-dialog": {
+ "version": "7.6.1",
+ "resolved": "https://registry.npmjs.org/rc-dialog/-/rc-dialog-7.6.1.tgz",
+ "integrity": "sha512-KUKf+2eZ4YL+lnXMG3hR4ZtIhC9glfH27NtTVz3gcoDIPAf3uUvaXVRNoDCiSi+OGKLyIb/b6EoidFh6nQC5Wg==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "rc-animate": "2.x",
+ "rc-util": "^4.16.1"
+ }
+ },
+ "rc-drawer": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/rc-drawer/-/rc-drawer-3.1.3.tgz",
+ "integrity": "sha512-2z+RdxmzXyZde/1OhVMfDR1e/GBswFeWSZ7FS3Fdd0qhgVdpV1wSzILzzxRaT481ItB5hOV+e8pZT07vdJE8kg==",
+ "requires": {
+ "classnames": "^2.2.6",
+ "rc-util": "^4.16.1",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-dropdown": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-2.4.1.tgz",
+ "integrity": "sha512-p0XYn0wrOpAZ2fUGE6YJ6U8JBNc5ASijznZ6dkojdaEfQJAeZtV9KMEewhxkVlxGSbbdXe10ptjBlTEW9vEwEg==",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "classnames": "^2.2.6",
+ "prop-types": "^15.5.8",
+ "rc-trigger": "^2.5.1",
+ "react-lifecycles-compat": "^3.0.2"
+ }
+ },
+ "rc-editor-core": {
+ "version": "0.8.10",
+ "resolved": "https://registry.npmjs.org/rc-editor-core/-/rc-editor-core-0.8.10.tgz",
+ "integrity": "sha512-T3aHpeMCIYA1sdAI7ynHHjXy5fqp83uPlD68ovZ0oClTSc3tbHmyCxXlA+Ti4YgmcpCYv7avF6a+TIbAka53kw==",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "classnames": "^2.2.5",
+ "draft-js": "^0.10.0",
+ "immutable": "^3.7.4",
+ "lodash": "^4.16.5",
+ "prop-types": "^15.5.8",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "rc-editor-mention": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/rc-editor-mention/-/rc-editor-mention-1.1.13.tgz",
+ "integrity": "sha512-3AOmGir91Fi2ogfRRaXLtqlNuIwQpvla7oUnGHS1+3eo7b+fUp5IlKcagqtwUBB5oDNofoySXkLBxzWvSYNp/Q==",
+ "requires": {
+ "babel-runtime": "^6.23.0",
+ "classnames": "^2.2.5",
+ "dom-scroll-into-view": "^1.2.0",
+ "draft-js": "~0.10.0",
+ "immutable": "~3.7.4",
+ "prop-types": "^15.5.8",
+ "rc-animate": "^2.3.0",
+ "rc-editor-core": "~0.8.3"
+ }
+ },
+ "rc-form": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/rc-form/-/rc-form-2.4.11.tgz",
+ "integrity": "sha512-8BL+FNlFLTOY/A5X6tU35GQJLSIpsmqpwn/tFAYQTczXc4dMJ33ggtH248Cum8+LS0jLTsJKG2L4Qp+1CkY+sA==",
+ "requires": {
+ "async-validator": "~1.11.3",
+ "babel-runtime": "6.x",
+ "create-react-class": "^15.5.3",
+ "dom-scroll-into-view": "1.x",
+ "hoist-non-react-statics": "^3.3.0",
+ "lodash": "^4.17.4",
+ "rc-util": "^4.15.3",
+ "warning": "^4.0.3"
+ }
+ },
+ "rc-hammerjs": {
+ "version": "0.6.9",
+ "resolved": "https://registry.npmjs.org/rc-hammerjs/-/rc-hammerjs-0.6.9.tgz",
+ "integrity": "sha512-4llgWO3RgLyVbEqUdGsDfzUDqklRlQW5VEhE3x35IvhV+w//VPRG34SBavK3D2mD/UaLKaohgU41V4agiftC8g==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "hammerjs": "^2.0.8",
+ "prop-types": "^15.5.9"
+ }
+ },
+ "rc-input-number": {
+ "version": "4.5.7",
+ "resolved": "https://registry.npmjs.org/rc-input-number/-/rc-input-number-4.5.7.tgz",
+ "integrity": "sha512-99PrQ90sTOKyyj7eu0VzwxY17xQ+bwG1XTQd+bTwFQ+IOUkIw7L4qSAYxt58sVYL+Cw+bu/RAtT2IpT9yC2pCQ==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.0",
+ "prop-types": "^15.5.7",
+ "rc-util": "^4.5.1",
+ "rmc-feedback": "^2.0.0"
+ }
+ },
+ "rc-mentions": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/rc-mentions/-/rc-mentions-0.4.2.tgz",
+ "integrity": "sha512-DTZurQzacLXOfVuiHydGzqkq7cFMHXF18l2jZ9PhWUn2cqvOSY3W4osN0Pq29AOMOBpcxdZCzgc7Lb0r/bgkDw==",
+ "requires": {
+ "@ant-design/create-react-context": "^0.2.4",
+ "classnames": "^2.2.6",
+ "rc-menu": "^7.4.22",
+ "rc-trigger": "^2.6.2",
+ "rc-util": "^4.6.0",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-menu": {
+ "version": "7.5.5",
+ "resolved": "https://registry.npmjs.org/rc-menu/-/rc-menu-7.5.5.tgz",
+ "integrity": "sha512-4YJXJgrpUGEA1rMftXN7bDhrV5rPB8oBJoHqT+GVXtIWCanfQxEnM3fmhHQhatL59JoAFMZhJaNzhJIk4FUWCQ==",
+ "requires": {
+ "classnames": "2.x",
+ "dom-scroll-into-view": "1.x",
+ "mini-store": "^2.0.0",
+ "mutationobserver-shim": "^0.3.2",
+ "rc-animate": "^2.10.1",
+ "rc-trigger": "^2.3.0",
+ "rc-util": "^4.13.0",
+ "resize-observer-polyfill": "^1.5.0",
+ "shallowequal": "^1.1.0"
+ }
+ },
+ "rc-notification": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-3.3.1.tgz",
+ "integrity": "sha512-U5+f4BmBVfMSf3OHSLyRagsJ74yKwlrQAtbbL5ijoA0F2C60BufwnOcHG18tVprd7iaIjzZt1TKMmQSYSvgrig==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "2.x",
+ "prop-types": "^15.5.8",
+ "rc-animate": "2.x",
+ "rc-util": "^4.0.4"
+ }
+ },
+ "rc-pagination": {
+ "version": "1.20.14",
+ "resolved": "https://registry.npmjs.org/rc-pagination/-/rc-pagination-1.20.14.tgz",
+ "integrity": "sha512-sNKwbFrxiqATqcIIShfrFs8BT03n4UUwTAMYae+JhHTmILQmXdvimEnZbVuWcno6G02DAJcLrFpmkn1h2tmEJw==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.6",
+ "prop-types": "^15.5.7",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-progress": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/rc-progress/-/rc-progress-2.5.3.tgz",
+ "integrity": "sha512-K2fa4CnqGehLZoMrdmBeZ86ONSTVcdk5FlqetbwJ3R/+42XfqhwQVOjWp2MH4P7XSQOMAGcNOy1SFfCP3415sg==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "prop-types": "^15.5.8"
+ }
+ },
+ "rc-rate": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/rc-rate/-/rc-rate-2.5.1.tgz",
+ "integrity": "sha512-3iJkNJT8xlHklPCdeZtUZmJmRVUbr6AHRlfSsztfYTXVlHrv2TcPn3XkHsH+12j812WVB7gvilS2j3+ffjUHXg==",
+ "requires": {
+ "classnames": "^2.2.5",
+ "prop-types": "^15.5.8",
+ "rc-util": "^4.3.0",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-resize-observer": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/rc-resize-observer/-/rc-resize-observer-0.1.3.tgz",
+ "integrity": "sha512-uzOQEwx83xdQSFOkOAM7x7GHIQKYnrDV4dWxtCxyG1BS1pkfJ4EvDeMfsvAJHSYkQXVBu+sgRHGbRtLG3qiuUg==",
+ "requires": {
+ "classnames": "^2.2.1",
+ "rc-util": "^4.13.0",
+ "resize-observer-polyfill": "^1.5.1"
+ }
+ },
+ "rc-select": {
+ "version": "9.2.3",
+ "resolved": "https://registry.npmjs.org/rc-select/-/rc-select-9.2.3.tgz",
+ "integrity": "sha512-WhswxOMWiNnkXRbxyrj0kiIvyCfo/BaRPaYbsDetSIAU2yEDwKHF798blCP5u86KLOBKBvtxWLFCkSsQw1so5w==",
+ "requires": {
+ "babel-runtime": "^6.23.0",
+ "classnames": "2.x",
+ "component-classes": "1.x",
+ "dom-scroll-into-view": "1.x",
+ "prop-types": "^15.5.8",
+ "raf": "^3.4.0",
+ "rc-animate": "2.x",
+ "rc-menu": "^7.3.0",
+ "rc-trigger": "^2.5.4",
+ "rc-util": "^4.0.4",
+ "react-lifecycles-compat": "^3.0.2",
+ "warning": "^4.0.2"
+ }
+ },
+ "rc-slider": {
+ "version": "8.7.1",
+ "resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-8.7.1.tgz",
+ "integrity": "sha512-WMT5mRFUEcrLWwTxsyS8jYmlaMsTVCZIGENLikHsNv+tE8ThU2lCoPfi/xFNUfJFNFSBFP3MwPez9ZsJmNp13g==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.5",
+ "prop-types": "^15.5.4",
+ "rc-tooltip": "^3.7.0",
+ "rc-util": "^4.0.4",
+ "react-lifecycles-compat": "^3.0.4",
+ "shallowequal": "^1.1.0",
+ "warning": "^4.0.3"
+ }
+ },
+ "rc-steps": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/rc-steps/-/rc-steps-3.5.0.tgz",
+ "integrity": "sha512-2Vkkrpa7PZbg7qPsqTNzVDov4u78cmxofjjnIHiGB9+9rqKS8oTLPzbW2uiWDr3Lk+yGwh8rbpGO1E6VAgBCOg==",
+ "requires": {
+ "babel-runtime": "^6.23.0",
+ "classnames": "^2.2.3",
+ "lodash": "^4.17.5",
+ "prop-types": "^15.5.7"
+ }
+ },
+ "rc-switch": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/rc-switch/-/rc-switch-1.9.0.tgz",
+ "integrity": "sha512-Isas+egaK6qSk64jaEw4GgPStY4umYDbT7ZY93bZF1Af+b/JEsKsJdNOU2qG3WI0Z6tXo2DDq0kJCv8Yhu0zww==",
+ "requires": {
+ "classnames": "^2.2.1",
+ "prop-types": "^15.5.6",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-table": {
+ "version": "6.10.15",
+ "resolved": "https://registry.npmjs.org/rc-table/-/rc-table-6.10.15.tgz",
+ "integrity": "sha512-LAr0M/gqt+irOjvPNBLApmQ0CUHNOfKsEBhu1uIuB3OlN1ynA9z+sdoTQyNd9+8NSl0MYnQOOfhtLChAY7nU0A==",
+ "requires": {
+ "classnames": "^2.2.5",
+ "component-classes": "^1.2.6",
+ "lodash": "^4.17.5",
+ "mini-store": "^2.0.0",
+ "prop-types": "^15.5.8",
+ "rc-util": "^4.13.0",
+ "react-lifecycles-compat": "^3.0.2",
+ "shallowequal": "^1.0.2"
+ }
+ },
+ "rc-tabs": {
+ "version": "9.7.0",
+ "resolved": "https://registry.npmjs.org/rc-tabs/-/rc-tabs-9.7.0.tgz",
+ "integrity": "sha512-kvmgp8/MfLzFZ06hWHignqomFQ5nF7BqKr5O1FfhE4VKsGrep52YSF/1MvS5oe0NPcI9XGNS2p751C5v6cYDpQ==",
+ "requires": {
+ "@ant-design/create-react-context": "^0.2.4",
+ "babel-runtime": "6.x",
+ "classnames": "2.x",
+ "lodash": "^4.17.5",
+ "prop-types": "15.x",
+ "raf": "^3.4.1",
+ "rc-hammerjs": "~0.6.0",
+ "rc-util": "^4.0.4",
+ "react-lifecycles-compat": "^3.0.4",
+ "resize-observer-polyfill": "^1.5.1",
+ "warning": "^4.0.3"
+ }
+ },
+ "rc-time-picker": {
+ "version": "3.7.3",
+ "resolved": "https://registry.npmjs.org/rc-time-picker/-/rc-time-picker-3.7.3.tgz",
+ "integrity": "sha512-Lv1Mvzp9fRXhXEnRLO4nW6GLNxUkfAZ3RsiIBsWjGjXXvMNjdr4BX/ayElHAFK0DoJqOhm7c5tjmIYpEOwcUXg==",
+ "requires": {
+ "classnames": "2.x",
+ "moment": "2.x",
+ "prop-types": "^15.5.8",
+ "raf": "^3.4.1",
+ "rc-trigger": "^2.2.0",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-tooltip": {
+ "version": "3.7.3",
+ "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-3.7.3.tgz",
+ "integrity": "sha512-dE2ibukxxkrde7wH9W8ozHKUO4aQnPZ6qBHtrTH9LoO836PjDdiaWO73fgPB05VfJs9FbZdmGPVEbXCeOP99Ww==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "prop-types": "^15.5.8",
+ "rc-trigger": "^2.2.2"
+ }
+ },
+ "rc-tree": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/rc-tree/-/rc-tree-2.1.4.tgz",
+ "integrity": "sha512-Xey794Iavgs8YldFlXcZLOhfcIhlX5Oz/yfKufknBXf2AlZCOkc7aHqSM9uTF7fBPtTGPhPxNEfOqHfY7b7xng==",
+ "requires": {
+ "@ant-design/create-react-context": "^0.2.4",
+ "classnames": "2.x",
+ "prop-types": "^15.5.8",
+ "rc-animate": "^2.6.0",
+ "rc-util": "^4.5.1",
+ "react-lifecycles-compat": "^3.0.4",
+ "warning": "^4.0.3"
+ }
+ },
+ "rc-tree-select": {
+ "version": "2.9.4",
+ "resolved": "https://registry.npmjs.org/rc-tree-select/-/rc-tree-select-2.9.4.tgz",
+ "integrity": "sha512-0HQkXAN4XbfBW20CZYh3G+V+VMrjX42XRtDCpyv6PDUm5vikC0Ob682ZBCVS97Ww2a5Hf6Ajmu0ahWEdIEpwhg==",
+ "requires": {
+ "classnames": "^2.2.1",
+ "dom-scroll-into-view": "^1.2.1",
+ "prop-types": "^15.5.8",
+ "raf": "^3.4.0",
+ "rc-animate": "^2.8.2",
+ "rc-tree": "~2.1.0",
+ "rc-trigger": "^3.0.0",
+ "rc-util": "^4.5.0",
+ "react-lifecycles-compat": "^3.0.4",
+ "shallowequal": "^1.0.2",
+ "warning": "^4.0.1"
+ },
+ "dependencies": {
+ "rc-trigger": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-3.0.0.tgz",
+ "integrity": "sha512-hQxbbJpo23E2QnYczfq3Ec5J5tVl2mUDhkqxrEsQAqk16HfADQg+iKNWzEYXyERSncdxfnzYuaBgy764mNRzTA==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.6",
+ "prop-types": "15.x",
+ "raf": "^3.4.0",
+ "rc-align": "^2.4.1",
+ "rc-animate": "^3.0.0-rc.1",
+ "rc-util": "^4.15.7"
+ },
+ "dependencies": {
+ "rc-animate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-3.0.0.tgz",
+ "integrity": "sha512-+ANeyCei4lWSJHWTcocywdYAy6lpRdBva/7Fs3nBBiAngW/W+Gmx+gQEcsmcgQBqziWUYnR91Bk12ltR3GBHPA==",
+ "requires": {
+ "@ant-design/css-animation": "^1.7.2",
+ "classnames": "^2.2.6",
+ "raf": "^3.4.0",
+ "rc-util": "^4.15.3"
+ }
+ }
+ }
+ }
+ }
+ },
+ "rc-trigger": {
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-2.6.5.tgz",
+ "integrity": "sha512-m6Cts9hLeZWsTvWnuMm7oElhf+03GOjOLfTuU0QmdB9ZrW7jR2IpI5rpNM7i9MvAAlMAmTx5Zr7g3uu/aMvZAw==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.6",
+ "prop-types": "15.x",
+ "rc-align": "^2.4.0",
+ "rc-animate": "2.x",
+ "rc-util": "^4.4.0",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "rc-upload": {
+ "version": "2.9.4",
+ "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-2.9.4.tgz",
+ "integrity": "sha512-WXt0HGxXyzLrPV6iec/96Rbl/6dyrAW8pKuY6wwD7yFYwfU5bjgKjv7vC8KNMJ6wzitFrZjnoiogNL3dF9dj3Q==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.5",
+ "prop-types": "^15.5.7",
+ "warning": "4.x"
+ }
+ },
+ "rc-util": {
+ "version": "4.20.7",
+ "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-4.20.7.tgz",
+ "integrity": "sha512-wzL7bGTimuUK/SgXlUIxcXYdQ27eAaBd8PTuiKz7ZI/Mbm5R6ZcRTWpeIGG56t+/79oi++kobfKbJrPxMTkKIw==",
+ "requires": {
+ "add-dom-event-listener": "^1.1.0",
+ "prop-types": "^15.5.10",
+ "react-is": "^16.12.0",
+ "react-lifecycles-compat": "^3.0.4",
+ "shallowequal": "^1.1.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ }
+ }
+ },
+ "react": {
+ "version": "16.8.3",
+ "resolved": "https://registry.npmjs.org/react/-/react-16.8.3.tgz",
+ "integrity": "sha512-3UoSIsEq8yTJuSu0luO1QQWYbgGEILm+eJl2QN/VLDi7hL+EN18M3q3oVZwmVzzBJ3DkM7RMdRwBmZZ+b4IzSA==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "scheduler": "^0.13.3"
+ }
+ },
+ "react-ace": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-9.1.1.tgz",
+ "integrity": "sha512-dL0w6GwtnS1opsOoWhJaF7rF7xCM+NOEOfePmDfiaeU+EyZQ6nRWDBgyzKsuiB3hyXH3G9D6FX37ur/LKUdKjA==",
+ "requires": {
+ "ace-builds": "^1.4.6",
+ "diff-match-patch": "^1.0.4",
+ "lodash.get": "^4.4.2",
+ "lodash.isequal": "^4.5.0",
+ "prop-types": "^15.7.2"
+ }
+ },
+ "react-dom": {
+ "version": "16.8.3",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.3.tgz",
+ "integrity": "sha512-ttMem9yJL4/lpItZAQ2NTFAbV7frotHk5DZEHXUOws2rMmrsvh1Na7ThGT0dTzUIl6pqTOi5tYREfL8AEna3lA==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "scheduler": "^0.13.3"
+ }
+ },
+ "react-draggable": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.2.0.tgz",
+ "integrity": "sha512-5wFq//gEoeTYprnd4ze8GrFc+Rbnx+9RkOMR3vk4EbWxj02U6L6T3yrlKeiw4X5CtjD2ma2+b3WujghcXNRzkw==",
+ "requires": {
+ "classnames": "^2.2.5",
+ "prop-types": "^15.6.0"
+ }
+ },
+ "react-grid-layout": {
+ "version": "0.18.2",
+ "resolved": "https://registry.npmjs.org/react-grid-layout/-/react-grid-layout-0.18.2.tgz",
+ "integrity": "sha512-PF4LfGoJ2SbuCJtQ3wPapC4ZuZcqKKNWv36m/QnxbfEw8C48HcGB8162wPkIUc9mfKPLE/MBlHjRSaPQ3VlwYw==",
+ "requires": {
+ "classnames": "2.x",
+ "lodash.isequal": "^4.0.0",
+ "prop-types": "^15.0.0",
+ "react-draggable": "^4.0.0",
+ "react-resizable": "^1.9.0"
+ }
+ },
+ "react-is": {
+ "version": "16.8.3",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.3.tgz",
+ "integrity": "sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA=="
+ },
+ "react-lazy-load": {
+ "version": "3.0.13",
+ "resolved": "https://registry.npmjs.org/react-lazy-load/-/react-lazy-load-3.0.13.tgz",
+ "integrity": "sha1-OwqS0zbUPT8Nc8vm81sXBQsIuCQ=",
+ "requires": {
+ "eventlistener": "0.0.1",
+ "lodash.debounce": "^4.0.0",
+ "lodash.throttle": "^4.0.0",
+ "prop-types": "^15.5.8"
+ }
+ },
+ "react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
+ "react-pivottable": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/react-pivottable/-/react-pivottable-0.9.0.tgz",
+ "integrity": "sha512-fs1pGV5z4BvOXL4iLu79kKCLgR5XINW2ZredJHoPqXEMbJaIv50Eoec1XowWf3i3Dvdb8EYgvNqlF2ggC4GJOw==",
+ "requires": {
+ "immutability-helper": "^2.3.1",
+ "prop-types": "^15.5.10",
+ "react-draggable": "^3.0.3",
+ "react-sortablejs": "^1.3.4",
+ "sortablejs": "^1.6.1"
+ },
+ "dependencies": {
+ "react-draggable": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-3.3.2.tgz",
+ "integrity": "sha512-oaz8a6enjbPtx5qb0oDWxtDNuybOylvto1QLydsXgKmwT7e3GXC2eMVDwEMIUYJIFqVG72XpOv673UuuAq6LhA==",
+ "requires": {
+ "classnames": "^2.2.5",
+ "prop-types": "^15.6.0"
+ }
+ }
+ }
+ },
+ "react-resizable": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/react-resizable/-/react-resizable-1.10.1.tgz",
+ "integrity": "sha512-Jd/bKOKx6+19NwC4/aMLRu/J9/krfxlDnElP41Oc+oLiUWs/zwV1S9yBfBZRnqAwQb6vQ/HRSk3bsSWGSgVbpw==",
+ "requires": {
+ "prop-types": "15.x",
+ "react-draggable": "^4.0.3"
+ }
+ },
+ "react-slick": {
+ "version": "0.25.2",
+ "resolved": "https://registry.npmjs.org/react-slick/-/react-slick-0.25.2.tgz",
+ "integrity": "sha512-8MNH/NFX/R7zF6W/w+FS5VXNyDusF+XDW1OU0SzODEU7wqYB+ZTGAiNJ++zVNAVqCAHdyCybScaUB+FCZOmBBw==",
+ "requires": {
+ "classnames": "^2.2.5",
+ "enquire.js": "^2.1.6",
+ "json2mq": "^0.2.0",
+ "lodash.debounce": "^4.0.8",
+ "resize-observer-polyfill": "^1.5.0"
+ }
+ },
+ "react-sortable-hoc": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz",
+ "integrity": "sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg==",
+ "requires": {
+ "@babel/runtime": "^7.2.0",
+ "invariant": "^2.2.4",
+ "prop-types": "^15.5.7"
+ }
+ },
+ "react-sortablejs": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/react-sortablejs/-/react-sortablejs-1.5.1.tgz",
+ "integrity": "sha512-bKIc1UVhjZt55Nb6WZFxZ8Jwyngg8CTt+w+iG1pA5k9LQsg1J0X6nLppHatSSDZDECtRZKp2z47tmmhPRJNj4g==",
+ "requires": {
+ "prop-types": ">=15.0.0"
+ }
+ },
+ "react-test-renderer": {
+ "version": "16.8.3",
+ "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.3.tgz",
+ "integrity": "sha512-rjJGYebduKNZH0k1bUivVrRLX04JfIQ0FKJLPK10TAb06XWhfi4gTobooF9K/DEFNW98iGac3OSxkfIJUN9Mdg==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.1.1",
+ "prop-types": "^15.6.2",
+ "react-is": "^16.8.3",
+ "scheduler": "^0.13.3"
+ }
+ },
+ "react-virtualized": {
+ "version": "9.21.2",
+ "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.21.2.tgz",
+ "integrity": "sha512-oX7I7KYiUM7lVXQzmhtF4Xg/4UA5duSA+/ZcAvdWlTLFCoFYq1SbauJT5gZK9cZS/wdYR6TPGpX/dqzvTqQeBA==",
+ "requires": {
+ "babel-runtime": "^6.26.0",
+ "clsx": "^1.0.1",
+ "dom-helpers": "^5.0.0",
+ "loose-envify": "^1.3.0",
+ "prop-types": "^15.6.0",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
+ "read-pkg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+ "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
+ "dev": true,
+ "requires": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz",
+ "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0",
+ "read-pkg": "^3.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "realpath-native": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz",
+ "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==",
+ "dev": true,
+ "requires": {
+ "util.promisify": "^1.0.0"
+ }
+ },
+ "redeyed": {
+ "version": "0.4.4",
+ "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-0.4.4.tgz",
+ "integrity": "sha1-N+mQpvKyGyoRwuakj9QTVpjLqX8=",
+ "requires": {
+ "esprima": "~1.0.4"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz",
+ "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0="
+ }
+ }
+ },
+ "reduce-css-calc": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
+ "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^0.4.2",
+ "math-expression-evaluator": "^1.2.14",
+ "reduce-function-call": "^1.0.1"
+ },
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+ "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+ "dev": true
+ }
+ }
+ },
+ "reduce-function-call": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz",
+ "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^0.4.2"
+ },
+ "dependencies": {
+ "balanced-match": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+ "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+ "dev": true
+ }
+ }
+ },
+ "reduce-simplicial-complex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/reduce-simplicial-complex/-/reduce-simplicial-complex-1.0.0.tgz",
+ "integrity": "sha1-dNaWovg196bc2SBl/YxRgfLt+Lw=",
+ "requires": {
+ "cell-orientation": "^1.0.1",
+ "compare-cell": "^1.0.0",
+ "compare-oriented-cell": "^1.0.1"
+ }
+ },
+ "regenerate": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
+ "integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg=="
+ },
+ "regenerate-unicode-properties": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz",
+ "integrity": "sha512-s5NGghCE4itSlUS+0WUj88G6cfMVMmH8boTPNvABf8od+2dhT9WDlWu8n01raQAJZMOK8Ch6jSexaRO7swd6aw==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+ "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+ },
+ "regenerator-transform": {
+ "version": "0.13.4",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz",
+ "integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==",
+ "dev": true,
+ "requires": {
+ "private": "^0.1.6"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "regex-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/regex-regex/-/regex-regex-1.0.0.tgz",
+ "integrity": "sha1-kEih6uuHD01IDavHb8Qs3MC8OnI="
+ },
+ "regexp-tree": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.5.tgz",
+ "integrity": "sha512-nUmxvfJyAODw+0B13hj8CFVAxhe7fDEAgJgaotBu3nnR+IgGgZq59YedJP5VYTlkEfqjuK6TuRpnymKdatLZfQ==",
+ "dev": true
+ },
+ "regexp.prototype.flags": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
+ "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+ "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.1.5",
+ "is-regex": "^1.0.5",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimleft": "^2.1.1",
+ "string.prototype.trimright": "^2.1.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "is-callable": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+ },
+ "is-regex": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "object-inspect": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+ "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "string.prototype.trimleft": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+ "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimstart": "^1.0.0"
+ }
+ },
+ "string.prototype.trimright": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+ "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimend": "^1.0.0"
+ }
+ }
+ }
+ },
+ "regexpp": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz",
+ "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==",
+ "dev": true
+ },
+ "regexpu-core": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.4.0.tgz",
+ "integrity": "sha512-eDDWElbwwI3K0Lo6CqbQbA6FwgtCz4kYTarrri1okfkRLZAqstU+B3voZBCjg8Fl6iq0gXrJG6MvRgLthfvgOA==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0",
+ "regenerate-unicode-properties": "^7.0.0",
+ "regjsgen": "^0.5.0",
+ "regjsparser": "^0.6.0",
+ "unicode-match-property-ecmascript": "^1.0.4",
+ "unicode-match-property-value-ecmascript": "^1.0.2"
+ }
+ },
+ "regjsgen": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz",
+ "integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz",
+ "integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ }
+ },
+ "regl": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/regl/-/regl-1.4.2.tgz",
+ "integrity": "sha512-wc/kE6kGmGfQk3G9f1Pai4TZ0K1pWxkD1Jeaj6CxJwEiB1jwHgEpqD84G2t7F0DmNXfQh7IUnoG1opxoONJ7Xg=="
+ },
+ "regl-error2d": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/regl-error2d/-/regl-error2d-2.0.8.tgz",
+ "integrity": "sha512-5nszdicXbimRUnYB42i+O7KPcla7PzI62nZLCP6qVRKlQCf3rSrWbikMNd1S84LE8+deWHWcb8rZ/v7rZ9qmmw==",
+ "requires": {
+ "array-bounds": "^1.0.1",
+ "bubleify": "^1.2.0",
+ "color-normalize": "^1.5.0",
+ "flatten-vertex-data": "^1.0.2",
+ "object-assign": "^4.1.1",
+ "pick-by-alias": "^1.2.0",
+ "to-float32": "^1.0.1",
+ "update-diff": "^1.1.0"
+ }
+ },
+ "regl-line2d": {
+ "version": "3.0.15",
+ "resolved": "https://registry.npmjs.org/regl-line2d/-/regl-line2d-3.0.15.tgz",
+ "integrity": "sha512-RuQbg9iZ6MyuInG8izF6zjQ/2g4qL6sg1egiuFalWzaGSvuve/IWBsIcqKTlwpiEsRt9b4cHu9NYs2fLt1gYJw==",
+ "requires": {
+ "array-bounds": "^1.0.1",
+ "array-normalize": "^1.1.4",
+ "bubleify": "^1.2.0",
+ "color-normalize": "^1.5.0",
+ "earcut": "^2.1.5",
+ "es6-weak-map": "^2.0.3",
+ "flatten-vertex-data": "^1.0.2",
+ "glslify": "^7.0.0",
+ "object-assign": "^4.1.1",
+ "parse-rect": "^1.2.0",
+ "pick-by-alias": "^1.2.0",
+ "to-float32": "^1.0.1"
+ }
+ },
+ "regl-scatter2d": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/regl-scatter2d/-/regl-scatter2d-3.1.8.tgz",
+ "integrity": "sha512-Z9MYAUx9t8e3MsiHBbJAEstbIqauXxzcL9DmuKXQuRWfCMF2DBytYJtE0FpbQU6639wEMAJ54SEIlISWF8sQ2g==",
+ "requires": {
+ "array-range": "^1.0.1",
+ "array-rearrange": "^2.2.2",
+ "clamp": "^1.0.1",
+ "color-id": "^1.1.0",
+ "color-normalize": "1.5.0",
+ "color-rgba": "^2.1.1",
+ "flatten-vertex-data": "^1.0.2",
+ "glslify": "^7.0.0",
+ "image-palette": "^2.1.0",
+ "is-iexplorer": "^1.0.0",
+ "object-assign": "^4.1.1",
+ "parse-rect": "^1.2.0",
+ "pick-by-alias": "^1.2.0",
+ "point-cluster": "^3.1.8",
+ "to-float32": "^1.0.1",
+ "update-diff": "^1.1.0"
+ }
+ },
+ "regl-splom": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/regl-splom/-/regl-splom-1.0.8.tgz",
+ "integrity": "sha512-4GQTgcArCbGLsXhgalWVBxeW7OXllnu+Gvil/4SbQQmtiqLCl+xgF79pISKY9mLXTlobxiX7cVKdjGjp25559A==",
+ "requires": {
+ "array-bounds": "^1.0.1",
+ "array-range": "^1.0.1",
+ "bubleify": "^1.2.0",
+ "color-alpha": "^1.0.4",
+ "defined": "^1.0.0",
+ "flatten-vertex-data": "^1.0.2",
+ "left-pad": "^1.3.0",
+ "parse-rect": "^1.2.0",
+ "pick-by-alias": "^1.2.0",
+ "point-cluster": "^3.1.8",
+ "raf": "^3.4.1",
+ "regl-scatter2d": "^3.1.2"
+ }
+ },
+ "relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
+ "dev": true
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "renderkid": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.3.tgz",
+ "integrity": "sha512-z8CLQp7EZBPCwCnncgf9C4XAi3WR0dv+uWu/PjIyhhAb5d6IJ/QZqlHFprHeKT+59//V6BNUsLbvN8+2LarxGA==",
+ "dev": true,
+ "requires": {
+ "css-select": "^1.1.0",
+ "dom-converter": "^0.2",
+ "htmlparser2": "^3.3.0",
+ "strip-ansi": "^3.0.0",
+ "utila": "^0.4.0"
+ }
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc="
+ },
+ "request": {
+ "version": "2.88.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
+ "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.0",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.4.3",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
+ "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.24",
+ "punycode": "^1.4.1"
+ }
+ }
+ }
+ },
+ "request-promise-core": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
+ "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.11"
+ }
+ },
+ "request-promise-native": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz",
+ "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==",
+ "dev": true,
+ "requires": {
+ "request-promise-core": "1.1.2",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+ "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+ "dev": true
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
+ },
+ "resize-observer-polyfill": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+ "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+ },
+ "resolve": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz",
+ "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==",
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-cwd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+ "dev": true,
+ "requires": {
+ "resolve-from": "^3.0.0"
+ },
+ "dependencies": {
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "dev": true
+ }
+ }
+ },
+ "resolve-dir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+ "dev": true,
+ "requires": {
+ "expand-tilde": "^2.0.0",
+ "global-modules": "^1.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "resolve-pathname": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
+ "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
+ },
+ "resolve-protobuf-schema": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz",
+ "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==",
+ "requires": {
+ "protocol-buffers-schema": "^3.3.1"
+ }
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "resumer": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz",
+ "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=",
+ "requires": {
+ "through": "~2.3.4"
+ }
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "right-align": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
+ "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
+ "requires": {
+ "align-text": "^0.1.1"
+ }
+ },
+ "right-now": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/right-now/-/right-now-1.0.0.tgz",
+ "integrity": "sha1-bolgne69fc2vja7Mmuo5z1haCRg="
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "rmc-feedback": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/rmc-feedback/-/rmc-feedback-2.0.0.tgz",
+ "integrity": "sha512-5PWOGOW7VXks/l3JzlOU9NIxRpuaSS8d9zA3UULUCuTKnpwBHNvv1jSJzxgbbCQeYzROWUpgKI4za3X4C/mKmQ==",
+ "requires": {
+ "babel-runtime": "6.x",
+ "classnames": "^2.2.5"
+ }
+ },
+ "robust-compress": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/robust-compress/-/robust-compress-1.0.0.tgz",
+ "integrity": "sha1-TPYsSzGNgwhRYBK7jBF1Lzkymxs="
+ },
+ "robust-determinant": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/robust-determinant/-/robust-determinant-1.1.0.tgz",
+ "integrity": "sha1-jsrnm3nKqz509t6+IjflORon6cc=",
+ "requires": {
+ "robust-compress": "^1.0.0",
+ "robust-scale": "^1.0.0",
+ "robust-sum": "^1.0.0",
+ "two-product": "^1.0.0"
+ }
+ },
+ "robust-dot-product": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/robust-dot-product/-/robust-dot-product-1.0.0.tgz",
+ "integrity": "sha1-yboBeL0sMEv9cl9Y6Inx2UYARVM=",
+ "requires": {
+ "robust-sum": "^1.0.0",
+ "two-product": "^1.0.0"
+ }
+ },
+ "robust-in-sphere": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/robust-in-sphere/-/robust-in-sphere-1.1.3.tgz",
+ "integrity": "sha1-HFiD0WpOkjkpR27zSBmFe/Kpz3U=",
+ "requires": {
+ "robust-scale": "^1.0.0",
+ "robust-subtract": "^1.0.0",
+ "robust-sum": "^1.0.0",
+ "two-product": "^1.0.0"
+ }
+ },
+ "robust-linear-solve": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/robust-linear-solve/-/robust-linear-solve-1.0.0.tgz",
+ "integrity": "sha1-DNasUEBpGm8qo81jEdcokFyjofE=",
+ "requires": {
+ "robust-determinant": "^1.1.0"
+ }
+ },
+ "robust-orientation": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/robust-orientation/-/robust-orientation-1.1.3.tgz",
+ "integrity": "sha1-2v9bANO+TmByLw6cAVbvln8cIEk=",
+ "requires": {
+ "robust-scale": "^1.0.2",
+ "robust-subtract": "^1.0.0",
+ "robust-sum": "^1.0.0",
+ "two-product": "^1.0.2"
+ }
+ },
+ "robust-product": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/robust-product/-/robust-product-1.0.0.tgz",
+ "integrity": "sha1-aFJQAHzbunzx3nW/9tKScBEJir4=",
+ "requires": {
+ "robust-scale": "^1.0.0",
+ "robust-sum": "^1.0.0"
+ }
+ },
+ "robust-scale": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/robust-scale/-/robust-scale-1.0.2.tgz",
+ "integrity": "sha1-d1Ey7QlULQKOWLLMecBikLz3jDI=",
+ "requires": {
+ "two-product": "^1.0.2",
+ "two-sum": "^1.0.0"
+ }
+ },
+ "robust-segment-intersect": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/robust-segment-intersect/-/robust-segment-intersect-1.0.1.tgz",
+ "integrity": "sha1-MlK2oPwboUreaRXMvgnLzpqrHBw=",
+ "requires": {
+ "robust-orientation": "^1.1.3"
+ }
+ },
+ "robust-subtract": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/robust-subtract/-/robust-subtract-1.0.0.tgz",
+ "integrity": "sha1-4LFk4e2LpOOl3aRaEgODSNvtPpo="
+ },
+ "robust-sum": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/robust-sum/-/robust-sum-1.0.0.tgz",
+ "integrity": "sha1-FmRuUlKStNJdgnV6KGlV4Lv6U9k="
+ },
+ "rst-selector-parser": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
+ "integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=",
+ "dev": true,
+ "requires": {
+ "lodash.flattendeep": "^4.4.0",
+ "nearley": "^2.7.10"
+ }
+ },
+ "rsvp": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz",
+ "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==",
+ "dev": true
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "run-queue": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+ "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1"
+ }
+ },
+ "rw": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
+ "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q="
+ },
+ "rxjs": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+ "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "sane": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/sane/-/sane-3.1.0.tgz",
+ "integrity": "sha512-G5GClRRxT1cELXfdAq7UKtUsv8q/ZC5k8lQGmjEm4HcAl3HzBy68iglyNCmw4+0tiXPCBZntslHlRhbnsSws+Q==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "capture-exit": "^1.2.0",
+ "exec-sh": "^0.2.0",
+ "execa": "^1.0.0",
+ "fb-watchman": "^2.0.0",
+ "fsevents": "^1.2.3",
+ "micromatch": "^3.1.4",
+ "minimist": "^1.1.1",
+ "walker": "~1.0.5",
+ "watch": "~0.18.0"
+ }
+ },
+ "sane-topojson": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/sane-topojson/-/sane-topojson-4.0.0.tgz",
+ "integrity": "sha512-bJILrpBboQfabG3BNnHI2hZl52pbt80BE09u4WhnrmzuF2JbMKZdl62G5glXskJ46p+gxE2IzOwGj/awR4g8AA=="
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
+ "scheduler": {
+ "version": "0.13.3",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.3.tgz",
+ "integrity": "sha512-UxN5QRYWtpR1egNWzJcVLk8jlegxAugswQc984lD3kU7NuobsO37/sRfbpTdBjtnD5TBNFA2Q2oLV5+UmPSmEQ==",
+ "requires": {
+ "loose-envify": "^1.1.0",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ },
+ "select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+ "dev": true
+ },
+ "selfsigned": {
+ "version": "1.10.4",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.4.tgz",
+ "integrity": "sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw==",
+ "dev": true,
+ "requires": {
+ "node-forge": "0.7.5"
+ }
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
+ },
+ "send": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+ "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.6.2",
+ "mime": "1.4.1",
+ "ms": "2.0.0",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.0",
+ "statuses": "~1.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "mime": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+ "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "serialize-javascript": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz",
+ "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==",
+ "dev": true
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.2",
+ "send": "0.16.2"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
+ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ }
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ },
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "shallow-copy": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz",
+ "integrity": "sha1-QV9CcC1z2BAzApLMXuhurhoRoXA="
+ },
+ "shallow-equal": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
+ "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
+ },
+ "shallowequal": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
+ "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
+ },
+ "sharkdown": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/sharkdown/-/sharkdown-0.1.1.tgz",
+ "integrity": "sha512-exwooSpmo5s45lrexgz6Q0rFQM574wYIX3iDZ7RLLqOb7IAoQZu9nxlZODU972g19sR69OIpKP2cpHTzU+PHIg==",
+ "requires": {
+ "cardinal": "~0.4.2",
+ "minimist": "0.0.5",
+ "split": "~0.2.10"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz",
+ "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY="
+ }
+ }
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "shell-quote": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
+ "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
+ "dev": true
+ },
+ "shellwords": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz",
+ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "signum": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/signum/-/signum-0.0.0.tgz",
+ "integrity": "sha1-q1UbEAM1EHCnBHg/GgnF52kfnPY="
+ },
+ "simplicial-complex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-1.0.0.tgz",
+ "integrity": "sha1-bDOk7Wn81Nkbe8rdOzC2NoPq4kE=",
+ "requires": {
+ "bit-twiddle": "^1.0.0",
+ "union-find": "^1.0.0"
+ }
+ },
+ "simplicial-complex-boundary": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/simplicial-complex-boundary/-/simplicial-complex-boundary-1.0.1.tgz",
+ "integrity": "sha1-csn/HiTeqjdMm7L6DL8MCB6++BU=",
+ "requires": {
+ "boundary-cells": "^2.0.0",
+ "reduce-simplicial-complex": "^1.0.0"
+ }
+ },
+ "simplicial-complex-contour": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/simplicial-complex-contour/-/simplicial-complex-contour-1.0.2.tgz",
+ "integrity": "sha1-iQqsrChDZTQBEFRc8mKaJuBL+dE=",
+ "requires": {
+ "marching-simplex-table": "^1.0.0",
+ "ndarray": "^1.0.15",
+ "ndarray-sort": "^1.0.0",
+ "typedarray-pool": "^1.1.0"
+ }
+ },
+ "simplify-planar-graph": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/simplify-planar-graph/-/simplify-planar-graph-2.0.1.tgz",
+ "integrity": "sha1-vIWJNyXzLo+oriVoE5hEbSy892Y=",
+ "requires": {
+ "robust-orientation": "^1.0.1",
+ "simplicial-complex": "^0.3.3"
+ },
+ "dependencies": {
+ "bit-twiddle": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/bit-twiddle/-/bit-twiddle-0.0.2.tgz",
+ "integrity": "sha1-wurruVKjuUrMFASX4c3NLxoz9Y4="
+ },
+ "simplicial-complex": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/simplicial-complex/-/simplicial-complex-0.3.3.tgz",
+ "integrity": "sha1-TDDK1X+eRXKd2PMGyHU1efRr6Z4=",
+ "requires": {
+ "bit-twiddle": "~0.0.1",
+ "union-find": "~0.0.3"
+ }
+ },
+ "union-find": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/union-find/-/union-find-0.0.4.tgz",
+ "integrity": "sha1-uFSzMBYZva0USwAUx4+W6sDS8PY="
+ }
+ }
+ },
+ "sisteransi": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.0.tgz",
+ "integrity": "sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==",
+ "dev": true
+ },
+ "slab-decomposition": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/slab-decomposition/-/slab-decomposition-1.0.2.tgz",
+ "integrity": "sha1-He1WdU1AixBznxRRA9/GGAf2UTQ=",
+ "requires": {
+ "binary-search-bounds": "^1.0.0",
+ "functional-red-black-tree": "^1.0.0",
+ "robust-orientation": "^1.1.3"
+ },
+ "dependencies": {
+ "binary-search-bounds": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-1.0.0.tgz",
+ "integrity": "sha1-MjyjF+PypA9CRMclX1OEpbIHu2k="
+ }
+ }
+ },
+ "slash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
+ "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+ "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ }
+ },
+ "sockjs": {
+ "version": "0.3.19",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
+ "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==",
+ "dev": true,
+ "requires": {
+ "faye-websocket": "^0.10.0",
+ "uuid": "^3.0.1"
+ }
+ },
+ "sockjs-client": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.3.0.tgz",
+ "integrity": "sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.5",
+ "eventsource": "^1.0.7",
+ "faye-websocket": "~0.11.1",
+ "inherits": "^2.0.3",
+ "json3": "^3.3.2",
+ "url-parse": "^1.4.3"
+ },
+ "dependencies": {
+ "faye-websocket": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.1.tgz",
+ "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ }
+ }
+ },
+ "sort-keys": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+ "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
+ "dev": true,
+ "requires": {
+ "is-plain-obj": "^1.0.0"
+ }
+ },
+ "sortablejs": {
+ "version": "1.10.2",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
+ "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
+ },
+ "source-list-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ },
+ "source-map-resolve": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
+ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.1",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-support": {
+ "version": "0.5.10",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.10.tgz",
+ "integrity": "sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+ "dev": true
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+ },
+ "spdx-correct": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
+ "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
+ "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
+ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
+ "integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
+ "dev": true
+ },
+ "spdy": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.0.tgz",
+ "integrity": "sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "^1.2.7",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "spdy-transport": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "readable-stream": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz",
+ "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz",
+ "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "split": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz",
+ "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=",
+ "requires": {
+ "through": "2"
+ }
+ },
+ "split-on-first": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
+ "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="
+ },
+ "split-polygon": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/split-polygon/-/split-polygon-1.0.0.tgz",
+ "integrity": "sha1-DqzIoTanaxKj2VJW6n2kXbDC0kc=",
+ "requires": {
+ "robust-dot-product": "^1.0.0",
+ "robust-sum": "^1.0.0"
+ }
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
+ },
+ "sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "ssri": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz",
+ "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "stack-trace": {
+ "version": "0.0.9",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz",
+ "integrity": "sha1-qPbq7KkGdMMz58Q5U/J1tFFRBpU="
+ },
+ "stack-utils": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz",
+ "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==",
+ "dev": true
+ },
+ "static-eval": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.5.tgz",
+ "integrity": "sha512-nNbV6LbGtMBgv7e9LFkt5JV8RVlRsyJrphfAt9tOtBBW/SfnzZDf2KnS72an8e434A+9e/BmJuTxeGPvrAK7KA==",
+ "requires": {
+ "escodegen": "^1.11.1"
+ }
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "static-module": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/static-module/-/static-module-1.5.0.tgz",
+ "integrity": "sha1-J9qYg8QajNCSNvhC8MHrxu32PYY=",
+ "requires": {
+ "concat-stream": "~1.6.0",
+ "duplexer2": "~0.0.2",
+ "escodegen": "~1.3.2",
+ "falafel": "^2.1.0",
+ "has": "^1.0.0",
+ "object-inspect": "~0.4.0",
+ "quote-stream": "~0.0.0",
+ "readable-stream": "~1.0.27-1",
+ "shallow-copy": "~0.0.1",
+ "static-eval": "~0.2.0",
+ "through2": "~0.4.1"
+ },
+ "dependencies": {
+ "escodegen": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.3.3.tgz",
+ "integrity": "sha1-8CQBb1qI4Eb9EgBQVek5gC5sXyM=",
+ "requires": {
+ "esprima": "~1.1.1",
+ "estraverse": "~1.5.0",
+ "esutils": "~1.0.0",
+ "source-map": "~0.1.33"
+ }
+ },
+ "esprima": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.1.1.tgz",
+ "integrity": "sha1-W28VR/TRAuZw4UDFCb5ncdautUk="
+ },
+ "estraverse": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz",
+ "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E="
+ },
+ "esutils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz",
+ "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA="
+ },
+ "object-inspect": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-0.4.0.tgz",
+ "integrity": "sha1-9RV8EWwUVbJDsG7pdwM5LFrYn+w="
+ },
+ "object-keys": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
+ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY="
+ },
+ "source-map": {
+ "version": "0.1.43",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz",
+ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=",
+ "optional": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ },
+ "static-eval": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/static-eval/-/static-eval-0.2.4.tgz",
+ "integrity": "sha1-t9NNg4k3uWn5ZBygfUj47eJj6ns=",
+ "requires": {
+ "escodegen": "~0.0.24"
+ },
+ "dependencies": {
+ "escodegen": {
+ "version": "0.0.28",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-0.0.28.tgz",
+ "integrity": "sha1-Dk/xcV8yh3XWyrUaxEpAbNer/9M=",
+ "requires": {
+ "esprima": "~1.0.2",
+ "estraverse": "~1.3.0",
+ "source-map": ">= 0.1.2"
+ }
+ },
+ "esprima": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz",
+ "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0="
+ },
+ "estraverse": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.3.2.tgz",
+ "integrity": "sha1-N8K4k+8T1yPydth41g2FNRUqbEI="
+ }
+ }
+ },
+ "through2": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz",
+ "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=",
+ "requires": {
+ "readable-stream": "~1.0.17",
+ "xtend": "~2.1.1"
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
+ "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
+ "requires": {
+ "object-keys": "~0.4.0"
+ }
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+ "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
+ "dev": true
+ },
+ "stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+ "dev": true
+ },
+ "stream-browserify": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "stream-each": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+ "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "stream-http": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "dev": true,
+ "requires": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "stream-shift": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
+ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI="
+ },
+ "strict-uri-encode": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
+ "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
+ },
+ "string-convert": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
+ "integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c="
+ },
+ "string-length": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz",
+ "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=",
+ "dev": true,
+ "requires": {
+ "astral-regex": "^1.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "string-split-by": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/string-split-by/-/string-split-by-1.0.0.tgz",
+ "integrity": "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A==",
+ "requires": {
+ "parenthesis": "^3.1.5"
+ }
+ },
+ "string-to-arraybuffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-to-arraybuffer/-/string-to-arraybuffer-1.0.2.tgz",
+ "integrity": "sha512-DaGZidzi93dwjQen5I2osxR9ERS/R7B1PFyufNMnzhj+fmlDQAc1DSDIJVJhgI8Oq221efIMbABUBdPHDRt43Q==",
+ "requires": {
+ "atob-lite": "^2.0.0",
+ "is-base64": "^0.1.0"
+ },
+ "dependencies": {
+ "atob-lite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz",
+ "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY="
+ }
+ }
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "string.prototype.padend": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz",
+ "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+ "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.1.5",
+ "is-regex": "^1.0.5",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimleft": "^2.1.1",
+ "string.prototype.trimright": "^2.1.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "object-inspect": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+ "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "string.prototype.trimleft": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+ "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimstart": "^1.0.0"
+ }
+ },
+ "string.prototype.trimright": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+ "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimend": "^1.0.0"
+ }
+ }
+ }
+ },
+ "string.prototype.trim": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz",
+ "integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "es-abstract": "^1.5.0",
+ "function-bind": "^1.0.2"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+ "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.1.5",
+ "is-regex": "^1.0.5",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimleft": "^2.1.1",
+ "string.prototype.trimright": "^2.1.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "is-callable": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+ },
+ "is-regex": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "object-inspect": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+ "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "string.prototype.trimleft": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+ "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimstart": "^1.0.0"
+ }
+ },
+ "string.prototype.trimright": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+ "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimend": "^1.0.0"
+ }
+ }
+ }
+ },
+ "string.prototype.trimleft": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz",
+ "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "function-bind": "^1.1.1"
+ }
+ },
+ "string.prototype.trimright": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz",
+ "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "function-bind": "^1.1.1"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+ "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.1.5",
+ "is-regex": "^1.0.5",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimleft": "^2.1.1",
+ "string.prototype.trimright": "^2.1.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "is-callable": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+ },
+ "is-regex": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "object-inspect": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+ "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "string.prototype.trimleft": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+ "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimstart": "^1.0.0"
+ }
+ },
+ "string.prototype.trimright": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+ "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimend": "^1.0.0"
+ }
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz",
+ "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==",
+ "dev": true
+ },
+ "strongly-connected-components": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strongly-connected-components/-/strongly-connected-components-1.0.1.tgz",
+ "integrity": "sha1-CSDitN9nyOrulsa2I0/inoc9upk="
+ },
+ "supercluster": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-6.0.2.tgz",
+ "integrity": "sha512-aa0v2HURjBTOpbcknilcfxGDuArM8khklKSmZ/T8ZXL0BuRwb5aRw95lz+2bmWpFvCXDX/+FzqHxmg0TIaJErw==",
+ "requires": {
+ "kdbush": "^3.0.0"
+ }
+ },
+ "superscript-text": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/superscript-text/-/superscript-text-1.0.0.tgz",
+ "integrity": "sha1-58snUlZzYN9QvrBhDOjfPXHY39g="
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "surface-nets": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/surface-nets/-/surface-nets-1.0.2.tgz",
+ "integrity": "sha1-5DPIy7qUpydMb0yZVStGG/H8eks=",
+ "requires": {
+ "ndarray-extract-contour": "^1.0.0",
+ "triangulate-hypercube": "^1.0.0",
+ "zero-crossings": "^1.0.0"
+ }
+ },
+ "svg-arc-to-cubic-bezier": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
+ "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="
+ },
+ "svg-path-bounds": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/svg-path-bounds/-/svg-path-bounds-1.0.1.tgz",
+ "integrity": "sha1-v0WLeDcmv1NDG0Yz8nkvYHSNn3Q=",
+ "requires": {
+ "abs-svg-path": "^0.1.1",
+ "is-svg-path": "^1.0.1",
+ "normalize-svg-path": "^1.0.0",
+ "parse-svg-path": "^0.1.2"
+ },
+ "dependencies": {
+ "normalize-svg-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.0.1.tgz",
+ "integrity": "sha1-b3Ka1rcLtMpO/y/ksQdInv4dVv4=",
+ "requires": {
+ "svg-arc-to-cubic-bezier": "^3.0.0"
+ }
+ }
+ }
+ },
+ "svg-path-sdf": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/svg-path-sdf/-/svg-path-sdf-1.1.3.tgz",
+ "integrity": "sha512-vJJjVq/R5lSr2KLfVXVAStktfcfa1pNFjFOgyJnzZFXlO/fDZ5DmM8FpnSKKzLPfEYTVeXuVBTHF296TpxuJVg==",
+ "requires": {
+ "bitmap-sdf": "^1.0.0",
+ "draw-svg-path": "^1.0.0",
+ "is-svg-path": "^1.0.1",
+ "parse-svg-path": "^0.1.2",
+ "svg-path-bounds": "^1.0.1"
+ }
+ },
+ "svgo": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
+ "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=",
+ "dev": true,
+ "requires": {
+ "coa": "~1.0.1",
+ "colors": "~1.1.2",
+ "csso": "~2.3.1",
+ "js-yaml": "~3.7.0",
+ "mkdirp": "~0.5.1",
+ "sax": "~1.2.1",
+ "whet.extend": "~0.9.9"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
+ "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^2.6.0"
+ }
+ }
+ }
+ },
+ "symbol-tree": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz",
+ "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=",
+ "dev": true
+ },
+ "table": {
+ "version": "5.4.6",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz",
+ "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.10.2",
+ "lodash": "^4.17.14",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+ "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "lodash": {
+ "version": "4.17.15",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "tape": {
+ "version": "4.13.2",
+ "resolved": "https://registry.npmjs.org/tape/-/tape-4.13.2.tgz",
+ "integrity": "sha512-waWwC/OqYVE9TS6r1IynlP2sEdk4Lfo6jazlgkuNkPTHIbuG2BTABIaKdlQWwPeB6Oo4ksZ1j33Yt0NTOAlYMQ==",
+ "requires": {
+ "deep-equal": "~1.1.1",
+ "defined": "~1.0.0",
+ "dotignore": "~0.1.2",
+ "for-each": "~0.3.3",
+ "function-bind": "~1.1.1",
+ "glob": "~7.1.6",
+ "has": "~1.0.3",
+ "inherits": "~2.0.4",
+ "is-regex": "~1.0.5",
+ "minimist": "~1.2.0",
+ "object-inspect": "~1.7.0",
+ "resolve": "~1.15.1",
+ "resumer": "~0.0.0",
+ "string.prototype.trim": "~1.2.1",
+ "through": "~2.3.8"
+ },
+ "dependencies": {
+ "deep-equal": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
+ "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+ "requires": {
+ "is-arguments": "^1.0.4",
+ "is-date-object": "^1.0.1",
+ "is-regex": "^1.0.4",
+ "object-is": "^1.0.1",
+ "object-keys": "^1.1.1",
+ "regexp.prototype.flags": "^1.2.0"
+ }
+ },
+ "es-abstract": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+ "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.1.5",
+ "is-regex": "^1.0.5",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimleft": "^2.1.1",
+ "string.prototype.trimright": "^2.1.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "is-callable": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+ },
+ "is-regex": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "object-inspect": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz",
+ "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw=="
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
+ },
+ "resolve": {
+ "version": "1.15.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
+ "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "string.prototype.trim": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz",
+ "integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1"
+ }
+ },
+ "string.prototype.trimleft": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
+ "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimstart": "^1.0.0"
+ }
+ },
+ "string.prototype.trimright": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
+ "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5",
+ "string.prototype.trimend": "^1.0.0"
+ }
+ }
+ }
+ },
+ "terser": {
+ "version": "3.16.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-3.16.1.tgz",
+ "integrity": "sha512-JDJjgleBROeek2iBcSNzOHLKsB/MdDf+E/BOAJ0Tk9r7p9/fVobfv7LMJ/g/k3v9SXdmjZnIlFd5nfn/Rt0Xow==",
+ "dev": true,
+ "requires": {
+ "commander": "~2.17.1",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.9"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "2.17.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+ "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
+ "dev": true
+ }
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz",
+ "integrity": "sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA==",
+ "dev": true,
+ "requires": {
+ "cacache": "^11.0.2",
+ "find-cache-dir": "^2.0.0",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^1.4.0",
+ "source-map": "^0.6.1",
+ "terser": "^3.16.1",
+ "webpack-sources": "^1.1.0",
+ "worker-farm": "^1.5.2"
+ },
+ "dependencies": {
+ "cacache": {
+ "version": "11.3.2",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
+ "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.3",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.3",
+ "graceful-fs": "^4.1.15",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.2",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "ssri": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+ "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+ "dev": true
+ }
+ }
+ },
+ "test-exclude": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.1.0.tgz",
+ "integrity": "sha512-gwf0S2fFsANC55fSeSqpb8BYk6w3FDvwZxfNjeF6FRgvFa43r+7wRiA/Q0IxoRU37wB/LE8IQ4221BsNucTaCA==",
+ "dev": true,
+ "requires": {
+ "arrify": "^1.0.1",
+ "minimatch": "^3.0.4",
+ "read-pkg-up": "^4.0.0",
+ "require-main-filename": "^1.0.1"
+ }
+ },
+ "text-cache": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/text-cache/-/text-cache-4.2.2.tgz",
+ "integrity": "sha512-zky+UDYiX0a/aPw/YTBD+EzKMlCTu1chFuCMZeAkgoRiceySdROu1V2kJXhCbtEdBhiOviYnAdGiSYl58HW0ZQ==",
+ "requires": {
+ "vectorize-text": "^3.2.1"
+ }
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "throat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz",
+ "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+ },
+ "through2": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz",
+ "integrity": "sha1-QaucZ7KdVyCQcUEOHXp6lozTrUg=",
+ "requires": {
+ "readable-stream": ">=1.0.33-1 <1.1.0-0",
+ "xtend": ">=4.0.0 <4.1.0-0"
+ }
+ },
+ "thunky": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.3.tgz",
+ "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==",
+ "dev": true
+ },
+ "timers-browserify": {
+ "version": "2.0.10",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz",
+ "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==",
+ "dev": true,
+ "requires": {
+ "setimmediate": "^1.0.4"
+ }
+ },
+ "tiny-invariant": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz",
+ "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA=="
+ },
+ "tiny-warning": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
+ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
+ },
+ "tinycolor2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
+ "integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
+ },
+ "tinyqueue": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz",
+ "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "tmpl": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
+ "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
+ "dev": true
+ },
+ "to-array-buffer": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/to-array-buffer/-/to-array-buffer-3.2.0.tgz",
+ "integrity": "sha512-zN33mwi0gpL+7xW1ITLfJ48CEj6ZQW0ZAP0MU+2W3kEY0PAIncyuxmD4OqkUVhPAbTP7amq9j/iwvZKYS+lzSQ==",
+ "requires": {
+ "flatten-vertex-data": "^1.0.2",
+ "is-blob": "^2.0.1",
+ "string-to-arraybuffer": "^1.0.0"
+ }
+ },
+ "to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
+ "dev": true
+ },
+ "to-float32": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-float32/-/to-float32-1.0.1.tgz",
+ "integrity": "sha512-nOy2WSwae3xhZbc+05xiCuU3ZPPmH0L4Rg4Q1qiOGFSuNSCTB9nVJaGgGl3ZScxAclX/L8hJuDHJGDAzbfuKCQ=="
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ }
+ },
+ "to-px": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/to-px/-/to-px-1.1.0.tgz",
+ "integrity": "sha512-bfg3GLYrGoEzrGoE05TAL/Uw+H/qrf2ptr9V3W7U0lkjjyYnIfgxmVLUfhQ1hZpIQwin81uxhDjvUkDYsC0xWw==",
+ "requires": {
+ "parse-unit": "^1.0.1"
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ }
+ },
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "to-uint8": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/to-uint8/-/to-uint8-1.4.1.tgz",
+ "integrity": "sha512-o+ochsMlTZyucbww8It401FC2Rx+OP2RpDeYbA6h+y9HgedDl1UjdsJ9CmzKEG7AFP9es5PmJ4eDWeeeXihESg==",
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "clamp": "^1.0.1",
+ "is-base64": "^0.1.0",
+ "is-float-array": "^1.0.0",
+ "to-array-buffer": "^3.0.0"
+ }
+ },
+ "toggle-selection": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
+ "integrity": "sha1-bkWxJj8gF/oKzH2J14sVuL932jI="
+ },
+ "topojson-client": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/topojson-client/-/topojson-client-2.1.0.tgz",
+ "integrity": "sha1-/59784mRGF4LQoTCsGroNPDqxsg=",
+ "requires": {
+ "commander": "2"
+ }
+ },
+ "toposort": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
+ "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "tr46": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "triangulate-hypercube": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/triangulate-hypercube/-/triangulate-hypercube-1.0.1.tgz",
+ "integrity": "sha1-2Acdsuv8/VHzCNC88qXEils20Tc=",
+ "requires": {
+ "gamma": "^0.1.0",
+ "permutation-parity": "^1.0.0",
+ "permutation-rank": "^1.0.0"
+ }
+ },
+ "triangulate-polyline": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/triangulate-polyline/-/triangulate-polyline-1.0.3.tgz",
+ "integrity": "sha1-v4uod6hQVBA/65+lphtOjXAXgU0=",
+ "requires": {
+ "cdt2d": "^1.0.0"
+ }
+ },
+ "trim-right": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+ "dev": true
+ },
+ "tryer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz",
+ "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+ "dev": true
+ },
+ "tsutils": {
+ "version": "3.17.1",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz",
+ "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ }
+ },
+ "tty-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "turntable-camera-controller": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/turntable-camera-controller/-/turntable-camera-controller-3.0.1.tgz",
+ "integrity": "sha1-jb0/4AVQGRxlFky4iJcQSVeK/Zk=",
+ "requires": {
+ "filtered-vector": "^1.2.1",
+ "gl-mat4": "^1.0.2",
+ "gl-vec3": "^1.0.2"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
+ },
+ "two-product": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/two-product/-/two-product-1.0.2.tgz",
+ "integrity": "sha1-Z9ldSyV6kh4stL16+VEfkIhSLqo="
+ },
+ "two-sum": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/two-sum/-/two-sum-1.0.0.tgz",
+ "integrity": "sha1-MdPzIjnk9zHsqd+RVeKyl/AIq2Q="
+ },
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-fest": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
+ "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.16",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
+ "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.18"
+ }
+ },
+ "type-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/type-name/-/type-name-2.0.2.tgz",
+ "integrity": "sha1-7+fUEj2KxSr/9/QMfk3sUmYAj7Q="
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+ },
+ "typedarray-pool": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/typedarray-pool/-/typedarray-pool-1.2.0.tgz",
+ "integrity": "sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ==",
+ "requires": {
+ "bit-twiddle": "^1.0.0",
+ "dup": "^1.0.0"
+ }
+ },
+ "ua-parser-js": {
+ "version": "0.7.21",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
+ "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ=="
+ },
+ "uglify-js": {
+ "version": "2.8.29",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz",
+ "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=",
+ "requires": {
+ "source-map": "~0.5.1",
+ "uglify-to-browserify": "~1.0.0",
+ "yargs": "~3.10.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
+ "uglify-to-browserify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
+ "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
+ "optional": true
+ },
+ "unicode-canonical-property-names-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ=="
+ },
+ "unicode-match-property-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
+ "requires": {
+ "unicode-canonical-property-names-ecmascript": "^1.0.4",
+ "unicode-property-aliases-ecmascript": "^1.0.4"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.0.2.tgz",
+ "integrity": "sha512-Rx7yODZC1L/T8XKo/2kNzVAQaRE88AaMvI1EF/Xnj3GW2wzN6fop9DDWuFAKUVFH7vozkz26DzP0qyWLKLIVPQ==",
+ "dev": true
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-2WSLa6OdYd2ng8oqiGIWnJqyFArvhn+5vgx5GTxMbUYjCYKUcuKS62YLFF0R/BDGlB1yzXjQOLtPAfHsgirEpg=="
+ },
+ "union-find": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/union-find/-/union-find-1.0.2.tgz",
+ "integrity": "sha1-KSusQV5q06iVNdI3AQ20pTYoTlg="
+ },
+ "union-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
+ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^0.4.3"
+ },
+ "dependencies": {
+ "set-value": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
+ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.1",
+ "to-object-path": "^0.3.0"
+ }
+ }
+ }
+ },
+ "uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8="
+ },
+ "uniqs": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
+ "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
+ "dev": true
+ },
+ "unique-filename": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "dev": true,
+ "requires": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz",
+ "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "universal-router": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/universal-router/-/universal-router-8.3.0.tgz",
+ "integrity": "sha512-cBkihRoHvRQAjdUnDE1GGuuw/TPAIi8z2pEsSmUVAWLeZdgjHzzAb1+0VOO6NvBOvySItOTQikzaGlRxRdJBnA==",
+ "requires": {
+ "path-to-regexp": "^3.1.0"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "unquote": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
+ "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ="
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ }
+ }
+ },
+ "upath": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz",
+ "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==",
+ "dev": true
+ },
+ "update-diff": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/update-diff/-/update-diff-1.1.0.tgz",
+ "integrity": "sha1-9RAYLYHugZ+4LDprIrYrve2ngI8="
+ },
+ "upper-case": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+ "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "dev": true,
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+ "dev": true
+ }
+ }
+ },
+ "url-loader": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-1.1.2.tgz",
+ "integrity": "sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "mime": "^2.0.3",
+ "schema-utils": "^1.0.0"
+ },
+ "dependencies": {
+ "mime": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+ "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "dev": true
+ }
+ }
+ },
+ "url-parse": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.4.tgz",
+ "integrity": "sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==",
+ "dev": true,
+ "requires": {
+ "querystringify": "^2.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "use-debounce": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-3.1.0.tgz",
+ "integrity": "sha512-DEf3L/ZKkOSTARk/DHlC6KAAJKwMqpck8Zx06SM2Wr+LfU1TzhO8hZRzB/qbpSQqREYWQes24n1q9doeTMqF4g=="
+ },
+ "use-media": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/use-media/-/use-media-1.4.0.tgz",
+ "integrity": "sha512-XsgyUAf3nhzZmEfhc5MqLHwyaPjs78bgytpVJ/xDl0TF4Bptf3vEpBNBBT/EIKOmsOc8UbuECq3mrP3mt1QANA=="
+ },
+ "util": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+ "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "util.promisify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz",
+ "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "object.getownpropertydescriptors": "^2.0.3"
+ }
+ },
+ "utila": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+ "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
+ "dev": true
+ },
+ "utils-copy": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/utils-copy/-/utils-copy-1.1.1.tgz",
+ "integrity": "sha1-biuXmCqozXPhGCo+b4vsPA9AWKc=",
+ "requires": {
+ "const-pinf-float64": "^1.0.0",
+ "object-keys": "^1.0.9",
+ "type-name": "^2.0.0",
+ "utils-copy-error": "^1.0.0",
+ "utils-indexof": "^1.0.0",
+ "utils-regex-from-string": "^1.0.0",
+ "validate.io-array": "^1.0.3",
+ "validate.io-buffer": "^1.0.1",
+ "validate.io-nonnegative-integer": "^1.0.0"
+ }
+ },
+ "utils-copy-error": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-copy-error/-/utils-copy-error-1.0.1.tgz",
+ "integrity": "sha1-eR3jk8DwmJCv1Z88vqY18HmpT6U=",
+ "requires": {
+ "object-keys": "^1.0.9",
+ "utils-copy": "^1.1.0"
+ }
+ },
+ "utils-indexof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/utils-indexof/-/utils-indexof-1.0.0.tgz",
+ "integrity": "sha1-IP6r8J7xAYtSNkPoOA57yD7GG1w=",
+ "requires": {
+ "validate.io-array-like": "^1.0.1",
+ "validate.io-integer-primitive": "^1.0.0"
+ }
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "dev": true
+ },
+ "utils-regex-from-string": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/utils-regex-from-string/-/utils-regex-from-string-1.0.0.tgz",
+ "integrity": "sha1-/hopCfjeD/DVGCyA+8ZU1qaH0Yk=",
+ "requires": {
+ "regex-regex": "^1.0.0",
+ "validate.io-string-primitive": "^1.0.0"
+ }
+ },
+ "uuid": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
+ "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+ "dev": true
+ },
+ "v8-compile-cache": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz",
+ "integrity": "sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw==",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "validate.io-array": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz",
+ "integrity": "sha1-W1osr9j4uFq7L4hroVPy2Tond00="
+ },
+ "validate.io-array-like": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/validate.io-array-like/-/validate.io-array-like-1.0.2.tgz",
+ "integrity": "sha1-evn363tRcVvrIhVmjsXM5U+t21o=",
+ "requires": {
+ "const-max-uint32": "^1.0.2",
+ "validate.io-integer-primitive": "^1.0.0"
+ }
+ },
+ "validate.io-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/validate.io-buffer/-/validate.io-buffer-1.0.2.tgz",
+ "integrity": "sha1-hS1nNAIZFNXROvwyUxdh43IO1E4="
+ },
+ "validate.io-integer": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/validate.io-integer/-/validate.io-integer-1.0.5.tgz",
+ "integrity": "sha1-FoSWSAuVviJH7EQ/IjPeT4mHgGg=",
+ "requires": {
+ "validate.io-number": "^1.0.3"
+ }
+ },
+ "validate.io-integer-primitive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/validate.io-integer-primitive/-/validate.io-integer-primitive-1.0.0.tgz",
+ "integrity": "sha1-qaoBA1X+hoHA/qbBp0rSQZyt3cY=",
+ "requires": {
+ "validate.io-number-primitive": "^1.0.0"
+ }
+ },
+ "validate.io-matrix-like": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/validate.io-matrix-like/-/validate.io-matrix-like-1.0.2.tgz",
+ "integrity": "sha1-XsMqddCInaxzbepovdYUWxVe38M="
+ },
+ "validate.io-ndarray-like": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/validate.io-ndarray-like/-/validate.io-ndarray-like-1.0.0.tgz",
+ "integrity": "sha1-2KOw7RZbvx0vwNAHMnDPpVIpWRk="
+ },
+ "validate.io-nonnegative-integer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/validate.io-nonnegative-integer/-/validate.io-nonnegative-integer-1.0.0.tgz",
+ "integrity": "sha1-gGkkOgjF+Y6VQTySnf17GPP28p8=",
+ "requires": {
+ "validate.io-integer": "^1.0.5"
+ }
+ },
+ "validate.io-number": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/validate.io-number/-/validate.io-number-1.0.3.tgz",
+ "integrity": "sha1-9j/+2iSL8opnqNSODjtGGhZluvg="
+ },
+ "validate.io-number-primitive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/validate.io-number-primitive/-/validate.io-number-primitive-1.0.0.tgz",
+ "integrity": "sha1-0uAfICmJNp3PEVVElWQgOv5YTlU="
+ },
+ "validate.io-positive-integer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/validate.io-positive-integer/-/validate.io-positive-integer-1.0.0.tgz",
+ "integrity": "sha1-ftLQO0wnVYzGagCqsPDpIYFKZYI=",
+ "requires": {
+ "validate.io-integer": "^1.0.5"
+ }
+ },
+ "validate.io-string-primitive": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/validate.io-string-primitive/-/validate.io-string-primitive-1.0.1.tgz",
+ "integrity": "sha1-uBNbn7E3K94C/dU60dDM1t55j+4="
+ },
+ "value-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
+ "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "dev": true
+ },
+ "vectorize-text": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/vectorize-text/-/vectorize-text-3.2.1.tgz",
+ "integrity": "sha512-rGojF+D9BB96iPZPUitfq5kaiS6eCJmfEel0NXOK/MzZSuXGiwhoop80PtaDas9/Hg/oaox1tI9g3h93qpuspg==",
+ "requires": {
+ "cdt2d": "^1.0.0",
+ "clean-pslg": "^1.1.0",
+ "ndarray": "^1.0.11",
+ "planar-graph-to-polyline": "^1.0.0",
+ "simplify-planar-graph": "^2.0.1",
+ "surface-nets": "^1.0.0",
+ "triangulate-polyline": "^1.0.0"
+ }
+ },
+ "vendors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
+ "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "vm-browserify": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+ "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+ "dev": true,
+ "requires": {
+ "indexof": "0.0.1"
+ }
+ },
+ "vt-pbf": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.1.tgz",
+ "integrity": "sha512-pHjWdrIoxurpmTcbfBWXaPwSmtPAHS105253P1qyEfSTV2HJddqjM+kIHquaT/L6lVJIk9ltTGc0IxR/G47hYA==",
+ "requires": {
+ "@mapbox/point-geometry": "0.1.0",
+ "@mapbox/vector-tile": "^1.3.1",
+ "pbf": "^3.0.5"
+ }
+ },
+ "w3c-hr-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
+ "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
+ "dev": true,
+ "requires": {
+ "browser-process-hrtime": "^0.1.2"
+ }
+ },
+ "walker": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz",
+ "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=",
+ "dev": true,
+ "requires": {
+ "makeerror": "1.0.x"
+ }
+ },
+ "warning": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
+ "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "watch": {
+ "version": "0.18.0",
+ "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz",
+ "integrity": "sha1-KAlUdsbffJDJYxOJkMClQj60uYY=",
+ "dev": true,
+ "requires": {
+ "exec-sh": "^0.2.0",
+ "minimist": "^1.2.0"
+ }
+ },
+ "watchpack": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz",
+ "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==",
+ "dev": true,
+ "requires": {
+ "chokidar": "^2.0.2",
+ "graceful-fs": "^4.1.2",
+ "neo-async": "^2.5.0"
+ }
+ },
+ "wbuf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "dev": true,
+ "requires": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "weak-map": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz",
+ "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes="
+ },
+ "weakmap-shim": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/weakmap-shim/-/weakmap-shim-1.1.1.tgz",
+ "integrity": "sha1-1lr9eEEJshZuAP9XHDMVDsKkC0k="
+ },
+ "webgl-context": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/webgl-context/-/webgl-context-2.2.0.tgz",
+ "integrity": "sha1-jzfXJXz23xzQpJ5qextyG5TMhqA=",
+ "requires": {
+ "get-canvas-context": "^1.0.1"
+ }
+ },
+ "webidl-conversions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+ "dev": true
+ },
+ "webpack": {
+ "version": "4.29.6",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.29.6.tgz",
+ "integrity": "sha512-MwBwpiE1BQpMDkbnUUaW6K8RFZjljJHArC6tWQJoFm0oQtfoSebtg4Y7/QHnJ/SddtjYLHaKGX64CFjG5rehJw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.8.5",
+ "@webassemblyjs/helper-module-context": "1.8.5",
+ "@webassemblyjs/wasm-edit": "1.8.5",
+ "@webassemblyjs/wasm-parser": "1.8.5",
+ "acorn": "^6.0.5",
+ "acorn-dynamic-import": "^4.0.0",
+ "ajv": "^6.1.0",
+ "ajv-keywords": "^3.1.0",
+ "chrome-trace-event": "^1.0.0",
+ "enhanced-resolve": "^4.1.0",
+ "eslint-scope": "^4.0.0",
+ "json-parse-better-errors": "^1.0.2",
+ "loader-runner": "^2.3.0",
+ "loader-utils": "^1.1.0",
+ "memory-fs": "~0.4.1",
+ "micromatch": "^3.1.8",
+ "mkdirp": "~0.5.0",
+ "neo-async": "^2.5.0",
+ "node-libs-browser": "^2.0.0",
+ "schema-utils": "^1.0.0",
+ "tapable": "^1.1.0",
+ "terser-webpack-plugin": "^1.1.0",
+ "watchpack": "^1.5.0",
+ "webpack-sources": "^1.3.0"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
+ "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
+ "dev": true
+ },
+ "enhanced-resolve": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
+ "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.4.0",
+ "tapable": "^1.0.0"
+ }
+ },
+ "eslint-scope": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "tapable": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
+ "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-build-notifier": {
+ "version": "0.1.30",
+ "resolved": "https://registry.npmjs.org/webpack-build-notifier/-/webpack-build-notifier-0.1.30.tgz",
+ "integrity": "sha512-HeZ4Wr8XP7W0kSmPQkZCXARQVIjVFNPyJBdUqkqcE0ySYNjr6vOH3ufHESLPuy5KmMRUjHJdqJ6y3McDfCjJxQ==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0",
+ "node-notifier": "5.2.1",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "node-notifier": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.2.1.tgz",
+ "integrity": "sha512-MIBs+AAd6dJ2SklbbE8RUDRlIVhU8MaNLh1A9SUZDUHPiZkWLFde6UNwG41yQHZEToHgJMXqyVZ9UcS/ReOVTg==",
+ "dev": true,
+ "requires": {
+ "growly": "^1.3.0",
+ "semver": "^5.4.1",
+ "shellwords": "^0.1.1",
+ "which": "^1.3.0"
+ }
+ }
+ }
+ },
+ "webpack-bundle-analyzer": {
+ "version": "2.13.1",
+ "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.13.1.tgz",
+ "integrity": "sha512-rwxyfecTAxoarCC9VlHlIpfQCmmJ/qWD5bpbjkof+7HrNhTNZIwZITxN6CdlYL2axGmwNUQ+tFgcSOiNXMf/sQ==",
+ "dev": true,
+ "requires": {
+ "acorn": "^5.3.0",
+ "bfj-node4": "^5.2.0",
+ "chalk": "^2.3.0",
+ "commander": "^2.13.0",
+ "ejs": "^2.5.7",
+ "express": "^4.16.2",
+ "filesize": "^3.5.11",
+ "gzip-size": "^4.1.0",
+ "lodash": "^4.17.4",
+ "mkdirp": "^0.5.1",
+ "opener": "^1.4.3",
+ "ws": "^4.0.0"
+ },
+ "dependencies": {
+ "ws": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz",
+ "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "webpack-cli": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.2.3.tgz",
+ "integrity": "sha512-Ik3SjV6uJtWIAN5jp5ZuBMWEAaP5E4V78XJ2nI+paFPh8v4HPSwo/myN0r29Xc/6ZKnd2IdrAlpSgNOu2CDQ6Q==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "cross-spawn": "^6.0.5",
+ "enhanced-resolve": "^4.1.0",
+ "findup-sync": "^2.0.0",
+ "global-modules": "^1.0.0",
+ "import-local": "^2.0.0",
+ "interpret": "^1.1.0",
+ "loader-utils": "^1.1.0",
+ "supports-color": "^5.5.0",
+ "v8-compile-cache": "^2.0.2",
+ "yargs": "^12.0.4"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0",
+ "wrap-ansi": "^2.0.0"
+ }
+ },
+ "enhanced-resolve": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
+ "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.4.0",
+ "tapable": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "tapable": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
+ "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "12.0.5",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
+ "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^11.1.1"
+ }
+ }
+ }
+ },
+ "webpack-dev-middleware": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.6.0.tgz",
+ "integrity": "sha512-oeXA3m+5gbYbDBGo4SvKpAHJJEGMoekUbHgo1RK7CP1sz7/WOSeu/dWJtSTk+rzDCLkPwQhGocgIq6lQqOyOwg==",
+ "dev": true,
+ "requires": {
+ "memory-fs": "^0.4.1",
+ "mime": "^2.3.1",
+ "range-parser": "^1.0.3",
+ "webpack-log": "^2.0.0"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "mime": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.0.tgz",
+ "integrity": "sha512-ikBcWwyqXQSHKtciCcctu9YfPbFYZ4+gbHEmE0Q8jzcTYQg5dHCr3g2wwAZjPoJfQVXZq6KXAjpXOTf5/cjT7w==",
+ "dev": true
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "webpack-dev-server": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.2.1.tgz",
+ "integrity": "sha512-sjuE4mnmx6JOh9kvSbPYw3u/6uxCLHNWfhWaIPwcXWsvWOPN+nc5baq4i9jui3oOBRXGonK9+OI0jVkaz6/rCw==",
+ "dev": true,
+ "requires": {
+ "ansi-html": "0.0.7",
+ "bonjour": "^3.5.0",
+ "chokidar": "^2.0.0",
+ "compression": "^1.5.2",
+ "connect-history-api-fallback": "^1.3.0",
+ "debug": "^4.1.1",
+ "del": "^3.0.0",
+ "express": "^4.16.2",
+ "html-entities": "^1.2.0",
+ "http-proxy-middleware": "^0.19.1",
+ "import-local": "^2.0.0",
+ "internal-ip": "^4.2.0",
+ "ip": "^1.1.5",
+ "killable": "^1.0.0",
+ "loglevel": "^1.4.1",
+ "opn": "^5.1.0",
+ "portfinder": "^1.0.9",
+ "schema-utils": "^1.0.0",
+ "selfsigned": "^1.9.1",
+ "semver": "^5.6.0",
+ "serve-index": "^1.7.2",
+ "sockjs": "0.3.19",
+ "sockjs-client": "1.3.0",
+ "spdy": "^4.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^6.1.0",
+ "url": "^0.11.0",
+ "webpack-dev-middleware": "^3.5.1",
+ "webpack-log": "^2.0.0",
+ "yargs": "12.0.2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+ "dev": true
+ },
+ "cliui": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
+ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^2.1.1",
+ "strip-ansi": "^4.0.0",
+ "wrap-ansi": "^2.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz",
+ "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==",
+ "dev": true,
+ "requires": {
+ "xregexp": "4.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "yargs": {
+ "version": "12.0.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.2.tgz",
+ "integrity": "sha512-e7SkEx6N6SIZ5c5H22RTZae61qtn3PYUE8JYbBFlK9sYmh3DMQ6E5ygtaG/2BW0JZi4WGgTR2IV5ChqlqrDGVQ==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^2.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.0.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1 || ^4.0.0",
+ "yargs-parser": "^10.1.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
+ "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^4.1.0"
+ }
+ }
+ }
+ },
+ "webpack-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
+ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^3.0.0",
+ "uuid": "^3.3.2"
+ }
+ },
+ "webpack-manifest-plugin": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-2.0.4.tgz",
+ "integrity": "sha512-nejhOHexXDBKQOj/5v5IZSfCeTO3x1Dt1RZEcGfBSul891X/eLIcIVH31gwxPDdsi2Z8LKKFGpM4w9+oTBOSCg==",
+ "dev": true,
+ "requires": {
+ "fs-extra": "^7.0.0",
+ "lodash": ">=3.5 <5",
+ "tapable": "^1.0.0"
+ },
+ "dependencies": {
+ "tapable": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
+ "integrity": "sha512-9I2ydhj8Z9veORCw5PRm4u9uebCn0mcCa6scWoNcbZ6dAtoo2618u9UUzxgmsCOreJpqDDuv61LvwofW7hLcBA==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-sources": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
+ "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz",
+ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=",
+ "dev": true,
+ "requires": {
+ "http-parser-js": ">=0.4.0",
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz",
+ "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==",
+ "dev": true
+ },
+ "wgs84": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/wgs84/-/wgs84-0.0.0.tgz",
+ "integrity": "sha1-NP3FVZF7blfPKigu0ENxDASc3HY="
+ },
+ "whatwg-encoding": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+ "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "0.4.24"
+ }
+ },
+ "whatwg-fetch": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz",
+ "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q=="
+ },
+ "whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
+ "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
+ "dev": true,
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "whet.extend": {
+ "version": "0.9.9",
+ "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz",
+ "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
+ "dev": true
+ },
+ "window-size": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
+ "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0="
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
+ },
+ "worker-farm": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
+ "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
+ "dev": true,
+ "requires": {
+ "errno": "~0.1.7"
+ }
+ },
+ "world-calendars": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/world-calendars/-/world-calendars-1.0.3.tgz",
+ "integrity": "sha1-slxQMrokEo/8QdCfr0pewbnBQzU=",
+ "requires": {
+ "object-assign": "^4.1.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+ "dev": true,
+ "requires": {
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1"
+ },
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "write": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz",
+ "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ },
+ "write-file-atomic": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz",
+ "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "imurmurhash": "^0.1.4",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ws": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
+ "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ },
+ "xml-name-validator": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+ "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+ "dev": true
+ },
+ "xregexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz",
+ "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+ "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
+ "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
+ "requires": {
+ "camelcase": "^1.0.2",
+ "cliui": "^2.1.0",
+ "decamelize": "^1.0.0",
+ "window-size": "0.1.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
+ "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
+ "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
+ "dev": true
+ }
+ }
+ },
+ "zero-crossings": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/zero-crossings/-/zero-crossings-1.0.1.tgz",
+ "integrity": "sha1-xWK9MRNkPzRDokXRJAa4i2m5qf8=",
+ "requires": {
+ "cwise-compiler": "^1.0.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 07a23ae092..8b21b08ad7 100644
--- a/package.json
+++ b/package.json
@@ -1,41 +1,39 @@
{
"name": "redash-client",
- "version": "25.04.0-dev",
+ "version": "9.0.0-beta",
"description": "The frontend part of Redash.",
"main": "index.js",
"scripts": {
"start": "npm-run-all --parallel watch:viz webpack-dev-server",
+ "bundle": "bin/bundle-extensions",
"clean": "rm -rf ./client/dist/",
- "build:viz": "(cd viz-lib && yarn build:babel)",
- "build": "yarn clean && yarn build:viz && NODE_OPTIONS=--openssl-legacy-provider NODE_ENV=production webpack",
- "watch:app": "NODE_OPTIONS=--openssl-legacy-provider webpack watch --progress",
- "watch:viz": "(cd viz-lib && yarn watch:babel)",
+ "build:viz": "(cd viz-lib && npm run build:babel)",
+ "build": "npm run clean && npm run build:viz && NODE_ENV=production webpack",
+ "build:old-node-version": "npm run clean && NODE_ENV=production node --max-old-space-size=4096 node_modules/.bin/webpack",
+ "watch:app": "webpack --watch --progress --colors -d",
+ "watch:viz": "(cd viz-lib && npm run watch:babel)",
"watch": "npm-run-all --parallel watch:*",
"webpack-dev-server": "webpack-dev-server",
- "analyze": "yarn clean && BUNDLE_ANALYZER=on NODE_OPTIONS=--openssl-legacy-provider webpack",
- "analyze:build": "yarn clean && NODE_ENV=production BUNDLE_ANALYZER=on NODE_OPTIONS=--openssl-legacy-provider webpack",
- "lint": "yarn lint:base --ext .js --ext .jsx --ext .ts --ext .tsx ./client",
- "lint:fix": "yarn lint:base --fix --ext .js --ext .jsx --ext .ts --ext .tsx ./client",
+ "analyze": "npm run clean && BUNDLE_ANALYZER=on webpack",
+ "analyze:build": "npm run clean && NODE_ENV=production BUNDLE_ANALYZER=on webpack",
+ "lint": "npm run lint:base -- --ext .js --ext .jsx ./client",
+ "lint:fix": "npm run lint:base -- --fix --ext .js --ext .jsx ./client",
"lint:base": "eslint --config ./client/.eslintrc.js --ignore-path ./client/.eslintignore",
- "lint:ci": "yarn lint --max-warnings 0 --format junit --output-file /tmp/test-results/eslint/results.xml",
- "prettier": "prettier --write 'client/app/**/*.{js,jsx,ts,tsx}' 'client/cypress/**/*.{js,jsx,ts,tsx}'",
- "type-check": "tsc --noEmit --project client/tsconfig.json",
- "type-check:watch": "yarn type-check --watch",
- "jest": "TZ=Africa/Khartoum jest",
- "test": "run-s type-check jest",
+ "lint:ci": "npm run lint -- --max-warnings 0 --format junit --output-file /tmp/test-results/eslint/results.xml",
+ "prettier": "prettier --write 'client/app/**/*.{js,jsx}' 'client/cypress/**/*.js'",
+ "test": "TZ=Africa/Khartoum jest",
"test:watch": "jest --watch",
+ "cypress:install": "npm install --no-save cypress@~4.5.0 @percy/agent@0.26.2 @percy/cypress@^2.2.0 atob@2.1.2",
"cypress": "node client/cypress/cypress.js",
- "preinstall": "cd viz-lib && yarn link --link-folder ../.yarn",
- "postinstall": "(cd viz-lib && yarn --frozen-lockfile && yarn build:babel) && yarn link --link-folder ./.yarn @redash/viz"
+ "postinstall": "(cd viz-lib && npm ci && npm run build:babel)"
},
"repository": {
"type": "git",
"url": "git+https://github.com/getredash/redash.git"
},
"engines": {
- "node": ">16.0 <21.0",
- "npm": "please-use-yarn",
- "yarn": "^1.22.10"
+ "node": "^12.0.0",
+ "npm": "^6.0.0"
},
"author": "Redash Contributors",
"license": "BSD-2-Clause",
@@ -44,117 +42,96 @@
},
"homepage": "https://redash.io/",
"dependencies": {
- "@ant-design/icons": "^4.2.1",
"@redash/viz": "file:viz-lib",
- "ace-builds": "^1.4.12",
- "antd": "^4.4.3",
- "axios": "0.27.2",
- "axios-auth-refresh": "3.3.6",
- "bootstrap": "^3.4.1",
+ "@types/prop-types": "^15.5.2",
+ "@types/react": "^16.3.13",
+ "@types/react-dom": "^16.0.5",
+ "ace-builds": "1.4.12",
+ "antd": "^3.26.17",
+ "axios": "^0.19.0",
+ "bootstrap": "^3.3.7",
"classnames": "^2.2.6",
"d3": "^3.5.17",
- "debug": "^3.2.7",
- "dompurify": "^2.0.17",
- "elliptic": "^6.6.0",
+ "debug": "^3.1.0",
+ "dompurify": "^2.0.7",
"font-awesome": "^4.7.0",
"history": "^4.10.1",
"hoist-non-react-statics": "^3.3.0",
+ "lodash": "^4.17.10",
"markdown": "0.5.0",
"material-design-iconic-font": "^2.2.0",
+ "moment": "^2.19.3",
"mousetrap": "^1.6.1",
"mustache": "^2.3.0",
"numeral": "^2.0.6",
- "path-to-regexp": "^3.3.0",
+ "path-to-regexp": "^3.1.0",
"prop-types": "^15.6.1",
"query-string": "^6.9.0",
- "react": "16.14.0",
- "react-ace": "^9.1.1",
- "react-dom": "^16.14.0",
+ "react": "^16.8.3",
+ "react-ace": "9.1.1",
+ "react-dom": "^16.8.3",
"react-grid-layout": "^0.18.2",
"react-resizable": "^1.10.1",
"react-virtualized": "^9.21.2",
- "sql-formatter": "git+https://github.com/getredash/sql-formatter.git",
"universal-router": "^8.3.0",
"use-debounce": "^3.1.0",
"use-media": "^1.4.0"
},
"devDependencies": {
- "@babel/cli": "^7.22.9",
- "@babel/core": "^7.22.9",
- "@babel/plugin-proposal-class-properties": "^7.18.6",
+ "@babel/core": "^7.2.2",
+ "@babel/plugin-proposal-class-properties": "^7.3.0",
"@babel/plugin-transform-object-assign": "^7.2.0",
- "@babel/preset-env": "^7.22.9",
+ "@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
- "@babel/preset-typescript": "^7.22.5",
- "@cypress/code-coverage": "^3.11.0",
- "@percy/agent": "^0.28.7",
- "@percy/cypress": "^3.1.2",
- "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
- "@testing-library/cypress": "^8.0.7",
- "@types/classnames": "^2.2.10",
- "@types/hoist-non-react-statics": "^3.3.1",
- "@types/lodash": "^4.14.157",
- "@types/prop-types": "^15.7.3",
- "@types/react": "^16.14.2",
- "@types/react-dom": "^16.9.10",
- "@types/sql-formatter": "^2.3.0",
"@typescript-eslint/eslint-plugin": "^2.10.0",
"@typescript-eslint/parser": "^2.10.0",
- "atob": "^2.1.2",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.1.0",
- "babel-loader": "^8.3.0",
- "babel-plugin-istanbul": "^6.1.1",
+ "babel-loader": "^8.0.5",
"babel-plugin-transform-builtin-extend": "^1.1.2",
- "copy-webpack-plugin": "^6.4.1",
- "css-loader": "^5.2.7",
- "cypress": "^11.2.0",
- "dayjs": "^1.11.9",
+ "copy-webpack-plugin": "^4.5.3",
+ "css-loader": "^0.28.7",
"enzyme": "^3.8.0",
"enzyme-adapter-react-16": "^1.7.1",
"enzyme-to-json": "^3.3.5",
"eslint": "^6.7.2",
"eslint-config-prettier": "^6.7.0",
"eslint-config-react-app": "^5.1.0",
- "eslint-loader": "^4.0.2",
+ "eslint-loader": "^3.0.3",
"eslint-plugin-chai-friendly": "^0.5.0",
"eslint-plugin-compat": "^3.3.0",
"eslint-plugin-cypress": "^2.0.1",
"eslint-plugin-flowtype": "^3.13.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-jest": "^22.2.2",
- "eslint-plugin-jsx-a11y": "^6.4.1",
+ "eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-no-only-tests": "^2.4.0",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-react-hooks": "^1.7.0",
- "file-loader": "^6.2.0",
- "html-webpack-plugin": "^4.5.2",
+ "file-loader": "^2.0.0",
+ "html-webpack-plugin": "^3.2.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.1.0",
- "less": "^3.13.1",
- "less-loader": "^5.0.0",
- "less-plugin-autoprefix": "^2.0.0",
- "lodash": "^4.17.21",
- "mini-css-extract-plugin": "^1.6.2",
+ "less": "^3.9.0",
+ "less-loader": "^4.1.0",
+ "less-plugin-autoprefix": "^1.5.1",
+ "mini-css-extract-plugin": "^0.4.4",
"mockdate": "^2.0.2",
"npm-run-all": "^4.1.5",
"prettier": "^1.19.1",
"raw-loader": "^0.5.1",
- "react-refresh": "^0.14.0",
- "react-test-renderer": "^16.14.0",
- "request-cookies": "^1.1.0",
- "style-loader": "^2.0.0",
- "typescript": "^4.1.2",
- "url-loader": "^4.1.1",
- "webpack": "^4.46.0",
- "webpack-build-notifier": "^2.3.0",
- "webpack-bundle-analyzer": "^4.9.0",
- "webpack-cli": "^4.10.0",
- "webpack-dev-server": "^4.15.1",
+ "react-test-renderer": "^16.5.2",
+ "request": "^2.88.0",
+ "url-loader": "^1.1.2",
+ "webpack": "^4.20.2",
+ "webpack-build-notifier": "^0.1.30",
+ "webpack-bundle-analyzer": "^2.11.1",
+ "webpack-cli": "^3.1.2",
+ "webpack-dev-server": "^3.1.9",
"webpack-manifest-plugin": "^2.0.4"
},
"optionalDependencies": {
- "fsevents": "^2.3.2"
+ "fsevents": "^1.2.9"
},
"jest": {
"rootDir": "./client",
@@ -173,16 +150,6 @@
"/app/__tests__/"
]
},
- "nyc": {
- "include": [
- "client/app/**",
- "viz-lib/**"
- ]
- },
- "browser": {
- "fs": false,
- "path": false
- },
"//": "browserslist set to 'Async functions' compatibility",
"browserslist": [
"Edge >= 15",
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index b0033c9ae7..0000000000
--- a/poetry.lock
+++ /dev/null
@@ -1,5496 +0,0 @@
-# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand.
-
-[[package]]
-name = "adal"
-version = "1.2.7"
-description = "Note: This library is already replaced by MSAL Python, available here: https://pypi.org/project/msal/ .ADAL Python remains available here as a legacy. The ADAL for Python library makes it easy for python application to authenticate to Azure Active Directory (AAD) in order to access AAD protected web resources."
-optional = false
-python-versions = "*"
-files = [
- {file = "adal-1.2.7-py2.py3-none-any.whl", hash = "sha256:2a7451ed7441ddbc57703042204a3e30ef747478eea022c70f789fc7f084bc3d"},
- {file = "adal-1.2.7.tar.gz", hash = "sha256:d74f45b81317454d96e982fd1c50e6fb5c99ac2223728aea8764433a39f566f1"},
-]
-
-[package.dependencies]
-cryptography = ">=1.1.0"
-PyJWT = ">=1.0.0,<3"
-python-dateutil = ">=2.1.0,<3"
-requests = ">=2.0.0,<3"
-
-[[package]]
-name = "advocate"
-version = "1.0.0"
-description = "A wrapper around the requests library for safely making HTTP requests on behalf of a third party"
-optional = false
-python-versions = "*"
-files = [
- {file = "advocate-1.0.0-py2.py3-none-any.whl", hash = "sha256:e8b340e49fadc0e416fbc9e81ef52d74858ccad16357dabde6cf9d99a7407d70"},
- {file = "advocate-1.0.0.tar.gz", hash = "sha256:1bf1170e41334279996580329c594e017540ab0eaf7a152323e743f0a85a353d"},
-]
-
-[package.dependencies]
-ndg-httpsclient = "*"
-netifaces = ">=0.10.5"
-pyasn1 = "*"
-pyopenssl = "*"
-requests = ">=2.18.0,<3.0"
-six = "*"
-urllib3 = ">=1.22,<2.0"
-
-[[package]]
-name = "alembic"
-version = "1.13.1"
-description = "A database migration tool for SQLAlchemy."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"},
- {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"},
-]
-
-[package.dependencies]
-importlib-metadata = {version = "*", markers = "python_version < \"3.9\""}
-importlib-resources = {version = "*", markers = "python_version < \"3.9\""}
-Mako = "*"
-SQLAlchemy = ">=1.3.0"
-typing-extensions = ">=4"
-
-[package.extras]
-tz = ["backports.zoneinfo"]
-
-[[package]]
-name = "aniso8601"
-version = "8.0.0"
-description = "A library for parsing ISO 8601 strings."
-optional = false
-python-versions = "*"
-files = [
- {file = "aniso8601-8.0.0-py2.py3-none-any.whl", hash = "sha256:c033f63d028b9a58e3ab0c2c7d0532ab4bfa7452bfc788fbfe3ddabd327b181a"},
- {file = "aniso8601-8.0.0.tar.gz", hash = "sha256:529dcb1f5f26ee0df6c0a1ee84b7b27197c3c50fc3a6321d66c544689237d072"},
-]
-
-[[package]]
-name = "anyio"
-version = "4.2.0"
-description = "High level compatibility layer for multiple asynchronous event loop implementations"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"},
- {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"},
-]
-
-[package.dependencies]
-exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
-idna = ">=2.8"
-sniffio = ">=1.1"
-typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""}
-
-[package.extras]
-doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"]
-test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"]
-trio = ["trio (>=0.23)"]
-
-[[package]]
-name = "appdirs"
-version = "1.4.4"
-description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
-optional = false
-python-versions = "*"
-files = [
- {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
- {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
-]
-
-[[package]]
-name = "asn1crypto"
-version = "1.5.1"
-description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP"
-optional = false
-python-versions = "*"
-files = [
- {file = "asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67"},
- {file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
-]
-
-[[package]]
-name = "async-timeout"
-version = "4.0.3"
-description = "Timeout context manager for asyncio programs"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"},
- {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"},
-]
-
-[[package]]
-name = "atsd-client"
-version = "3.0.5"
-description = "Axibase Time Series Database API Client for Python"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "atsd_client-3.0.5-py3-none-any.whl", hash = "sha256:36cb4adffb58afd994dace6eb703711e6c40c2977c974783492d99e10104bfd5"},
-]
-
-[package.dependencies]
-python-dateutil = "*"
-requests = ">=2.12.1"
-tzlocal = "*"
-
-[package.extras]
-analysis = ["pandas"]
-
-[[package]]
-name = "attrs"
-version = "23.2.0"
-description = "Classes Without Boilerplate"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"},
- {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"},
-]
-
-[package.extras]
-cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
-dev = ["attrs[tests]", "pre-commit"]
-docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
-tests = ["attrs[tests-no-zope]", "zope-interface"]
-tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"]
-tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"]
-
-[[package]]
-name = "authlib"
-version = "0.15.5"
-description = "The ultimate Python library in building OAuth and OpenID Connect servers."
-optional = false
-python-versions = "*"
-files = [
- {file = "Authlib-0.15.5-py2.py3-none-any.whl", hash = "sha256:ecf4a7a9f2508c0bb07e93a752dd3c495cfaffc20e864ef0ffc95e3f40d2abaf"},
- {file = "Authlib-0.15.5.tar.gz", hash = "sha256:b83cf6360c8e92b0e9df0d1f32d675790bcc4e3c03977499b1eed24dcdef4252"},
-]
-
-[package.dependencies]
-cryptography = "*"
-
-[package.extras]
-client = ["requests"]
-
-[[package]]
-name = "azure-kusto-data"
-version = "0.0.35"
-description = "Kusto Data Client"
-optional = false
-python-versions = "*"
-files = [
- {file = "azure-kusto-data-0.0.35.tar.gz", hash = "sha256:0a64ac028cd10c5ba0212c2238b4648d3f613360d305139217addea9a5136869"},
- {file = "azure_kusto_data-0.0.35-py2.py3-none-any.whl", hash = "sha256:3862261c4812eeb05fa110e2df951de0df838f3f1fe9d46a48879b8f5532b365"},
-]
-
-[package.dependencies]
-adal = ">=1.0.0"
-python-dateutil = ">=2.8.0"
-requests = ">=2.13.0"
-six = ">=1.10.0"
-
-[package.extras]
-pandas = ["pandas (==0.24.1)"]
-
-[[package]]
-name = "backoff"
-version = "2.2.1"
-description = "Function decoration for backoff and retry"
-optional = false
-python-versions = ">=3.7,<4.0"
-files = [
- {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"},
- {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"},
-]
-
-[[package]]
-name = "backports-zoneinfo"
-version = "0.2.1"
-description = "Backport of the standard library zoneinfo module"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"},
- {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"},
- {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"},
- {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"},
- {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"},
- {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"},
- {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"},
- {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"},
- {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"},
- {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"},
- {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"},
- {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"},
- {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"},
- {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"},
- {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"},
- {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"},
-]
-
-[package.extras]
-tzdata = ["tzdata"]
-
-[[package]]
-name = "bcrypt"
-version = "4.1.2"
-description = "Modern password hashing for your software and your servers"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "bcrypt-4.1.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ac621c093edb28200728a9cca214d7e838529e557027ef0581685909acd28b5e"},
- {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea505c97a5c465ab8c3ba75c0805a102ce526695cd6818c6de3b1a38f6f60da1"},
- {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57fa9442758da926ed33a91644649d3e340a71e2d0a5a8de064fb621fd5a3326"},
- {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c"},
- {file = "bcrypt-4.1.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6cad43d8c63f34b26aef462b6f5e44fdcf9860b723d2453b5d391258c4c8e966"},
- {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:44290ccc827d3a24604f2c8bcd00d0da349e336e6503656cb8192133e27335e2"},
- {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:732b3920a08eacf12f93e6b04ea276c489f1c8fb49344f564cca2adb663b3e4c"},
- {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5"},
- {file = "bcrypt-4.1.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b8df79979c5bae07f1db22dcc49cc5bccf08a0380ca5c6f391cbb5790355c0b0"},
- {file = "bcrypt-4.1.2-cp37-abi3-win32.whl", hash = "sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369"},
- {file = "bcrypt-4.1.2-cp37-abi3-win_amd64.whl", hash = "sha256:9800ae5bd5077b13725e2e3934aa3c9c37e49d3ea3d06318010aa40f54c63551"},
- {file = "bcrypt-4.1.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:71b8be82bc46cedd61a9f4ccb6c1a493211d031415a34adde3669ee1b0afbb63"},
- {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e3c6642077b0c8092580c819c1684161262b2e30c4f45deb000c38947bf483"},
- {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:387e7e1af9a4dd636b9505a465032f2f5cb8e61ba1120e79a0e1cd0b512f3dfc"},
- {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7"},
- {file = "bcrypt-4.1.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2a298db2a8ab20056120b45e86c00a0a5eb50ec4075b6142db35f593b97cb3fb"},
- {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ba55e40de38a24e2d78d34c2d36d6e864f93e0d79d0b6ce915e4335aa81d01b1"},
- {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:3566a88234e8de2ccae31968127b0ecccbb4cddb629da744165db72b58d88ca4"},
- {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b90e216dc36864ae7132cb151ffe95155a37a14e0de3a8f64b49655dd959ff9c"},
- {file = "bcrypt-4.1.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:69057b9fc5093ea1ab00dd24ede891f3e5e65bee040395fb1e66ee196f9c9b4a"},
- {file = "bcrypt-4.1.2-cp39-abi3-win32.whl", hash = "sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f"},
- {file = "bcrypt-4.1.2-cp39-abi3-win_amd64.whl", hash = "sha256:be3ab1071662f6065899fe08428e45c16aa36e28bc42921c4901a191fda6ee42"},
- {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d75fc8cd0ba23f97bae88a6ec04e9e5351ff3c6ad06f38fe32ba50cbd0d11946"},
- {file = "bcrypt-4.1.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:a97e07e83e3262599434816f631cc4c7ca2aa8e9c072c1b1a7fec2ae809a1d2d"},
- {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e51c42750b7585cee7892c2614be0d14107fad9581d1738d954a262556dd1aab"},
- {file = "bcrypt-4.1.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba4e4cc26610581a6329b3937e02d319f5ad4b85b074846bf4fef8a8cf51e7bb"},
- {file = "bcrypt-4.1.2.tar.gz", hash = "sha256:33313a1200a3ae90b75587ceac502b048b840fc69e7f7a0905b5f87fac7a1258"},
-]
-
-[package.extras]
-tests = ["pytest (>=3.2.1,!=3.3.0)"]
-typecheck = ["mypy"]
-
-[[package]]
-name = "bitarray"
-version = "2.9.2"
-description = "efficient arrays of booleans -- C extension"
-optional = false
-python-versions = "*"
-files = [
- {file = "bitarray-2.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:917905de565d9576eb20f53c797c15ba88b9f4f19728acabec8d01eee1d3756a"},
- {file = "bitarray-2.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b35bfcb08b7693ab4bf9059111a6e9f14e07d57ac93cd967c420db58ab9b71e1"},
- {file = "bitarray-2.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ea1923d2e7880f9e1959e035da661767b5a2e16a45dfd57d6aa831e8b65ee1bf"},
- {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0b63a565e8a311cc8348ff1262d5784df0f79d64031d546411afd5dd7ef67d"},
- {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cf0620da2b81946d28c0b16f3e3704d38e9837d85ee4f0652816e2609aaa4fed"},
- {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:79a9b8b05f2876c7195a2b698c47528e86a73c61ea203394ff8e7a4434bda5c8"},
- {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:345c76b349ff145549652436235c5532e5bfe9db690db6f0a6ad301c62b9ef21"},
- {file = "bitarray-2.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e2936f090bf3f4d1771f44f9077ebccdbc0415d2b598d51a969afcb519df505"},
- {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f9346e98fc2abcef90b942973087e2462af6d3e3710e82938078d3493f7fef52"},
- {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e6ec283d4741befb86e8c3ea2e9ac1d17416c956d392107e45263e736954b1f7"},
- {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:962892646599529917ef26266091e4cb3077c88b93c3833a909d68dcc971c4e3"},
- {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e8da5355d7d75a52df5b84750989e34e39919ec7e59fafc4c104cc1607ab2d31"},
- {file = "bitarray-2.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:603e7d640e54ad764d2b4da6b61e126259af84f253a20f512dd10689566e5478"},
- {file = "bitarray-2.9.2-cp310-cp310-win32.whl", hash = "sha256:f00079f8e69d75c2a417de7961a77612bb77ef46c09bc74607d86de4740771ef"},
- {file = "bitarray-2.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:1bb33673e7f7190a65f0a940c1ef63266abdb391f4a3e544a47542d40a81f536"},
- {file = "bitarray-2.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fe71fd4b76380c2772f96f1e53a524da7063645d647a4fcd3b651bdd80ca0f2e"},
- {file = "bitarray-2.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d527172919cdea1e13994a66d9708a80c3d33dedcf2f0548e4925e600fef3a3a"},
- {file = "bitarray-2.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:052c5073bdcaa9dd10628d99d37a2f33ec09364b86dd1f6281e2d9f8d3db3060"},
- {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e064caa55a6ed493aca1eda06f8b3f689778bc780a75e6ad7724642ba5dc62f7"},
- {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:508069a04f658210fdeee85a7a0ca84db4bcc110cbb1d21f692caa13210f24a7"},
- {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4da73ebd537d75fa7bccfc2228fcaedea0803f21dd9d0bf0d3b67fef3c4af294"},
- {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cb378eaa65cd43098f11ff5d27e48ee3b956d2c00d2d6b5bfc2a09fe183be47"},
- {file = "bitarray-2.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d14c790b91f6cbcd9b718f88ed737c78939980c69ac8c7f03dd7e60040c12951"},
- {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7eea9318293bc0ea6447e9ebfba600a62f3428bea7e9c6d42170ae4f481dbab3"},
- {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b76ffec27c7450b8a334f967366a9ebadaea66ee43f5b530c12861b1a991f503"},
- {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:76b76a07d4ee611405045c6950a1e24c4362b6b44808d4ad6eea75e0dbc59af4"},
- {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c7d16beeaaab15b075990cd26963d6b5b22e8c5becd131781514a00b8bdd04bd"},
- {file = "bitarray-2.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60df43e868a615c7e15117a1e1c2e5e11f48f6457280eba6ddf8fbefbec7da99"},
- {file = "bitarray-2.9.2-cp311-cp311-win32.whl", hash = "sha256:e788608ed7767b7b3bbde6d49058bccdf94df0de9ca75d13aa99020cc7e68095"},
- {file = "bitarray-2.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:a23397da092ef0a8cfe729571da64c2fc30ac18243caa82ac7c4f965087506ff"},
- {file = "bitarray-2.9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:90e3a281ffe3897991091b7c46fca38c2675bfd4399ffe79dfeded6c52715436"},
- {file = "bitarray-2.9.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bed637b674db5e6c8a97a4a321e3e4d73e72d50b5c6b29950008a93069cc64cd"},
- {file = "bitarray-2.9.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e49066d251dbbe4e6e3a5c3937d85b589e40e2669ad0eef41a00f82ec17d844b"},
- {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c4344e96642e2211fb3a50558feff682c31563a4c64529a931769d40832ca79"},
- {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aeb60962ec4813c539a59fbd4f383509c7222b62c3fb1faa76b54943a613e33a"},
- {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed0f7982f10581bb16553719e5e8f933e003f5b22f7d25a68bdb30fac630a6ff"},
- {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c71d1cabdeee0cdda4669168618f0e46b7dace207b29da7b63aaa1adc2b54081"},
- {file = "bitarray-2.9.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0ef2d0a6f1502d38d911d25609b44c6cc27bee0a4363dd295df78b075041b60"},
- {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6f71d92f533770fb027388b35b6e11988ab89242b883f48a6fe7202d238c61f8"},
- {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ba0734aa300757c924f3faf8148e1b8c247176a0ac8e16aefdf9c1eb19e868f7"},
- {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:d91406f413ccbf4af6ab5ae7bc78f772a95609f9ddd14123db36ef8c37116d95"},
- {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:87abb7f80c0a042f3fe8e5264da1a2756267450bb602110d5327b8eaff7682e7"},
- {file = "bitarray-2.9.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b558ce85579b51a2e38703877d1e93b7728a7af664dd45a34e833534f0b755d"},
- {file = "bitarray-2.9.2-cp312-cp312-win32.whl", hash = "sha256:dac2399ee2889fbdd3472bfc2ede74c34cceb1ccf29a339964281a16eb1d3188"},
- {file = "bitarray-2.9.2-cp312-cp312-win_amd64.whl", hash = "sha256:48a30d718d1a6dfc22a49547450107abe8f4afdf2abdcbe76eb9ed88edc49498"},
- {file = "bitarray-2.9.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2c6be1b651fad8f3adb7a5aa12c65b612cd9b89530969af941844ae680f7d981"},
- {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5b399ae6ab975257ec359f03b48fc00b1c1cd109471e41903548469b8feae5c"},
- {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b3543c8a1cb286ad105f11c25d8d0f712f41c5c55f90be39f0e5a1376c7d0b0"},
- {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03adaacb79e2fb8f483ab3a67665eec53bb3fd0cd5dbd7358741aef124688db3"},
- {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae5b0657380d2581e13e46864d147a52c1e2bbac9f59b59c576e42fa7d10cf0"},
- {file = "bitarray-2.9.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c1f4bf6ea8eb9d7f30808c2e9894237a96650adfecbf5f3643862dc5982f89e"},
- {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a8873089be2aa15494c0f81af1209f6e1237d762c5065bc4766c1b84321e1b50"},
- {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:677e67f50e2559efc677a4366707070933ad5418b8347a603a49a070890b19bc"},
- {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:a620d8ce4ea2f1c73c6b6b1399e14cb68c6915e2be3fad5808c2998ed55b4acf"},
- {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:64115ccabbdbe279c24c367b629c6b1d3da9ed36c7420129e27c338a3971bfee"},
- {file = "bitarray-2.9.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5d6fb422772e75385b76ad1c52f45a68bd4efafd8be8d0061c11877be74c4d43"},
- {file = "bitarray-2.9.2-cp36-cp36m-win32.whl", hash = "sha256:852e202875dd6dfd6139ce7ec4e98dac2b17d8d25934dc99900831e81c3adaef"},
- {file = "bitarray-2.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7dfefdcb0dc6a3ba9936063cec65a74595571b375beabe18742b3d91d087eefd"},
- {file = "bitarray-2.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b306c4cf66912511422060f7f5e1149c8bdb404f8e00e600561b0749fdd45659"},
- {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a09c4f81635408e3387348f415521d4b94198c562c23330f560596a6aaa26eaf"},
- {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5361413fd2ecfdf44dc8f065177dc6aba97fa80a91b815586cb388763acf7f8d"},
- {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e8a9475d415ef1eaae7942df6f780fa4dcd48fce32825eda591a17abba869299"},
- {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9b87baa7bfff9a5878fcc1bffe49ecde6e647a72a64b39a69cd8a2992a43a34"},
- {file = "bitarray-2.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb6b86cfdfc503e92cb71c68766a24565359136961642504a7cc9faf936d9c88"},
- {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cd56b8ae87ebc71bcacbd73615098e8a8de952ecbb5785b6b4e2b07da8a06e1f"},
- {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3fa909cfd675004aed8b4cc9df352415933656e0155a6209d878b7cb615c787e"},
- {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b069ca9bf728e0c5c5b60e00a89df9af34cc170c695c3bfa3b372d8f40288efb"},
- {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:6067f2f07a7121749858c7daa93c8774325c91590b3e81a299621e347740c2ae"},
- {file = "bitarray-2.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:321841cdad1dd0f58fe62e80e9c9c7531f8ebf8be93f047401e930dc47425b1e"},
- {file = "bitarray-2.9.2-cp37-cp37m-win32.whl", hash = "sha256:54e16e32e60973bb83c315de9975bc1bcfc9bd50bb13001c31da159bc49b0ca1"},
- {file = "bitarray-2.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:f4dcadb7b8034aa3491ee8f5a69b3d9ba9d7d1e55c3cc1fc45be313e708277f8"},
- {file = "bitarray-2.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c8919fdbd3bb596b104388b56ae4b266eb28da1f2f7dff2e1f9334a21840fe96"},
- {file = "bitarray-2.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:eb7a9d8a2e400a1026de341ad48e21670a6261a75b06df162c5c39b0d0e7c8f4"},
- {file = "bitarray-2.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6ec84668dd7b937874a2b2c293cd14ba84f37be0d196dead852e0ada9815d807"},
- {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2de9a31c34e543ae089fd2a5ced01292f725190e379921384f695e2d7184bd3"},
- {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9521f49ae121a17c0a41e5112249e6fa7f6a571245b1118de81fb86e7c1bc1ce"},
- {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6cc6545d6d76542aee3d18c1c9485fb7b9812b8df4ebe52c4535ec42081b48f"},
- {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:856bbe1616425f71c0df5ef2e8755e878d9504d5a531acba58ab4273c52c117a"},
- {file = "bitarray-2.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4bba8042ea6ab331ade91bc435d81ad72fddb098e49108610b0ce7780c14e68"},
- {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a035da89c959d98afc813e3c62f052690d67cfd55a36592f25d734b70de7d4b0"},
- {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6d70b1579da7fb71be5a841a1f965d19aca0ef27f629cfc07d06b09aafd0a333"},
- {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:405b83bed28efaae6d86b6ab287c75712ead0adbfab2a1075a1b7ab47dad4d62"},
- {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:7eb8be687c50da0b397d5e0ab7ca200b5ebb639e79a9f5e285851d1944c94be9"},
- {file = "bitarray-2.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eceb551dfeaf19c609003a69a0cf8264b0efd7abc3791a11dfabf4788daf0d19"},
- {file = "bitarray-2.9.2-cp38-cp38-win32.whl", hash = "sha256:bb198c6ed1edbcdaf3d1fa3c9c9d1cdb7e179a5134ef5ee660b53cdec43b34e7"},
- {file = "bitarray-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:648d2f2685590b0103c67a937c2fb9e09bcc8dfb166f0c7c77bd341902a6f5b3"},
- {file = "bitarray-2.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ea816dc8f8e65841a8bbdd30e921edffeeb6f76efe6a1eb0da147b60d539d1cf"},
- {file = "bitarray-2.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4d0e32530f941c41eddfc77600ec89b65184cb909c549336463a738fab3ed285"},
- {file = "bitarray-2.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4a22266fb416a3b6c258bf7f83c9fe531ba0b755a56986a81ad69dc0f3bcc070"},
- {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc6d3e80dd8239850f2604833ff3168b28909c8a9357abfed95632cccd17e3e7"},
- {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f135e804986b12bf14f2cd1eb86674c47dea86c4c5f0fa13c88978876b97ebe6"},
- {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87580c7f7d14f7ec401eda7adac1e2a25e95153e9c339872c8ae61b3208819a1"},
- {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64b433e26993127732ac7b66a7821b2537c3044355798de7c5fcb0af34b8296f"},
- {file = "bitarray-2.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e497c535f2a9b68c69d36631bf2dba243e05eb343b00b9c7bbdc8c601c6802d"},
- {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e40b3cb9fa1edb4e0175d7c06345c49c7925fe93e39ef55ecb0bc40c906b0c09"},
- {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f2f8692f95c9e377eb19ca519d30d1f884b02feb7e115f798de47570a359e43f"},
- {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f0b84fc50b6dbeced4fa390688c07c10a73222810fb0e08392bd1a1b8259de36"},
- {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:d656ad38c942e38a470ddbce26b5020e08e1a7ea86b8fd413bb9024b5189993a"},
- {file = "bitarray-2.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6ab0f1dbfe5070db98771a56aa14797595acd45a1af9eadfb193851a270e7996"},
- {file = "bitarray-2.9.2-cp39-cp39-win32.whl", hash = "sha256:0a99b23ac845a9ea3157782c97465e6ae026fe0c7c4c1ed1d88f759fd6ea52d9"},
- {file = "bitarray-2.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:9bbcfc7c279e8d74b076e514e669b683f77b4a2a328585b3f16d4c5259c91222"},
- {file = "bitarray-2.9.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:43847799461d8ba71deb4d97b47250c2c2fb66d82cd3cb8b4caf52bb97c03034"},
- {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4f44381b0a4bdf64416082f4f0e7140377ae962c0ced6f983c6d7bbfc034040"},
- {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a484061616fb4b158b80789bd3cb511f399d2116525a8b29b6334c68abc2310f"},
- {file = "bitarray-2.9.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ff9e38356cc803e06134cf8ae9758e836ccd1b793135ef3db53c7c5d71e93bc"},
- {file = "bitarray-2.9.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b44105792fbdcfbda3e26ee88786790fda409da4c71f6c2b73888108cf8f062f"},
- {file = "bitarray-2.9.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7e913098de169c7fc890638ce5e171387363eb812579e637c44261460ac00aa2"},
- {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6fe315355cdfe3ed22ef355b8bdc81a805ca4d0949d921576560e5b227a1112"},
- {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f708e91fdbe443f3bec2df394ed42328fb9b0446dff5cb4199023ac6499e09fd"},
- {file = "bitarray-2.9.2-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b7b09489b71f9f1f64c0fa0977e250ec24500767dab7383ba9912495849cadf"},
- {file = "bitarray-2.9.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:128cc3488176145b9b137fdcf54c1c201809bbb8dd30b260ee40afe915843b43"},
- {file = "bitarray-2.9.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:21f21e7f56206be346bdbda2a6bdb2165a5e6a11821f88fd4911c5a6bbbdc7e2"},
- {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f4dd3af86dd8a617eb6464622fb64ca86e61ce99b59b5c35d8cd33f9c30603d"},
- {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6465de861aff7a2559f226b37982007417eab8c3557543879987f58b453519bd"},
- {file = "bitarray-2.9.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbaf2bb71d6027152d603f1d5f31e0dfd5e50173d06f877bec484e5396d4594b"},
- {file = "bitarray-2.9.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2f32948c86e0d230a296686db28191b67ed229756f84728847daa0c7ab7406e3"},
- {file = "bitarray-2.9.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be94e5a685e60f9d24532af8fe5c268002e9016fa80272a94727f435de3d1003"},
- {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5cc9381fd54f3c23ae1039f977bfd6d041a5c3c1518104f616643c3a5a73b15"},
- {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd926e8ae4d1ed1ac4a8f37212a62886292f692bc1739fde98013bf210c2d175"},
- {file = "bitarray-2.9.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:461a3dafb9d5fda0bb3385dc507d78b1984b49da3fe4c6d56c869a54373b7008"},
- {file = "bitarray-2.9.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:393cb27fd859af5fd9c16eb26b1c59b17b390ff66b3ae5d0dd258270191baf13"},
- {file = "bitarray-2.9.2.tar.gz", hash = "sha256:a8f286a51a32323715d77755ed959f94bef13972e9a2fe71b609e40e6d27957e"},
-]
-
-[[package]]
-name = "blinker"
-version = "1.6.2"
-description = "Fast, simple object-to-object and broadcast signaling"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "blinker-1.6.2-py3-none-any.whl", hash = "sha256:c3d739772abb7bc2860abf5f2ec284223d9ad5c76da018234f6f50d6f31ab1f0"},
- {file = "blinker-1.6.2.tar.gz", hash = "sha256:4afd3de66ef3a9f8067559fb7a1cbe555c17dcbe15971b05d1b625c3e7abe213"},
-]
-
-[[package]]
-name = "boto3"
-version = "1.28.8"
-description = "The AWS SDK for Python"
-optional = false
-python-versions = ">= 3.7"
-files = [
- {file = "boto3-1.28.8-py3-none-any.whl", hash = "sha256:7132ac3f3a9c28b84dcc344cfb439d37d2c5ab45f6b577358fc9aeba5d5aab63"},
- {file = "boto3-1.28.8.tar.gz", hash = "sha256:cf88309d9b8cd9a2fb0c8049cb4b217b4e9dcb55bf670d6054b0bbe2eef25e57"},
-]
-
-[package.dependencies]
-botocore = ">=1.31.8,<1.32.0"
-jmespath = ">=0.7.1,<2.0.0"
-s3transfer = ">=0.6.0,<0.7.0"
-
-[package.extras]
-crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
-
-[[package]]
-name = "botocore"
-version = "1.31.8"
-description = "Low-level, data-driven core of boto 3."
-optional = false
-python-versions = ">= 3.7"
-files = [
- {file = "botocore-1.31.8-py3-none-any.whl", hash = "sha256:61ba7efaa6305c1928b9b3fbb6f780cbfbd762e19008d20c11ba52b47f20e1b0"},
- {file = "botocore-1.31.8.tar.gz", hash = "sha256:092baa2168ae78080b0c28011527bfc11d8debd3767aa1e9a4ce8a91fd9943a2"},
-]
-
-[package.dependencies]
-jmespath = ">=0.7.1,<2.0.0"
-python-dateutil = ">=2.1,<3.0.0"
-urllib3 = ">=1.25.4,<1.27"
-
-[package.extras]
-crt = ["awscrt (==0.16.26)"]
-
-[[package]]
-name = "cachetools"
-version = "5.3.2"
-description = "Extensible memoizing collections and decorators"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"},
- {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"},
-]
-
-[[package]]
-name = "cassandra-driver"
-version = "3.21.0"
-description = "DataStax Driver for Apache Cassandra"
-optional = false
-python-versions = "*"
-files = [
- {file = "cassandra-driver-3.21.0.tar.gz", hash = "sha256:ee976a061a7f981c0b34f4564f6fc171a86de87a67c4057c4f04f2713202e157"},
- {file = "cassandra_driver-3.21.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:3a1e3ca35ad13084048ce901954cdf18a584146d10be46539b85412c3d45d5ac"},
- {file = "cassandra_driver-3.21.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:daf280b6cade9fe2cb54145621b6001a765985709a735a7695ea7ea2de0ab751"},
- {file = "cassandra_driver-3.21.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:ec5ee471348ec914d7c4f64584bd8bd7e1b33d3821917ff09054244d32918117"},
- {file = "cassandra_driver-3.21.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7b0cf5aa2bdd215179222b2f033bc39f19ae123bdaad6b16fc37b5b1ec3d5b28"},
- {file = "cassandra_driver-3.21.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ff477931d4b5f1db16b4899bb0a7e46c78d72b8470bdd07e794cbe70dc188199"},
- {file = "cassandra_driver-3.21.0-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:d66d4918f85c01da85e0cc6e0c5ed51c3e488c0196d13c0a0abd4e3414b6fa18"},
- {file = "cassandra_driver-3.21.0-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:5a49a5a37b0768eb48eaf12c53c91b0653ace5d083af4346e6603fa28beb62a0"},
- {file = "cassandra_driver-3.21.0-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:2b3a9ef665872e89449cbd6e6dc723d0ed9be5a1c831236761e8771a38f1d213"},
- {file = "cassandra_driver-3.21.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:77f43afc6b360e0d31a537938fbd60bebc1aed710e186755ccb2e00f8b05e8d0"},
- {file = "cassandra_driver-3.21.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:3b79cc2675ff6e2ec88b388379f5c69ed749b84a9b22d752905e5f1683cd4629"},
- {file = "cassandra_driver-3.21.0-cp35-cp35m-win32.whl", hash = "sha256:f37b0710380063c5f42358a9534217f7a0fcd8a08985a43d4137fb954fcacb41"},
- {file = "cassandra_driver-3.21.0-cp35-cp35m-win_amd64.whl", hash = "sha256:d9af252db0a0dcd2fa0f27ff0862c8dbd8a4ea03d2e7dc7bcbe3bbae0a02eabf"},
- {file = "cassandra_driver-3.21.0-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:1ae6622d5c338eacac0ade1ef416fd01a16187be729d824ca89c41954e9c0548"},
- {file = "cassandra_driver-3.21.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:56505e9c8cab996c892f5d688234c0f87fd792d2387c545048ee3e5f2a48eaca"},
- {file = "cassandra_driver-3.21.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d49c8cbbcae2c5dc3b695e31c0e993e3c9c00143d9ba464bbf0efb61b7bb48c6"},
- {file = "cassandra_driver-3.21.0-cp36-cp36m-win32.whl", hash = "sha256:b1c5e02b24090623f00febf3531f48aa0387f8c5cc1926075989266e70a894ac"},
- {file = "cassandra_driver-3.21.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5c4aaedcda980f8fe33b12f08a2197f54dc4c48f61072aec490ef7aa8309eb3f"},
- {file = "cassandra_driver-3.21.0-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:0eb6cf8421ecd6925030e115236072bd553730bce7c82b8889830b21159813e8"},
- {file = "cassandra_driver-3.21.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4d6cbe0d005a71a28e8d6251139367745c7d3dc1ac152822b1de95af219a0a0c"},
- {file = "cassandra_driver-3.21.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6648282da46bc18f2502249aa35535c12fc22e5d2d6e41bdc6809d9d845787f7"},
- {file = "cassandra_driver-3.21.0-cp37-cp37m-win32.whl", hash = "sha256:a883979390e2f82f0fab37c8c1bf8cd0ad5a5cc2f5f8bd57f31718870366b083"},
- {file = "cassandra_driver-3.21.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fdbb1a5a8c02e9e8af95713aafe448020b9c8d04e4ac69486ab2fa4894a6146a"},
- {file = "cassandra_driver-3.21.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:feb638c3be9f71f36823cedb92f3fa55eb47ada0dabf528f6ad20020afb3e8f9"},
- {file = "cassandra_driver-3.21.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5d54fb30aa3ecaa943025bc98fed8fa4bd653a7efba9c7573f493085b7442c97"},
- {file = "cassandra_driver-3.21.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:ff248431521f4bf522b3f600f3a3a88b622a0a7b64326a5115f343d0b18c028e"},
- {file = "cassandra_driver-3.21.0-cp38-cp38-win32.whl", hash = "sha256:5c942123f4cb42a188ab9a95f0ca27cbc48c263cc8c061ecf9cd959c23efbb04"},
- {file = "cassandra_driver-3.21.0-cp38-cp38-win_amd64.whl", hash = "sha256:71d5f3e322feea3d8a8f32d593598b6f9790e3f7e28cadf3bce98af170984f5c"},
-]
-
-[package.dependencies]
-futures = "*"
-geomet = ">=0.1,<0.2"
-six = ">=1.9"
-
-[package.extras]
-graph = ["gremlinpython (==3.3.4)"]
-
-[[package]]
-name = "certifi"
-version = "2024.7.4"
-description = "Python package for providing Mozilla's CA Bundle."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"},
- {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"},
-]
-
-[[package]]
-name = "cffi"
-version = "1.16.0"
-description = "Foreign Function Interface for Python calling C code."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"},
- {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"},
- {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"},
- {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"},
- {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"},
- {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"},
- {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"},
- {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"},
- {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"},
- {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"},
- {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"},
- {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"},
- {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"},
- {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"},
- {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"},
- {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"},
- {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"},
- {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"},
- {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"},
- {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"},
- {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"},
- {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"},
- {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"},
- {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"},
- {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"},
- {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"},
- {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"},
- {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"},
- {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"},
- {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"},
- {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"},
- {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
-]
-
-[package.dependencies]
-pycparser = "*"
-
-[[package]]
-name = "cfgv"
-version = "3.4.0"
-description = "Validate configuration and produce human readable error messages."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
- {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
-]
-
-[[package]]
-name = "charset-normalizer"
-version = "3.3.2"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"},
- {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"},
- {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"},
- {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"},
- {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"},
- {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"},
- {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"},
- {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"},
-]
-
-[[package]]
-name = "ciso8601"
-version = "2.3.1"
-description = "Fast ISO8601 date time parser for Python written in C"
-optional = false
-python-versions = "*"
-files = [
- {file = "ciso8601-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:57db9a28e87f9e4fccba643fb70a9ba1515adc5e1325508eb2c10dd96620314c"},
- {file = "ciso8601-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c59646197ddbf84909b6c31d55f744cfeef51811e3910b61d0f58f2885823fd"},
- {file = "ciso8601-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a25da209193134842cd573464a5323f46fcc3ed781b633f15a34793ba7e1064"},
- {file = "ciso8601-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ae83f4e60fc7e260a4188e4ec4ac1bdd40bdb382eeda92fc266c5aa2f0a1ee"},
- {file = "ciso8601-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2c1ef17d1ea52a39b2dce6535583631ae4bfb65c76f0ee8c99413a6861a46c9e"},
- {file = "ciso8601-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3771049ba29bd1077588c0a24be1d53f7493e7cc686b2caa92f7cae129636a0e"},
- {file = "ciso8601-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:55381365366dacb57207cec610d26c9a6c0d237cb65a0cf67a2baaa5299f2366"},
- {file = "ciso8601-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f25647803c9a5aaaed130c53bbec7ea06a4f95ba5c7016f59e444b4ef7ac39e"},
- {file = "ciso8601-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:473288cd63efe6a2cf3f4b5f90394e53095358ccb13d6128f87a2da85d0f389b"},
- {file = "ciso8601-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:121d27c55f4455eaa27ba3bd602beca915df9a352f235e935636a4660321070e"},
- {file = "ciso8601-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef44cb4dc83f37019a356c7a72692cbe17072456f4879ca6bc0339f67eee5d00"},
- {file = "ciso8601-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:364702e338212b6c1a8643d9399ada21560cf132f363853473560625cb4207f1"},
- {file = "ciso8601-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8acb45545e6a654310c6ef788aacb2d73686646c414ceacdd9f5f78a83165af5"},
- {file = "ciso8601-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:99addd8b113f85fac549167073f317a318cd2b5841552598ceb97b97c5708a38"},
- {file = "ciso8601-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f39bb5936debf21c52e5d52b89f26857c303da80c43a72883946096a6ef5e561"},
- {file = "ciso8601-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:21cf83ca945bb26ecd95364ae2c9ed0276378e5fe35ce1b64d4c6d5b33038ea3"},
- {file = "ciso8601-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:013410263cba46748d2de29e9894341ae41223356cde7970478c32bd0984d10c"},
- {file = "ciso8601-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b26935687ef1837b56997d8c61f1d789e698be58b261410e629eda9c89812141"},
- {file = "ciso8601-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0d980a2a88030d4d8b2434623c250866a75b4979d289eba69bec445c51ace99f"},
- {file = "ciso8601-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:87721de54e008fb1c4c3978553b05a9c417aa25b76ddf5702d6f7e8d9b109288"},
- {file = "ciso8601-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f107a4c051e7c0416824279264d94f4ed3da0fbd82bd96ec3c3293426826de4"},
- {file = "ciso8601-2.3.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:02ecbd7c8336c4e1c6bb725b898e29414ee92bdc0be6c72fb07036836b1ac867"},
- {file = "ciso8601-2.3.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36525b1f380f4601533f4631c69911e44efb9cb50beab1da3248b0daa32bced4"},
- {file = "ciso8601-2.3.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:874d20c6339e9096baaadfd1b9610bb8d5b373a0f2858cc06de8142b98d2129c"},
- {file = "ciso8601-2.3.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:46a3663c2cf838f0149e1cdb8e4bdc95716e03cf2d5f803a6eb755d825896ebe"},
- {file = "ciso8601-2.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e8e76825f80ce313d75bbbef1d3b8bd9e0ce31dbc157d1981e9593922c9983e7"},
- {file = "ciso8601-2.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6850889813f3135e0aa18f0aaec64249dd81d36a1b9bce60bb45182930c86663"},
- {file = "ciso8601-2.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c690ac24ec3407f68cdfd5e032c6cb18126ef33d6c4b3db0669b9cbb8c96bd4"},
- {file = "ciso8601-2.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:024c52d5d0670f15ca3dc53eff7345b6eaee22fba929675f6a408f9d1e159d98"},
- {file = "ciso8601-2.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7ae2c3442d042de5330672d0d28486ed92f9d7c6dc010943aa618fd361d4638"},
- {file = "ciso8601-2.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:22128f0def36fa3c4cf0c482a216e8b8ad722def08bc11c07438eff82bdcd02a"},
- {file = "ciso8601-2.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:025859ec286a994aa3f2120c0f27d053b719cabc975398338374f2cc1f961125"},
- {file = "ciso8601-2.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2a64ff58904d4418d60fa9619014ae820ae21f7aef58da46df78a4c647f951ec"},
- {file = "ciso8601-2.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d1f85c0b7fa742bbfd18177137ccbaa3f867dd06157f91595075bb959a733048"},
- {file = "ciso8601-2.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ac59453664781dfddebee51f9a36e41819993823fdb09ddc0ce0e4bd3ff0c3"},
- {file = "ciso8601-2.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:eaecca7e0c3ef9e8f5e963e212b083684e849f9a9bb25834d3042363223a73cd"},
- {file = "ciso8601-2.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ad8f417c45eea973a694599b96f40d841215bfee352cb9963383e8d66b309981"},
- {file = "ciso8601-2.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:b869396e9756a7c0696d8eb69ce1d8980bea5e25c86e5996b10d78c900a4362c"},
- {file = "ciso8601-2.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7eb7b5ef8714d3d1fe9f3256b7a679ad783da899a0b7503a5ace78186735f840"},
- {file = "ciso8601-2.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:02828107880848ff497971ebc98e6dc851ad7af8ec14a58089e0e11f3111cad6"},
- {file = "ciso8601-2.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:566b4a8b2f9717e54ffcdd732a7c8051a91da30a60a4f1dafb62e303a1dbac69"},
- {file = "ciso8601-2.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58a749d63f28c2eda71416c9d6014113b0748abf5fd14c502b01bd515502fedf"},
- {file = "ciso8601-2.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cb135de0e3b8feb7e74a4f7a234e8c8545957fe8d26316a1a549553f425c629d"},
- {file = "ciso8601-2.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:695583810836a42945084b33621b22b0309701c6916689f6a3588fa44c5bc413"},
- {file = "ciso8601-2.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:21204d98496cf5c0511dc21533be55c2a2d34b8c65603946a116812ffbae3b2d"},
- {file = "ciso8601-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c29ea2b03dee2dc0a5d3e4a0b7d7768c597781e9fa451fe1025600f7cb55a89"},
- {file = "ciso8601-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7533256af90724b8b7a707dcd1be4b67989447595c8e1e1c28399d4fd51dac50"},
- {file = "ciso8601-2.3.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4bc9d577c0d1e57532513fc2899f5231727e28981a426767f7fa13dacb18c06"},
- {file = "ciso8601-2.3.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:4e30501eed43eea7ef64f032c81cd1d8b2020035cbdcefad40db72e2f3bc97ff"},
- {file = "ciso8601-2.3.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:070f568de3bc269268296cb9265704dc5fcb9d4c12b1f1c67536624174df5d09"},
- {file = "ciso8601-2.3.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:9065053c034c80c0afd74c71a4906675d07078a05cfd1cb5ff70661378cdbe60"},
- {file = "ciso8601-2.3.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac00d293cdb3d1a5c78e09b3d75c7b0292ab45d5b26853b436ff5087eba2165"},
- {file = "ciso8601-2.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:06941e2ee46701f083aeb21d13eb762d74d5ed6c46ff22119f27a42ed6edc8f9"},
- {file = "ciso8601-2.3.1.tar.gz", hash = "sha256:3212c7ffe5d8080270548b5f2692ffd2039683b6628a8d2ad456122cc5793c4c"},
-]
-
-[[package]]
-name = "click"
-version = "8.1.3"
-description = "Composable command line interface toolkit"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
- {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-
-[[package]]
-name = "cmem-cmempy"
-version = "21.2.3"
-description = "API wrapper for eccenca Corporate Memory"
-optional = false
-python-versions = "*"
-files = [
- {file = "cmem_cmempy-21.2.3-py3-none-any.whl", hash = "sha256:6b64b695e954df194ee8947ff203c63b35839dd2274dbef97f14c3fbefdac8fd"},
- {file = "cmem_cmempy-21.2.3.tar.gz", hash = "sha256:81c7f07291d4287a828db14bdc68a54497ada8ba19cac87048432a310df818aa"},
-]
-
-[package.dependencies]
-certifi = "*"
-pyparsing = "*"
-rdflib = "*"
-requests = "*"
-six = "*"
-
-[[package]]
-name = "colorama"
-version = "0.4.6"
-description = "Cross-platform colored terminal text."
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-files = [
- {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
- {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
-]
-
-[[package]]
-name = "coverage"
-version = "7.2.7"
-description = "Code coverage measurement for Python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"},
- {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"},
- {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"},
- {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"},
- {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"},
- {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"},
- {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"},
- {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"},
- {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"},
- {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"},
- {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"},
- {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"},
- {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"},
- {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"},
- {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"},
- {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"},
- {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"},
- {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"},
- {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"},
- {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"},
- {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"},
- {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"},
- {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"},
- {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"},
- {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"},
- {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"},
- {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"},
- {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"},
- {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"},
- {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"},
- {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"},
- {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"},
- {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"},
- {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"},
- {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"},
- {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"},
-]
-
-[package.dependencies]
-tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""}
-
-[package.extras]
-toml = ["tomli"]
-
-[[package]]
-name = "crontab"
-version = "1.0.1"
-description = "Parse and use crontab schedules in Python"
-optional = false
-python-versions = "*"
-files = [
- {file = "crontab-1.0.1.tar.gz", hash = "sha256:89477e3f93c81365e738d5ee2659509e6373bb2846de13922663e79aa74c6b91"},
-]
-
-[[package]]
-name = "cryptography"
-version = "43.0.1"
-description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"},
- {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"},
- {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"},
- {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"},
- {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"},
- {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"},
- {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"},
- {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"},
- {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"},
- {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"},
- {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"},
- {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"},
- {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"},
- {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"},
- {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"},
- {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"},
- {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"},
- {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"},
- {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"},
- {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"},
- {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"},
- {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"},
- {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"},
- {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"},
- {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"},
- {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"},
- {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"},
-]
-
-[package.dependencies]
-cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
-
-[package.extras]
-docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"]
-docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"]
-nox = ["nox"]
-pep8test = ["check-sdist", "click", "mypy", "ruff"]
-sdist = ["build"]
-ssh = ["bcrypt (>=3.1.5)"]
-test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
-test-randomorder = ["pytest-randomly"]
-
-[[package]]
-name = "databend-py"
-version = "0.4.6"
-description = "Python driver with native interface for Databend"
-optional = false
-python-versions = ">=3.4, <4"
-files = [
- {file = "databend-py-0.4.6.tar.gz", hash = "sha256:450ffabd75e5adff29cac394a975df98911ca468a45d8c12fe3eb5e85506c9b0"},
- {file = "databend_py-0.4.6-py3-none-any.whl", hash = "sha256:937566f2b1fd21d096bb2743374cd2f0ed36fa97edc0ec505c93da2975a7717f"},
-]
-
-[package.dependencies]
-environs = "*"
-"mysql.connector" = "*"
-pytz = "*"
-requests = "*"
-
-[[package]]
-name = "databend-sqlalchemy"
-version = "0.2.4"
-description = "Databend dialect for SQLAlchemy."
-optional = false
-python-versions = ">=3.4, <4"
-files = [
- {file = "databend_sqlalchemy-0.2.4-py3-none-any.whl", hash = "sha256:e50afe6dcb0c97830560ba8b9f0160d95f4e3b7199ad1955ee9c339fb28b91e6"},
-]
-
-[package.dependencies]
-databend-py = "*"
-"mysql.connector" = "*"
-sqlalchemy = "*"
-
-[package.extras]
-sqlalchemy = ["sqlalchemy (>1.3.21,<2.0)"]
-superset = ["apache-superset (>=1.4.1)"]
-
-[[package]]
-name = "debugpy"
-version = "1.8.9"
-description = "An implementation of the Debug Adapter Protocol for Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e"},
- {file = "debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f"},
- {file = "debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037"},
- {file = "debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e"},
- {file = "debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040"},
- {file = "debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70"},
- {file = "debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66"},
- {file = "debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"},
- {file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"},
- {file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"},
- {file = "debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11"},
- {file = "debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53"},
- {file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"},
- {file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"},
- {file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"},
- {file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"},
- {file = "debugpy-1.8.9-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea"},
- {file = "debugpy-1.8.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9"},
- {file = "debugpy-1.8.9-cp38-cp38-win32.whl", hash = "sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5"},
- {file = "debugpy-1.8.9-cp38-cp38-win_amd64.whl", hash = "sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693"},
- {file = "debugpy-1.8.9-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1"},
- {file = "debugpy-1.8.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65"},
- {file = "debugpy-1.8.9-cp39-cp39-win32.whl", hash = "sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c"},
- {file = "debugpy-1.8.9-cp39-cp39-win_amd64.whl", hash = "sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5"},
- {file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"},
- {file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"},
-]
-
-[[package]]
-name = "defusedxml"
-version = "0.7.1"
-description = "XML bomb protection for Python stdlib modules"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
- {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
-]
-
-[[package]]
-name = "deprecated"
-version = "1.2.14"
-description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"},
- {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"},
-]
-
-[package.dependencies]
-wrapt = ">=1.10,<2"
-
-[package.extras]
-dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"]
-
-[[package]]
-name = "disposable-email-domains"
-version = "0.0.95"
-description = "A set of disposable email domains"
-optional = false
-python-versions = "*"
-files = [
- {file = "disposable-email-domains-0.0.95.tar.gz", hash = "sha256:36fa1ee104c19e618a7ea33c12e1edf1d3cd5007411cb6d58262639f4256a983"},
- {file = "disposable_email_domains-0.0.95-py2.py3-none-any.whl", hash = "sha256:38770e55db520f280bcf03fc51757d75f080128d7723ce4c41054ae2420ed161"},
-]
-
-[package.extras]
-dev = ["check-manifest"]
-
-[[package]]
-name = "distlib"
-version = "0.3.8"
-description = "Distribution utilities"
-optional = false
-python-versions = "*"
-files = [
- {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
- {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
-]
-
-[[package]]
-name = "dnspython"
-version = "2.6.1"
-description = "DNS toolkit"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"},
- {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"},
-]
-
-[package.extras]
-dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"]
-dnssec = ["cryptography (>=41)"]
-doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"]
-doq = ["aioquic (>=0.9.25)"]
-idna = ["idna (>=3.6)"]
-trio = ["trio (>=0.23)"]
-wmi = ["wmi (>=1.5.1)"]
-
-[[package]]
-name = "e6data-python-connector"
-version = "1.1.9"
-description = "Client for the e6data distributed SQL Engine."
-optional = false
-python-versions = "*"
-files = [
- {file = "e6data-python-connector-1.1.9.tar.gz", hash = "sha256:b8b3ea750b397ddc7aa250c46ed8b2ae00edab64267ffce9a76e4add69d79fe8"},
-]
-
-[package.dependencies]
-future = "*"
-grpcio = "*"
-grpcio-tools = "*"
-pycryptodome = "*"
-python-dateutil = "*"
-pytz = "*"
-sqlalchemy = ">=1.0.0"
-thrift = "*"
-
-[[package]]
-name = "elementpath"
-version = "4.1.5"
-description = "XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "elementpath-4.1.5-py3-none-any.whl", hash = "sha256:2ac1a2fb31eb22bbbf817f8cf6752f844513216263f0e3892c8e79782fe4bb55"},
- {file = "elementpath-4.1.5.tar.gz", hash = "sha256:c2d6dc524b29ef751ecfc416b0627668119d8812441c555d7471da41d4bacb8d"},
-]
-
-[package.extras]
-dev = ["Sphinx", "coverage", "flake8", "lxml", "lxml-stubs", "memory-profiler", "memray", "mypy", "tox", "xmlschema (>=2.0.0)"]
-
-[[package]]
-name = "environs"
-version = "10.2.0"
-description = "simplified environment variable parsing"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "environs-10.2.0-py3-none-any.whl", hash = "sha256:579dddb252ef4bb83a302df82a99c98f6f3db30f043d1b7acff36264b0bfdc69"},
- {file = "environs-10.2.0.tar.gz", hash = "sha256:9513dd388c1eeb8e82f1ea5a701356abfb7a3d79925bff937ade67fe096e420d"},
-]
-
-[package.dependencies]
-marshmallow = ">=3.0.0"
-python-dotenv = "*"
-
-[package.extras]
-dev = ["environs[lint,tests]", "tox"]
-django = ["dj-database-url", "dj-email-url", "django-cache-url"]
-lint = ["flake8 (==7.0.0)", "flake8-bugbear (==23.11.28)", "mypy (==1.8.0)", "pre-commit (>=3.6,<4.0)"]
-tests = ["environs[django]", "pytest"]
-
-[[package]]
-name = "et-xmlfile"
-version = "1.1.0"
-description = "An implementation of lxml.xmlfile for the standard library"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "et_xmlfile-1.1.0-py3-none-any.whl", hash = "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada"},
- {file = "et_xmlfile-1.1.0.tar.gz", hash = "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c"},
-]
-
-[[package]]
-name = "exceptiongroup"
-version = "1.2.0"
-description = "Backport of PEP 654 (exception groups)"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
- {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
-]
-
-[package.extras]
-test = ["pytest (>=6)"]
-
-[[package]]
-name = "filelock"
-version = "3.13.1"
-description = "A platform independent file lock."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
- {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
-]
-
-[package.extras]
-docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
-testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
-typing = ["typing-extensions (>=4.8)"]
-
-[[package]]
-name = "flask"
-version = "2.3.2"
-description = "A simple framework for building complex web applications."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "Flask-2.3.2-py3-none-any.whl", hash = "sha256:77fd4e1249d8c9923de34907236b747ced06e5467ecac1a7bb7115ae0e9670b0"},
- {file = "Flask-2.3.2.tar.gz", hash = "sha256:8c2f9abd47a9e8df7f0c3f091ce9497d011dc3b31effcf4c85a6e2b50f4114ef"},
-]
-
-[package.dependencies]
-blinker = ">=1.6.2"
-click = ">=8.1.3"
-importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""}
-itsdangerous = ">=2.1.2"
-Jinja2 = ">=3.1.2"
-Werkzeug = ">=2.3.3"
-
-[package.extras]
-async = ["asgiref (>=3.2)"]
-dotenv = ["python-dotenv"]
-
-[[package]]
-name = "flask-limiter"
-version = "3.3.1"
-description = "Rate limiting for flask applications"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "Flask-Limiter-3.3.1.tar.gz", hash = "sha256:2b99fec0cfc44f490bd729da52bb89c5c4158f38812d0f3854c01d0a83664923"},
- {file = "Flask_Limiter-3.3.1-py3-none-any.whl", hash = "sha256:3451fb8d84f50007753b799831c57c59c1eb3432cc9754cc4b7e41a88d8bdf51"},
-]
-
-[package.dependencies]
-Flask = ">=2"
-limits = ">=2.8"
-ordered-set = ">4,<5"
-rich = ">=12,<14"
-typing-extensions = ">=4"
-
-[package.extras]
-memcached = ["limits[memcached]"]
-mongodb = ["limits[mongodb]"]
-redis = ["limits[redis]"]
-
-[[package]]
-name = "flask-login"
-version = "0.6.0"
-description = "User authentication and session management for Flask."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "Flask-Login-0.6.0.tar.gz", hash = "sha256:aa84fcfb4c3cf09ca58c08e816b7bce73f1349ba1cf13d00d8dffc5872d5fcf6"},
- {file = "Flask_Login-0.6.0-py3-none-any.whl", hash = "sha256:5cb01ce4dc253967b6ac722a11e46de83b6272ef7a19cc7b5725ae636916d04d"},
-]
-
-[package.dependencies]
-Flask = ">=1.0.4"
-Werkzeug = ">=1.0.1"
-
-[[package]]
-name = "flask-mail"
-version = "0.9.1"
-description = "Flask extension for sending email"
-optional = false
-python-versions = "*"
-files = [
- {file = "Flask-Mail-0.9.1.tar.gz", hash = "sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41"},
-]
-
-[package.dependencies]
-blinker = "*"
-Flask = "*"
-
-[[package]]
-name = "flask-migrate"
-version = "2.5.2"
-description = "SQLAlchemy database migrations for Flask applications using Alembic"
-optional = false
-python-versions = "*"
-files = [
- {file = "Flask-Migrate-2.5.2.tar.gz", hash = "sha256:a96ff1875a49a40bd3e8ac04fce73fdb0870b9211e6168608cbafa4eb839d502"},
- {file = "Flask_Migrate-2.5.2-py2.py3-none-any.whl", hash = "sha256:6fb038be63d4c60727d5dfa5f581a6189af5b4e2925bc378697b4f0a40cfb4e1"},
-]
-
-[package.dependencies]
-alembic = ">=0.7"
-Flask = ">=0.9"
-Flask-SQLAlchemy = ">=1.0"
-
-[[package]]
-name = "flask-restful"
-version = "0.3.10"
-description = "Simple framework for creating REST APIs"
-optional = false
-python-versions = "*"
-files = [
- {file = "Flask-RESTful-0.3.10.tar.gz", hash = "sha256:fe4af2ef0027df8f9b4f797aba20c5566801b6ade995ac63b588abf1a59cec37"},
- {file = "Flask_RESTful-0.3.10-py2.py3-none-any.whl", hash = "sha256:1cf93c535172f112e080b0d4503a8d15f93a48c88bdd36dd87269bdaf405051b"},
-]
-
-[package.dependencies]
-aniso8601 = ">=0.82"
-Flask = ">=0.8"
-pytz = "*"
-six = ">=1.3.0"
-
-[package.extras]
-docs = ["sphinx"]
-
-[[package]]
-name = "flask-sqlalchemy"
-version = "2.5.1"
-description = "Adds SQLAlchemy support to your Flask application."
-optional = false
-python-versions = ">= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*"
-files = [
- {file = "Flask-SQLAlchemy-2.5.1.tar.gz", hash = "sha256:2bda44b43e7cacb15d4e05ff3cc1f8bc97936cc464623424102bfc2c35e95912"},
- {file = "Flask_SQLAlchemy-2.5.1-py2.py3-none-any.whl", hash = "sha256:f12c3d4cc5cc7fdcc148b9527ea05671718c3ea45d50c7e732cceb33f574b390"},
-]
-
-[package.dependencies]
-Flask = ">=0.10"
-SQLAlchemy = ">=0.8.0"
-
-[[package]]
-name = "flask-talisman"
-version = "0.7.0"
-description = "HTTP security headers for Flask."
-optional = false
-python-versions = "*"
-files = [
- {file = "flask-talisman-0.7.0.tar.gz", hash = "sha256:468131464a249274ed226efc21b372518f442487e58918ccab8357eaa638fd1f"},
- {file = "flask_talisman-0.7.0-py2.py3-none-any.whl", hash = "sha256:eaa754f4b771dfbe473843391d69643b79e3a38c865790011ac5e4179c68e3ec"},
-]
-
-[package.dependencies]
-six = ">=1.9.0"
-
-[[package]]
-name = "flask-wtf"
-version = "1.1.1"
-description = "Form rendering, validation, and CSRF protection for Flask with WTForms."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "Flask-WTF-1.1.1.tar.gz", hash = "sha256:41c4244e9ae626d63bed42ae4785b90667b885b1535d5a4095e1f63060d12aa9"},
- {file = "Flask_WTF-1.1.1-py3-none-any.whl", hash = "sha256:7887d6f1ebb3e17bf648647422f0944c9a469d0fcf63e3b66fb9a83037e38b2c"},
-]
-
-[package.dependencies]
-Flask = "*"
-itsdangerous = "*"
-WTForms = "*"
-
-[package.extras]
-email = ["email-validator"]
-
-[[package]]
-name = "freezegun"
-version = "1.2.1"
-description = "Let your Python tests travel through time"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "freezegun-1.2.1-py3-none-any.whl", hash = "sha256:15103a67dfa868ad809a8f508146e396be2995172d25f927e48ce51c0bf5cb09"},
- {file = "freezegun-1.2.1.tar.gz", hash = "sha256:b4c64efb275e6bc68dc6e771b17ffe0ff0f90b81a2a5189043550b6519926ba4"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.7"
-
-[[package]]
-name = "fsspec"
-version = "2024.10.0"
-description = "File-system specification"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "fsspec-2024.10.0-py3-none-any.whl", hash = "sha256:03b9a6785766a4de40368b88906366755e2819e758b83705c88cd7cb5fe81871"},
- {file = "fsspec-2024.10.0.tar.gz", hash = "sha256:eda2d8a4116d4f2429db8550f2457da57279247dd930bb12f821b58391359493"},
-]
-
-[package.extras]
-abfs = ["adlfs"]
-adl = ["adlfs"]
-arrow = ["pyarrow (>=1)"]
-dask = ["dask", "distributed"]
-dev = ["pre-commit", "ruff"]
-doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"]
-dropbox = ["dropbox", "dropboxdrivefs", "requests"]
-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"]
-fuse = ["fusepy"]
-gcs = ["gcsfs"]
-git = ["pygit2"]
-github = ["requests"]
-gs = ["gcsfs"]
-gui = ["panel"]
-hdfs = ["pyarrow (>=1)"]
-http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"]
-libarchive = ["libarchive-c"]
-oci = ["ocifs"]
-s3 = ["s3fs"]
-sftp = ["paramiko"]
-smb = ["smbprotocol"]
-ssh = ["paramiko"]
-test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"]
-test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"]
-test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"]
-tqdm = ["tqdm"]
-
-[[package]]
-name = "funcy"
-version = "1.13"
-description = "A fancy and practical functional tools"
-optional = false
-python-versions = "*"
-files = [
- {file = "funcy-1.13-py2.py3-none-any.whl", hash = "sha256:141950038e72bdc2d56fa82468586a1d1291b9cc9346daaaa322dffed1d1da6e"},
- {file = "funcy-1.13.tar.gz", hash = "sha256:918f333f675d9841ec7d77b9f0d5a272ed290393a33c8ef20e605847de89b1c3"},
-]
-
-[[package]]
-name = "future"
-version = "0.18.3"
-description = "Clean single-source support for Python 3 and 2"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "future-0.18.3.tar.gz", hash = "sha256:34a17436ed1e96697a86f9de3d15a3b0be01d8bc8de9c1dffd59fb8234ed5307"},
-]
-
-[[package]]
-name = "futures"
-version = "3.0.5"
-description = "Backport of the concurrent.futures package from Python 3.2"
-optional = false
-python-versions = "*"
-files = [
- {file = "futures-3.0.5-py2-none-any.whl", hash = "sha256:f7f16b6bf9653a918a03f1f2c2d62aac0cd64b1bc088e93ea279517f6b61120b"},
- {file = "futures-3.0.5.tar.gz", hash = "sha256:0542525145d5afc984c88f914a0c85c77527f65946617edb5274f72406f981df"},
-]
-
-[[package]]
-name = "geomet"
-version = "0.1.2"
-description = "GeoJSON <-> WKT/WKB conversion utilities"
-optional = false
-python-versions = "*"
-files = [
- {file = "geomet-0.1.2.tar.gz", hash = "sha256:cef6c73cfc0c4ea3961e16a6979dce75ef0298f0023cbd482855134dcdf7c010"},
-]
-
-[package.dependencies]
-click = "*"
-six = "*"
-
-[[package]]
-name = "gevent"
-version = "23.9.1"
-description = "Coroutine-based network library"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "gevent-23.9.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:a3c5e9b1f766a7a64833334a18539a362fb563f6c4682f9634dea72cbe24f771"},
- {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b101086f109168b23fa3586fccd1133494bdb97f86920a24dc0b23984dc30b69"},
- {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36a549d632c14684bcbbd3014a6ce2666c5f2a500f34d58d32df6c9ea38b6535"},
- {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:272cffdf535978d59c38ed837916dfd2b5d193be1e9e5dcc60a5f4d5025dd98a"},
- {file = "gevent-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcb8612787a7f4626aa881ff15ff25439561a429f5b303048f0fca8a1c781c39"},
- {file = "gevent-23.9.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:d57737860bfc332b9b5aa438963986afe90f49645f6e053140cfa0fa1bdae1ae"},
- {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5f3c781c84794926d853d6fb58554dc0dcc800ba25c41d42f6959c344b4db5a6"},
- {file = "gevent-23.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dbb22a9bbd6a13e925815ce70b940d1578dbe5d4013f20d23e8a11eddf8d14a7"},
- {file = "gevent-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:707904027d7130ff3e59ea387dddceedb133cc742b00b3ffe696d567147a9c9e"},
- {file = "gevent-23.9.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:45792c45d60f6ce3d19651d7fde0bc13e01b56bb4db60d3f32ab7d9ec467374c"},
- {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e24c2af9638d6c989caffc691a039d7c7022a31c0363da367c0d32ceb4a0648"},
- {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e1ead6863e596a8cc2a03e26a7a0981f84b6b3e956101135ff6d02df4d9a6b07"},
- {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65883ac026731ac112184680d1f0f1e39fa6f4389fd1fc0bf46cc1388e2599f9"},
- {file = "gevent-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf7af500da05363e66f122896012acb6e101a552682f2352b618e541c941a011"},
- {file = "gevent-23.9.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c3e5d2fa532e4d3450595244de8ccf51f5721a05088813c1abd93ad274fe15e7"},
- {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c84d34256c243b0a53d4335ef0bc76c735873986d478c53073861a92566a8d71"},
- {file = "gevent-23.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ada07076b380918829250201df1d016bdafb3acf352f35e5693b59dceee8dd2e"},
- {file = "gevent-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:921dda1c0b84e3d3b1778efa362d61ed29e2b215b90f81d498eb4d8eafcd0b7a"},
- {file = "gevent-23.9.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:ed7a048d3e526a5c1d55c44cb3bc06cfdc1947d06d45006cc4cf60dedc628904"},
- {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c1abc6f25f475adc33e5fc2dbcc26a732608ac5375d0d306228738a9ae14d3b"},
- {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4368f341a5f51611411ec3fc62426f52ac3d6d42eaee9ed0f9eebe715c80184e"},
- {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52b4abf28e837f1865a9bdeef58ff6afd07d1d888b70b6804557e7908032e599"},
- {file = "gevent-23.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52e9f12cd1cda96603ce6b113d934f1aafb873e2c13182cf8e86d2c5c41982ea"},
- {file = "gevent-23.9.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de350fde10efa87ea60d742901e1053eb2127ebd8b59a7d3b90597eb4e586599"},
- {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fde6402c5432b835fbb7698f1c7f2809c8d6b2bd9d047ac1f5a7c1d5aa569303"},
- {file = "gevent-23.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:dd6c32ab977ecf7c7b8c2611ed95fa4aaebd69b74bf08f4b4960ad516861517d"},
- {file = "gevent-23.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:455e5ee8103f722b503fa45dedb04f3ffdec978c1524647f8ba72b4f08490af1"},
- {file = "gevent-23.9.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:7ccf0fd378257cb77d91c116e15c99e533374a8153632c48a3ecae7f7f4f09fe"},
- {file = "gevent-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d163d59f1be5a4c4efcdd13c2177baaf24aadf721fdf2e1af9ee54a998d160f5"},
- {file = "gevent-23.9.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7532c17bc6c1cbac265e751b95000961715adef35a25d2b0b1813aa7263fb397"},
- {file = "gevent-23.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:78eebaf5e73ff91d34df48f4e35581ab4c84e22dd5338ef32714264063c57507"},
- {file = "gevent-23.9.1-cp38-cp38-win32.whl", hash = "sha256:f632487c87866094546a74eefbca2c74c1d03638b715b6feb12e80120960185a"},
- {file = "gevent-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:62d121344f7465e3739989ad6b91f53a6ca9110518231553fe5846dbe1b4518f"},
- {file = "gevent-23.9.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:bf456bd6b992eb0e1e869e2fd0caf817f0253e55ca7977fd0e72d0336a8c1c6a"},
- {file = "gevent-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43daf68496c03a35287b8b617f9f91e0e7c0d042aebcc060cadc3f049aadd653"},
- {file = "gevent-23.9.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:7c28e38dcde327c217fdafb9d5d17d3e772f636f35df15ffae2d933a5587addd"},
- {file = "gevent-23.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fae8d5b5b8fa2a8f63b39f5447168b02db10c888a3e387ed7af2bd1b8612e543"},
- {file = "gevent-23.9.1-cp39-cp39-win32.whl", hash = "sha256:2c7b5c9912378e5f5ccf180d1fdb1e83f42b71823483066eddbe10ef1a2fcaa2"},
- {file = "gevent-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:a2898b7048771917d85a1d548fd378e8a7b2ca963db8e17c6d90c76b495e0e2b"},
- {file = "gevent-23.9.1.tar.gz", hash = "sha256:72c002235390d46f94938a96920d8856d4ffd9ddf62a303a0d7c118894097e34"},
-]
-
-[package.dependencies]
-cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""}
-greenlet = {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}
-"zope.event" = "*"
-"zope.interface" = "*"
-
-[package.extras]
-dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"]
-docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"]
-monitor = ["psutil (>=5.7.0)"]
-recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"]
-test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests", "setuptools"]
-
-[[package]]
-name = "google-api-python-client"
-version = "1.7.11"
-description = "Google API Client Library for Python"
-optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
-files = [
- {file = "google-api-python-client-1.7.11.tar.gz", hash = "sha256:a8a88174f66d92aed7ebbd73744c2c319b4b1ce828e565f9ec721352d2e2fb8c"},
- {file = "google_api_python_client-1.7.11-py2-none-any.whl", hash = "sha256:3121d55d106ef1a2756e8074239512055bd99eb44da417b3dd680f9a1385adec"},
-]
-
-[package.dependencies]
-google-auth = ">=1.4.1"
-google-auth-httplib2 = ">=0.0.3"
-httplib2 = ">=0.9.2,<1dev"
-six = ">=1.6.1,<2dev"
-uritemplate = ">=3.0.0,<4dev"
-
-[[package]]
-name = "google-auth"
-version = "2.26.1"
-description = "Google Authentication Library"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "google-auth-2.26.1.tar.gz", hash = "sha256:54385acca5c0fbdda510cd8585ba6f3fcb06eeecf8a6ecca39d3ee148b092590"},
- {file = "google_auth-2.26.1-py2.py3-none-any.whl", hash = "sha256:2c8b55e3e564f298122a02ab7b97458ccfcc5617840beb5d0ac757ada92c9780"},
-]
-
-[package.dependencies]
-cachetools = ">=2.0.0,<6.0"
-pyasn1-modules = ">=0.2.1"
-rsa = ">=3.1.4,<5"
-
-[package.extras]
-aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"]
-enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"]
-pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"]
-reauth = ["pyu2f (>=0.1.5)"]
-requests = ["requests (>=2.20.0,<3.0.0.dev0)"]
-
-[[package]]
-name = "google-auth-httplib2"
-version = "0.2.0"
-description = "Google Authentication Library: httplib2 transport"
-optional = false
-python-versions = "*"
-files = [
- {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"},
- {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"},
-]
-
-[package.dependencies]
-google-auth = "*"
-httplib2 = ">=0.19.0"
-
-[[package]]
-name = "google-auth-oauthlib"
-version = "1.2.0"
-description = "Google Authentication Library"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "google-auth-oauthlib-1.2.0.tar.gz", hash = "sha256:292d2d3783349f2b0734a0a0207b1e1e322ac193c2c09d8f7c613fb7cc501ea8"},
- {file = "google_auth_oauthlib-1.2.0-py2.py3-none-any.whl", hash = "sha256:297c1ce4cb13a99b5834c74a1fe03252e1e499716718b190f56bcb9c4abc4faf"},
-]
-
-[package.dependencies]
-google-auth = ">=2.15.0"
-requests-oauthlib = ">=0.7.0"
-
-[package.extras]
-tool = ["click (>=6.0.0)"]
-
-[[package]]
-name = "greenlet"
-version = "2.0.2"
-description = "Lightweight in-process concurrent programming"
-optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*"
-files = [
- {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"},
- {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"},
- {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"},
- {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"},
- {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"},
- {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d967650d3f56af314b72df7089d96cda1083a7fc2da05b375d2bc48c82ab3f3c"},
- {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"},
- {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"},
- {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"},
- {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"},
- {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"},
- {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"},
- {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"},
- {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"},
- {file = "greenlet-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d4606a527e30548153be1a9f155f4e283d109ffba663a15856089fb55f933e47"},
- {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"},
- {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"},
- {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"},
- {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"},
- {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"},
- {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"},
- {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"},
- {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"},
- {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"},
- {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"},
- {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"},
- {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"},
- {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"},
- {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"},
- {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"},
- {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"},
- {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"},
- {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"},
- {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"},
- {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"},
- {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"},
- {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"},
- {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"},
- {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"},
- {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"},
- {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"},
- {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"},
- {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"},
- {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"},
- {file = "greenlet-2.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1087300cf9700bbf455b1b97e24db18f2f77b55302a68272c56209d5587c12d1"},
- {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"},
- {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"},
- {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"},
- {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"},
- {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"},
- {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"},
- {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"},
- {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"},
- {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8512a0c38cfd4e66a858ddd1b17705587900dd760c6003998e9472b77b56d417"},
- {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"},
- {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"},
- {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"},
- {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"},
- {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"},
- {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"},
- {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"},
- {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"},
- {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"},
- {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"},
-]
-
-[package.extras]
-docs = ["Sphinx", "docutils (<0.18)"]
-test = ["objgraph", "psutil"]
-
-[[package]]
-name = "grpcio"
-version = "1.60.0"
-description = "HTTP/2-based RPC framework"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "grpcio-1.60.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:d020cfa595d1f8f5c6b343530cd3ca16ae5aefdd1e832b777f9f0eb105f5b139"},
- {file = "grpcio-1.60.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:b98f43fcdb16172dec5f4b49f2fece4b16a99fd284d81c6bbac1b3b69fcbe0ff"},
- {file = "grpcio-1.60.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:20e7a4f7ded59097c84059d28230907cd97130fa74f4a8bfd1d8e5ba18c81491"},
- {file = "grpcio-1.60.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:452ca5b4afed30e7274445dd9b441a35ece656ec1600b77fff8c216fdf07df43"},
- {file = "grpcio-1.60.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43e636dc2ce9ece583b3e2ca41df5c983f4302eabc6d5f9cd04f0562ee8ec1ae"},
- {file = "grpcio-1.60.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e306b97966369b889985a562ede9d99180def39ad42c8014628dd3cc343f508"},
- {file = "grpcio-1.60.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f897c3b127532e6befdcf961c415c97f320d45614daf84deba0a54e64ea2457b"},
- {file = "grpcio-1.60.0-cp310-cp310-win32.whl", hash = "sha256:b87efe4a380887425bb15f220079aa8336276398dc33fce38c64d278164f963d"},
- {file = "grpcio-1.60.0-cp310-cp310-win_amd64.whl", hash = "sha256:a9c7b71211f066908e518a2ef7a5e211670761651039f0d6a80d8d40054047df"},
- {file = "grpcio-1.60.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:fb464479934778d7cc5baf463d959d361954d6533ad34c3a4f1d267e86ee25fd"},
- {file = "grpcio-1.60.0-cp311-cp311-macosx_10_10_universal2.whl", hash = "sha256:4b44d7e39964e808b071714666a812049765b26b3ea48c4434a3b317bac82f14"},
- {file = "grpcio-1.60.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:90bdd76b3f04bdb21de5398b8a7c629676c81dfac290f5f19883857e9371d28c"},
- {file = "grpcio-1.60.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:91229d7203f1ef0ab420c9b53fe2ca5c1fbeb34f69b3bc1b5089466237a4a134"},
- {file = "grpcio-1.60.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b36a2c6d4920ba88fa98075fdd58ff94ebeb8acc1215ae07d01a418af4c0253"},
- {file = "grpcio-1.60.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:297eef542156d6b15174a1231c2493ea9ea54af8d016b8ca7d5d9cc65cfcc444"},
- {file = "grpcio-1.60.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:87c9224acba0ad8bacddf427a1c2772e17ce50b3042a789547af27099c5f751d"},
- {file = "grpcio-1.60.0-cp311-cp311-win32.whl", hash = "sha256:95ae3e8e2c1b9bf671817f86f155c5da7d49a2289c5cf27a319458c3e025c320"},
- {file = "grpcio-1.60.0-cp311-cp311-win_amd64.whl", hash = "sha256:467a7d31554892eed2aa6c2d47ded1079fc40ea0b9601d9f79204afa8902274b"},
- {file = "grpcio-1.60.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:a7152fa6e597c20cb97923407cf0934e14224af42c2b8d915f48bc3ad2d9ac18"},
- {file = "grpcio-1.60.0-cp312-cp312-macosx_10_10_universal2.whl", hash = "sha256:7db16dd4ea1b05ada504f08d0dca1cd9b926bed3770f50e715d087c6f00ad748"},
- {file = "grpcio-1.60.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:b0571a5aef36ba9177e262dc88a9240c866d903a62799e44fd4aae3f9a2ec17e"},
- {file = "grpcio-1.60.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fd9584bf1bccdfff1512719316efa77be235469e1e3295dce64538c4773840b"},
- {file = "grpcio-1.60.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6a478581b1a1a8fdf3318ecb5f4d0cda41cacdffe2b527c23707c9c1b8fdb55"},
- {file = "grpcio-1.60.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:77c8a317f0fd5a0a2be8ed5cbe5341537d5c00bb79b3bb27ba7c5378ba77dbca"},
- {file = "grpcio-1.60.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1c30bb23a41df95109db130a6cc1b974844300ae2e5d68dd4947aacba5985aa5"},
- {file = "grpcio-1.60.0-cp312-cp312-win32.whl", hash = "sha256:2aef56e85901c2397bd557c5ba514f84de1f0ae5dd132f5d5fed042858115951"},
- {file = "grpcio-1.60.0-cp312-cp312-win_amd64.whl", hash = "sha256:e381fe0c2aa6c03b056ad8f52f8efca7be29fb4d9ae2f8873520843b6039612a"},
- {file = "grpcio-1.60.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:92f88ca1b956eb8427a11bb8b4a0c0b2b03377235fc5102cb05e533b8693a415"},
- {file = "grpcio-1.60.0-cp37-cp37m-macosx_10_10_universal2.whl", hash = "sha256:e278eafb406f7e1b1b637c2cf51d3ad45883bb5bd1ca56bc05e4fc135dfdaa65"},
- {file = "grpcio-1.60.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:a48edde788b99214613e440fce495bbe2b1e142a7f214cce9e0832146c41e324"},
- {file = "grpcio-1.60.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de2ad69c9a094bf37c1102b5744c9aec6cf74d2b635558b779085d0263166454"},
- {file = "grpcio-1.60.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:073f959c6f570797272f4ee9464a9997eaf1e98c27cb680225b82b53390d61e6"},
- {file = "grpcio-1.60.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c826f93050c73e7769806f92e601e0efdb83ec8d7c76ddf45d514fee54e8e619"},
- {file = "grpcio-1.60.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9e30be89a75ee66aec7f9e60086fadb37ff8c0ba49a022887c28c134341f7179"},
- {file = "grpcio-1.60.0-cp37-cp37m-win_amd64.whl", hash = "sha256:b0fb2d4801546598ac5cd18e3ec79c1a9af8b8f2a86283c55a5337c5aeca4b1b"},
- {file = "grpcio-1.60.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:9073513ec380434eb8d21970e1ab3161041de121f4018bbed3146839451a6d8e"},
- {file = "grpcio-1.60.0-cp38-cp38-macosx_10_10_universal2.whl", hash = "sha256:74d7d9fa97809c5b892449b28a65ec2bfa458a4735ddad46074f9f7d9550ad13"},
- {file = "grpcio-1.60.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:1434ca77d6fed4ea312901122dc8da6c4389738bf5788f43efb19a838ac03ead"},
- {file = "grpcio-1.60.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e61e76020e0c332a98290323ecfec721c9544f5b739fab925b6e8cbe1944cf19"},
- {file = "grpcio-1.60.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675997222f2e2f22928fbba640824aebd43791116034f62006e19730715166c0"},
- {file = "grpcio-1.60.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5208a57eae445ae84a219dfd8b56e04313445d146873117b5fa75f3245bc1390"},
- {file = "grpcio-1.60.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:428d699c8553c27e98f4d29fdc0f0edc50e9a8a7590bfd294d2edb0da7be3629"},
- {file = "grpcio-1.60.0-cp38-cp38-win32.whl", hash = "sha256:83f2292ae292ed5a47cdcb9821039ca8e88902923198f2193f13959360c01860"},
- {file = "grpcio-1.60.0-cp38-cp38-win_amd64.whl", hash = "sha256:705a68a973c4c76db5d369ed573fec3367d7d196673fa86614b33d8c8e9ebb08"},
- {file = "grpcio-1.60.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:c193109ca4070cdcaa6eff00fdb5a56233dc7610216d58fb81638f89f02e4968"},
- {file = "grpcio-1.60.0-cp39-cp39-macosx_10_10_universal2.whl", hash = "sha256:676e4a44e740deaba0f4d95ba1d8c5c89a2fcc43d02c39f69450b1fa19d39590"},
- {file = "grpcio-1.60.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:5ff21e000ff2f658430bde5288cb1ac440ff15c0d7d18b5fb222f941b46cb0d2"},
- {file = "grpcio-1.60.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c86343cf9ff7b2514dd229bdd88ebba760bd8973dac192ae687ff75e39ebfab"},
- {file = "grpcio-1.60.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fd3b3968ffe7643144580f260f04d39d869fcc2cddb745deef078b09fd2b328"},
- {file = "grpcio-1.60.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:30943b9530fe3620e3b195c03130396cd0ee3a0d10a66c1bee715d1819001eaf"},
- {file = "grpcio-1.60.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b10241250cb77657ab315270b064a6c7f1add58af94befa20687e7c8d8603ae6"},
- {file = "grpcio-1.60.0-cp39-cp39-win32.whl", hash = "sha256:79a050889eb8d57a93ed21d9585bb63fca881666fc709f5d9f7f9372f5e7fd03"},
- {file = "grpcio-1.60.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a97a681e82bc11a42d4372fe57898d270a2707f36c45c6676e49ce0d5c41353"},
- {file = "grpcio-1.60.0.tar.gz", hash = "sha256:2199165a1affb666aa24adf0c97436686d0a61bc5fc113c037701fb7c7fceb96"},
-]
-
-[package.extras]
-protobuf = ["grpcio-tools (>=1.60.0)"]
-
-[[package]]
-name = "grpcio-tools"
-version = "1.48.2"
-description = "Protobuf code generator for gRPC"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "grpcio-tools-1.48.2.tar.gz", hash = "sha256:8902a035708555cddbd61b5467cea127484362decc52de03f061a1a520fe90cd"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:92acc3e10ba2b0dcb90a88ae9fe1cc0ffba6868545207e4ff20ca95284f8e3c9"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:e5bb396d63495667d4df42e506eed9d74fc9a51c99c173c04395fe7604c848f1"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:84a84d601a238572d049d3108e04fe4c206536e81076d56e623bd525a1b38def"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70564521e86a0de35ea9ac6daecff10cb46860aec469af65869974807ce8e98b"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdbbe63f6190187de5946891941629912ac8196701ed2253fa91624a397822ec"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae56f133b05b7e5d780ef7e032dd762adad7f3dc8f64adb43ff5bfabd659f435"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f0feb4f2b777fa6377e977faa89c26359d4f31953de15e035505b92f41aa6906"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-win32.whl", hash = "sha256:80f450272316ca0924545f488c8492649ca3aeb7044d4bf59c426dcdee527f7c"},
- {file = "grpcio_tools-1.48.2-cp310-cp310-win_amd64.whl", hash = "sha256:21ff50e321736eba22210bf9b94e05391a9ac345f26e7df16333dc75d63e74fb"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-linux_armv7l.whl", hash = "sha256:d598ccde6338b2cfbb3124f34c95f03394209013f9b1ed4a5360a736853b1c27"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:a43d26714933f23de93ea0bf9c86c66a6ede709b8ca32e357f9e2181703e64ae"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:55fdebc73fb580717656b1bafa4f8eca448726a7aa22726a6c0a7895d2f0f088"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8588819b22d0de3aa1951e1991cc3e4b9aa105eecf6e3e24eb0a2fc8ab958b3e"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9771d4d317dca029dfaca7ec9282d8afe731c18bc536ece37fd39b8a974cc331"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d886a9e052a038642b3af5d18e6f2085d1656d9788e202dc23258cf3a751e7ca"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d77e8b1613876e0d8fd17709509d4ceba13492816426bd156f7e88a4c47e7158"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-win32.whl", hash = "sha256:dcaaecdd5e847de5c1d533ea91522bf56c9e6b2dc98cdc0d45f0a1c26e846ea2"},
- {file = "grpcio_tools-1.48.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0119aabd9ceedfdf41b56b9fdc8284dd85a7f589d087f2694d743f346a368556"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-linux_armv7l.whl", hash = "sha256:189be2a9b672300ca6845d94016bdacc052fdbe9d1ae9e85344425efae2ff8ef"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:9443f5c30bac449237c3cf99da125f8d6e6c01e17972bc683ee73b75dea95573"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:e0403e095b343431195db1305248b50019ad55d3dd310254431af87e14ef83a2"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5410d6b601d1404835e34466bd8aee37213489b36ee1aad2276366e265ff29d4"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51be91b7c7056ff9ee48b1eccd4a2840b0126230803a5e09dfc082a5b16a91c1"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:516eedd5eb7af6326050bc2cfceb3a977b9cc1144f283c43cc4956905285c912"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d18599ab572b2f15a8f3db49503272d1bb4fcabb4b4d1214ef03aca1816b20a0"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-win32.whl", hash = "sha256:d18ef2adc05a8ef9e58ac46357f6d4ce7e43e077c7eda0a4425773461f9d0e6e"},
- {file = "grpcio_tools-1.48.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d9753944e5a6b6b78b76ce9d2ae0fe3f748008c1849deb7fadcb64489d6553b"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:3c8749dca04a8d302862ceeb1dfbdd071ee13b281395975f24405a347e5baa57"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:7307dd2408b82ea545ae63502ec03036b025f449568556ea9a056e06129a7a4e"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:072234859f6069dc43a6be8ad6b7d682f4ba1dc2e2db2ebf5c75f62eee0f6dfb"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cc298fbfe584de8876a85355efbcf796dfbcfac5948c9560f5df82e79336e2a"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f75973a42c710999acd419968bc79f00327e03e855bbe82c6529e003e49af660"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f766050e491d0b3203b6b85638015f543816a2eb7d089fc04e86e00f6de0e31d"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8e0d74403484eb77e8df2566a64b8b0b484b5c87903678c381634dd72f252d5e"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-win32.whl", hash = "sha256:cb75bac0cd43858cb759ef103fe68f8c540cb58b63dda127e710228fec3007b8"},
- {file = "grpcio_tools-1.48.2-cp38-cp38-win_amd64.whl", hash = "sha256:cabc8b0905cedbc3b2b7b2856334fa35cce3d4bc79ae241cacd8cca8940a5c85"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:e712a6d00606ad19abdeae852a7e521d6f6d0dcea843708fecf3a38be16a851e"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:e7e7668f89fd598c5469bb58e16bfd12b511d9947ccc75aec94da31f62bc3758"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a415fbec67d4ff7efe88794cbe00cf548d0f0a5484cceffe0a0c89d47694c491"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d96e96ae7361aa51c9cd9c73b677b51f691f98df6086860fcc3c45852d96b0b0"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e20d7885a40e68a2bda92908acbabcdf3c14dd386c3845de73ba139e9df1f132"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8a5614251c46da07549e24f417cf989710250385e9d80deeafc53a0ee7df6325"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ace0035766fe01a1b096aa050be9f0a9f98402317e7aeff8bfe55349be32a407"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-win32.whl", hash = "sha256:4fa4300b1be59b046492ed3c5fdb59760bc6433f44c08f50de900f9552ec7461"},
- {file = "grpcio_tools-1.48.2-cp39-cp39-win_amd64.whl", hash = "sha256:0fb6c1c1e56eb26b224adc028a4204b6ad0f8b292efa28067dff273bbc8b27c4"},
-]
-
-[package.dependencies]
-grpcio = ">=1.48.2"
-protobuf = ">=3.12.0,<4.0dev"
-setuptools = "*"
-
-[[package]]
-name = "gspread"
-version = "5.11.2"
-description = "Google Spreadsheets Python API"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "gspread-5.11.2-py3-none-any.whl", hash = "sha256:525a9d3ef712d5747867d32b61f5d7aa035ead0835b56cd1ae2a6d310eaef077"},
- {file = "gspread-5.11.2.tar.gz", hash = "sha256:fdc477cbda48bc9ea77eb8a4bf737985bfdba44f04677e4d791eb70bcbae2b95"},
-]
-
-[package.dependencies]
-google-auth = ">=1.12.0"
-google-auth-oauthlib = ">=0.4.1"
-
-[[package]]
-name = "gunicorn"
-version = "22.0.0"
-description = "WSGI HTTP Server for UNIX"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"},
- {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"},
-]
-
-[package.dependencies]
-packaging = "*"
-
-[package.extras]
-eventlet = ["eventlet (>=0.24.1,!=0.36.0)"]
-gevent = ["gevent (>=1.4.0)"]
-setproctitle = ["setproctitle"]
-testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
-tornado = ["tornado (>=0.2)"]
-
-[[package]]
-name = "h11"
-version = "0.14.0"
-description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
- {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
-]
-
-[[package]]
-name = "httpcore"
-version = "0.16.3"
-description = "A minimal low-level HTTP client."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "httpcore-0.16.3-py3-none-any.whl", hash = "sha256:da1fb708784a938aa084bde4feb8317056c55037247c787bd7e19eb2c2949dc0"},
- {file = "httpcore-0.16.3.tar.gz", hash = "sha256:c5d6f04e2fc530f39e0c077e6a30caa53f1451096120f1f38b954afd0b17c0cb"},
-]
-
-[package.dependencies]
-anyio = ">=3.0,<5.0"
-certifi = "*"
-h11 = ">=0.13,<0.15"
-sniffio = "==1.*"
-
-[package.extras]
-http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (==1.*)"]
-
-[[package]]
-name = "httplib2"
-version = "0.19.0"
-description = "A comprehensive HTTP client library."
-optional = false
-python-versions = "*"
-files = [
- {file = "httplib2-0.19.0-py3-none-any.whl", hash = "sha256:749c32603f9bf16c1277f59531d502e8f1c2ca19901ae653b49c4ed698f0820e"},
- {file = "httplib2-0.19.0.tar.gz", hash = "sha256:e0d428dad43c72dbce7d163b7753ffc7a39c097e6788ef10f4198db69b92f08e"},
-]
-
-[package.dependencies]
-pyparsing = ">=2.4.2,<3"
-
-[[package]]
-name = "httpx"
-version = "0.23.3"
-description = "The next generation HTTP client."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "httpx-0.23.3-py3-none-any.whl", hash = "sha256:a211fcce9b1254ea24f0cd6af9869b3d29aba40154e947d2a07bb499b3e310d6"},
- {file = "httpx-0.23.3.tar.gz", hash = "sha256:9818458eb565bb54898ccb9b8b251a28785dd4a55afbc23d0eb410754fe7d0f9"},
-]
-
-[package.dependencies]
-certifi = "*"
-httpcore = ">=0.15.0,<0.17.0"
-rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]}
-sniffio = "*"
-
-[package.extras]
-brotli = ["brotli", "brotlicffi"]
-cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<13)"]
-http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (==1.*)"]
-
-[[package]]
-name = "identify"
-version = "2.5.33"
-description = "File identification library for Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"},
- {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"},
-]
-
-[package.extras]
-license = ["ukkonen"]
-
-[[package]]
-name = "idna"
-version = "3.7"
-description = "Internationalized Domain Names in Applications (IDNA)"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"},
- {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"},
-]
-
-[[package]]
-name = "importlib-metadata"
-version = "7.0.1"
-description = "Read metadata from Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"},
- {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"},
-]
-
-[package.dependencies]
-zipp = ">=0.5"
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
-perf = ["ipython"]
-testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"]
-
-[[package]]
-name = "importlib-resources"
-version = "6.1.1"
-description = "Read resources from Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"},
- {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"},
-]
-
-[package.dependencies]
-zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"]
-testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"]
-
-[[package]]
-name = "impyla"
-version = "0.16.0"
-description = "Python client for the Impala distributed query engine"
-optional = false
-python-versions = "*"
-files = [
- {file = "impyla-0.16.0.tar.gz", hash = "sha256:df46d6aae12cb3657293c739e2b61efead4ebf8186e813e7ea0d20e3527a3099"},
-]
-
-[package.dependencies]
-bitarray = "*"
-six = "*"
-thrift = ">=0.9.3"
-thriftpy2 = {version = ">=0.4.0,<0.5.0", markers = "python_version >= \"3.0\""}
-
-[package.extras]
-kerberos = ["thrift_sasl (==0.2.1)"]
-
-[[package]]
-name = "influxdb"
-version = "5.2.3"
-description = "InfluxDB client"
-optional = false
-python-versions = "*"
-files = [
- {file = "influxdb-5.2.3-py2.py3-none-any.whl", hash = "sha256:270ec1ec9cf1927a38cf5ec808e76f364482977577eb8c335f6aed5fcdc4cb25"},
- {file = "influxdb-5.2.3.tar.gz", hash = "sha256:30276c7e04bf7659424c733b239ba2f0804d7a1f3c59ec5dd3f88c56176c8d36"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.6.0"
-pytz = "*"
-requests = ">=2.17.0"
-six = ">=1.10.0"
-
-[package.extras]
-test = ["mock", "nose", "nose-cov", "requests-mock"]
-
-[[package]]
-name = "influxdb-client"
-version = "1.38.0"
-description = "InfluxDB 2.0 Python client library"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "influxdb_client-1.38.0-py3-none-any.whl", hash = "sha256:7d04c06b833800be3350c6cb8f19f01f3f4ab33a77a24969568a141e4e132358"},
- {file = "influxdb_client-1.38.0.tar.gz", hash = "sha256:88ee8c1beb6b3b1359f4117d51704d5da5ac70e598b9fe786823e36ac86175a8"},
-]
-
-[package.dependencies]
-certifi = ">=14.05.14"
-python-dateutil = ">=2.5.3"
-reactivex = ">=4.0.4"
-setuptools = ">=21.0.0"
-urllib3 = ">=1.26.0"
-
-[package.extras]
-async = ["aiocsv (>=1.2.2)", "aiohttp (>=3.8.1)"]
-ciso = ["ciso8601 (>=2.1.1)"]
-extra = ["numpy", "pandas (>=0.25.3)"]
-test = ["aioresponses (>=0.7.3)", "coverage (>=4.0.3)", "flake8 (>=5.0.3)", "httpretty (==1.0.5)", "jinja2 (==3.1.2)", "nose (>=1.3.7)", "pluggy (>=0.3.1)", "psutil (>=5.6.3)", "py (>=1.4.31)", "pytest (>=5.0.0)", "pytest-cov (>=3.0.0)", "pytest-timeout (>=2.1.0)", "randomize (>=0.13)", "sphinx (==1.8.5)", "sphinx-rtd-theme"]
-
-[[package]]
-name = "iniconfig"
-version = "2.0.0"
-description = "brain-dead simple config-ini parsing"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
- {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
-]
-
-[[package]]
-name = "isodate"
-version = "0.6.1"
-description = "An ISO 8601 date/time/duration parser and formatter"
-optional = false
-python-versions = "*"
-files = [
- {file = "isodate-0.6.1-py2.py3-none-any.whl", hash = "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96"},
- {file = "isodate-0.6.1.tar.gz", hash = "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9"},
-]
-
-[package.dependencies]
-six = "*"
-
-[[package]]
-name = "itsdangerous"
-version = "2.1.2"
-description = "Safely pass data to untrusted environments and back."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "itsdangerous-2.1.2-py3-none-any.whl", hash = "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44"},
- {file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
-]
-
-[[package]]
-name = "jedi"
-version = "0.19.1"
-description = "An autocompletion tool for Python that can be used for text editors."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
- {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
-]
-
-[package.dependencies]
-parso = ">=0.8.3,<0.9.0"
-
-[package.extras]
-docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
-qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
-testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
-
-[[package]]
-name = "jinja2"
-version = "3.1.5"
-description = "A very fast and expressive template engine."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"},
- {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=2.0"
-
-[package.extras]
-i18n = ["Babel (>=2.7)"]
-
-[[package]]
-name = "jmespath"
-version = "1.0.1"
-description = "JSON Matching Expressions"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"},
- {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"},
-]
-
-[[package]]
-name = "jsonschema"
-version = "3.1.1"
-description = "An implementation of JSON Schema validation for Python"
-optional = false
-python-versions = "*"
-files = [
- {file = "jsonschema-3.1.1-py2.py3-none-any.whl", hash = "sha256:94c0a13b4a0616458b42529091624e66700a17f847453e52279e35509a5b7631"},
- {file = "jsonschema-3.1.1.tar.gz", hash = "sha256:2fa0684276b6333ff3c0b1b27081f4b2305f0a36cf702a23db50edb141893c3f"},
-]
-
-[package.dependencies]
-attrs = ">=17.4.0"
-importlib-metadata = "*"
-pyrsistent = ">=0.14.0"
-setuptools = "*"
-six = ">=1.11.0"
-
-[package.extras]
-format = ["idna", "jsonpointer (>1.13)", "rfc3987", "strict-rfc3339", "webcolors"]
-
-[[package]]
-name = "jwcrypto"
-version = "1.5.6"
-description = "Implementation of JOSE Web standards"
-optional = false
-python-versions = ">= 3.8"
-files = [
- {file = "jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:150d2b0ebbdb8f40b77f543fb44ffd2baeff48788be71f67f03566692fd55789"},
- {file = "jwcrypto-1.5.6.tar.gz", hash = "sha256:771a87762a0c081ae6166958a954f80848820b2ab066937dc8b8379d65b1b039"},
-]
-
-[package.dependencies]
-cryptography = ">=3.4"
-typing-extensions = ">=4.5.0"
-
-[[package]]
-name = "ldap3"
-version = "2.9.1"
-description = "A strictly RFC 4510 conforming LDAP V3 pure Python client library"
-optional = false
-python-versions = "*"
-files = [
- {file = "ldap3-2.9.1-py2.py3-none-any.whl", hash = "sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70"},
- {file = "ldap3-2.9.1.tar.gz", hash = "sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f"},
-]
-
-[package.dependencies]
-pyasn1 = ">=0.4.6"
-
-[[package]]
-name = "limits"
-version = "3.7.0"
-description = "Rate limiting utilities"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "limits-3.7.0-py3-none-any.whl", hash = "sha256:c528817b7fc15f3e86ad091ba3e40231f6430a91b753db864767684cda8a7f2e"},
- {file = "limits-3.7.0.tar.gz", hash = "sha256:124c6a04d2f4b20990fb1de019eec9474d6c1346c70d8fd0561609b86998b64a"},
-]
-
-[package.dependencies]
-deprecated = ">=1.2"
-importlib-resources = ">=1.3"
-packaging = ">=21,<24"
-typing-extensions = "*"
-
-[package.extras]
-all = ["aetcd", "coredis (>=3.4.0,<5)", "emcache (>=0.6.1)", "emcache (>=1)", "etcd3", "motor (>=3,<4)", "pymemcache (>3,<5.0.0)", "pymongo (>4.1,<5)", "redis (>3,!=4.5.2,!=4.5.3,<6.0.0)", "redis (>=4.2.0,!=4.5.2,!=4.5.3)"]
-async-etcd = ["aetcd"]
-async-memcached = ["emcache (>=0.6.1)", "emcache (>=1)"]
-async-mongodb = ["motor (>=3,<4)"]
-async-redis = ["coredis (>=3.4.0,<5)"]
-etcd = ["etcd3"]
-memcached = ["pymemcache (>3,<5.0.0)"]
-mongodb = ["pymongo (>4.1,<5)"]
-redis = ["redis (>3,!=4.5.2,!=4.5.3,<6.0.0)"]
-rediscluster = ["redis (>=4.2.0,!=4.5.2,!=4.5.3)"]
-
-[[package]]
-name = "mako"
-version = "1.3.0"
-description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "Mako-1.3.0-py3-none-any.whl", hash = "sha256:57d4e997349f1a92035aa25c17ace371a4213f2ca42f99bee9a602500cfd54d9"},
- {file = "Mako-1.3.0.tar.gz", hash = "sha256:e3a9d388fd00e87043edbe8792f45880ac0114e9c4adc69f6e9bfb2c55e3b11b"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=0.9.2"
-
-[package.extras]
-babel = ["Babel"]
-lingua = ["lingua"]
-testing = ["pytest"]
-
-[[package]]
-name = "markdown-it-py"
-version = "3.0.0"
-description = "Python port of markdown-it. Markdown parsing, done right!"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
- {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
-]
-
-[package.dependencies]
-mdurl = ">=0.1,<1.0"
-
-[package.extras]
-benchmarking = ["psutil", "pytest", "pytest-benchmark"]
-code-style = ["pre-commit (>=3.0,<4.0)"]
-compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
-linkify = ["linkify-it-py (>=1,<3)"]
-plugins = ["mdit-py-plugins"]
-profiling = ["gprof2dot"]
-rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
-testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
-
-[[package]]
-name = "markupsafe"
-version = "2.1.1"
-description = "Safely add untrusted strings to HTML/XML markup."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
- {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
- {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
- {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
- {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
- {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
-]
-
-[[package]]
-name = "marshmallow"
-version = "3.20.2"
-description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "marshmallow-3.20.2-py3-none-any.whl", hash = "sha256:c21d4b98fee747c130e6bc8f45c4b3199ea66bc00c12ee1f639f0aeca034d5e9"},
- {file = "marshmallow-3.20.2.tar.gz", hash = "sha256:4c1daff273513dc5eb24b219a8035559dc573c8f322558ef85f5438ddd1236dd"},
-]
-
-[package.dependencies]
-packaging = ">=17.0"
-
-[package.extras]
-dev = ["pre-commit (>=2.4,<4.0)", "pytest", "pytz", "simplejson", "tox"]
-docs = ["alabaster (==0.7.15)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==3.0.1)", "sphinx-version-warning (==1.1.2)"]
-lint = ["pre-commit (>=2.4,<4.0)"]
-tests = ["pytest", "pytz", "simplejson"]
-
-[[package]]
-name = "maxminddb"
-version = "2.5.2"
-description = "Reader for the MaxMind DB format"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "maxminddb-2.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f5682963a5817066db50f219c33aaa7eb969888211a289a444c42b5dfa0c0f78"},
- {file = "maxminddb-2.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fe6bb1b5ea132fcd9fd7b16c80247f0ba667018d5f9f98cd645b297e3b02fbf"},
- {file = "maxminddb-2.5.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:955a3ec4b161e872cc615b7a09ae9770049e9794e7b3832e3d78905a65c5049d"},
- {file = "maxminddb-2.5.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:29d63e7711e5f95c7c190010e57dca9e262aee8ac300aaf75c3f7ede0b5a5863"},
- {file = "maxminddb-2.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:08a540ec3661f6ca40499c86028e96dca5780e9d471b485dc797859b0b22dd22"},
- {file = "maxminddb-2.5.2-cp310-cp310-win32.whl", hash = "sha256:17fdb691c389a0e956410d5baef9ad082a0aa67dd6aa231d193499e71a104c19"},
- {file = "maxminddb-2.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:d71b48d3dff9150a44e949b28fa5e7251a7a6895a3a77e200ce08410f096f12f"},
- {file = "maxminddb-2.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1409a045eb04cebb297221eab1020c4f05434d02c0961410f6996ef474482998"},
- {file = "maxminddb-2.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d839c480e4b93bb37bb1cc2777d77e6b2127c006e60b56f748f10571d8b0e471"},
- {file = "maxminddb-2.5.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bca70905515fe50684974a9afaa7db4a4e9fbfdebcb0c2cde9db8e048e0d8145"},
- {file = "maxminddb-2.5.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:67f97cd0c6aac39a51294b04a1e922532125285c24b18a58e2a9c92c7691fa9f"},
- {file = "maxminddb-2.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1a3fab6bea6cc59444e6bad2a4fbf91228f6f51dcb29d09ed091930a475bd8cb"},
- {file = "maxminddb-2.5.2-cp311-cp311-win32.whl", hash = "sha256:a99e3125528ea31e807f80e8c5b65118dc5cc122d0a435f1691a3cc1df55840c"},
- {file = "maxminddb-2.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:b6adf63695fa5e3d2549f7c2c9d82c6d252edd5c6ba67074637d2cb944143673"},
- {file = "maxminddb-2.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ed504ca9f3c42e8e71bdbe21f5b818139a1448ac15d7bb6ce12cf41e3b7e2067"},
- {file = "maxminddb-2.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a5053231228d7cbf57d98a741b3cbee9efa9e689348dbb56c414e5a4c7f6f1c"},
- {file = "maxminddb-2.5.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7e8688342bab592647313cd2054779bcd35ad85933424ceae9f07e3a9779986"},
- {file = "maxminddb-2.5.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:335ee3140b41d4e751c14f8fae297aa064c7d3f184c9fbb2790336123187c440"},
- {file = "maxminddb-2.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b0203fa2731da45e5461f6e8a0768e85bba8e02137a1598b3fcadf7cbfe8e6f2"},
- {file = "maxminddb-2.5.2-cp312-cp312-win32.whl", hash = "sha256:8b89129de70e1629f200df9dfda4e4f477c26b05c29e0836604a00209c9466d5"},
- {file = "maxminddb-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:099f4e27feec4bb9658034a3eb853e746721fc15709030bee4f2f889f4a34185"},
- {file = "maxminddb-2.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:19d8d1e9bbc5281fb4c8112d541d2bd350fd8b5ddfbb43a6951e46df7cd27b9d"},
- {file = "maxminddb-2.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94183a78628cad257183a88ce12a3bb9ffbfe0544bd0c1aafc1f9dc55629dd1b"},
- {file = "maxminddb-2.5.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17de49660372dcccaa23958eccdd1c2464f92f594d027045ad76788db14a5da4"},
- {file = "maxminddb-2.5.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae05c4f87b1dd9a21d430c52451eef5f3bd5af609d093408db91fe0dc4d8d7d1"},
- {file = "maxminddb-2.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cb718908b9dffa10e02361094158ae68ded5a82c750de89737437999a81bafe"},
- {file = "maxminddb-2.5.2-cp38-cp38-win32.whl", hash = "sha256:e0faa0c4c458eb0eb2f267daa7b106baef72c3c7ebcbece00b9e974fc8321412"},
- {file = "maxminddb-2.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:bac5a29fdc5df9222f7baecbcc4a88b309a66a7d147b34160940c0850ee4b9c5"},
- {file = "maxminddb-2.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c204f53ef7c1d77e9fb0dba415dbb56419f2b08ccaca66cd772e29b3a793c3e7"},
- {file = "maxminddb-2.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae98508a200db6f7ae5985a53039aba8eef7ed71d34b0a0e9c9145c3e6139fc3"},
- {file = "maxminddb-2.5.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e9198d25e252b27d4e9526d5fcd4b78341c23153363a94f1246de5afcd39f6d"},
- {file = "maxminddb-2.5.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b85b008f8e2cf3abfabdc24041549c51c97ea9a8bc46eeeadac8cec7acf9fbf0"},
- {file = "maxminddb-2.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6f50210506e9818162ef6706d3127efb0575dfe2cc98a7236ca2011f1cc3effe"},
- {file = "maxminddb-2.5.2-cp39-cp39-win32.whl", hash = "sha256:2bba43d370a57785f5ef61c10d0b4bf8de58d431da3c4c2ed78bb2ff3d07edbf"},
- {file = "maxminddb-2.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:2e01b09480b97d2ebe6765618fb12a0f52caa17368d6cf1f42481d6740428de7"},
- {file = "maxminddb-2.5.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:dd47d13376eaee2e8d1a1fb55d3d6ccdcc995bc931699967f7d5670ec6a454a3"},
- {file = "maxminddb-2.5.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abd626efaba4f0bc867462337f846796da0bb97b82125dbdbc63067947e353b0"},
- {file = "maxminddb-2.5.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ddbe547d83a2e28e81d9f59fd9708d3044ffb2398ee0f8df2e2a2e9cdea6646"},
- {file = "maxminddb-2.5.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:22184fa2514c15f5b39e4e2522f4f73d00afcf5eb7102c473f9376f3c3a03b81"},
- {file = "maxminddb-2.5.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5cb6702fbcc5b209ac3cffacd9cf0a5155feabbeb6fdcf497038be7cb6e52da6"},
- {file = "maxminddb-2.5.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0c3ebfc0af00445089629faffa4c5a1fcc42a1ca5d7dffc42bba314fde20c6d"},
- {file = "maxminddb-2.5.2-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:461dcf0a4f67aa1c9faea6d52c4060d39559bf68e99a514cf8c1e01af383f90b"},
- {file = "maxminddb-2.5.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e012e889639aab411f5483990188da51c968377f665dcb90584971dbf314d50a"},
- {file = "maxminddb-2.5.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:20596e452d03071db37a72c8ef9236126c04ed342864f68db0adf0d1bc9f642e"},
- {file = "maxminddb-2.5.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ec51b66774b102824c9a3dd4916356283f6a61db1868d4ebcb98bf26486718e"},
- {file = "maxminddb-2.5.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fda0dd512f345cc92492f96c61a0df47efc2e2064c15e8053ab2114b362d64d"},
- {file = "maxminddb-2.5.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:862fcfe226ebda29a537cdce678dc8dc71ca6540ad2483099f80c6a1ee4cdbdd"},
- {file = "maxminddb-2.5.2.tar.gz", hash = "sha256:b3c33e4fc7821ee6c9f40837116e16ab6175863d4a64eee024c5bec686690a87"},
-]
-
-[package.dependencies]
-setuptools = ">=68.2.2"
-
-[[package]]
-name = "maxminddb-geolite2"
-version = "2018.703"
-description = "Provides access to the geolite2 database. This product includes GeoLite2 data created by MaxMind, available from http://www.maxmind.com/"
-optional = false
-python-versions = "*"
-files = [
- {file = "maxminddb-geolite2-2018.703.tar.gz", hash = "sha256:2bd118c5567f3a8323d6c5da23a6e6d52cfc09cd9987b54eb712cf6001a96e03"},
-]
-
-[package.dependencies]
-maxminddb = "*"
-
-[[package]]
-name = "mdurl"
-version = "0.1.2"
-description = "Markdown URL utilities"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
- {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
-]
-
-[[package]]
-name = "memsql"
-version = "3.2.0"
-description = "Useful utilities and plugins for MemSQL integration."
-optional = false
-python-versions = "*"
-files = [
- {file = "memsql-3.2.0.tar.gz", hash = "sha256:d986c979d066e243bb35a16c030358838b2c155b9f0dee046f7dac1e99c291d6"},
-]
-
-[package.dependencies]
-mysqlclient = ">=1.4,<3.0"
-python-dateutil = "<3.0"
-simplejson = "*"
-wraptor = "*"
-
-[[package]]
-name = "mock"
-version = "5.0.2"
-description = "Rolling backport of unittest.mock for all Pythons"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "mock-5.0.2-py3-none-any.whl", hash = "sha256:0e0bc5ba78b8db3667ad636d964eb963dc97a59f04c6f6214c5f0e4a8f726c56"},
- {file = "mock-5.0.2.tar.gz", hash = "sha256:06f18d7d65b44428202b145a9a36e99c2ee00d1eb992df0caf881d4664377891"},
-]
-
-[package.extras]
-build = ["blurb", "twine", "wheel"]
-docs = ["sphinx"]
-test = ["pytest", "pytest-cov"]
-
-[[package]]
-name = "msgpack"
-version = "1.0.7"
-description = "MessagePack serializer"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"},
- {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"},
- {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"},
- {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"},
- {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"},
- {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"},
- {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"},
- {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"},
- {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"},
- {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"},
- {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"},
- {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"},
- {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"},
- {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"},
- {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"},
- {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"},
- {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"},
- {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"},
- {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"},
- {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"},
- {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"},
- {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"},
- {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"},
- {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"},
- {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"},
- {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"},
- {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"},
- {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"},
- {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"},
- {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"},
- {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"},
- {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"},
- {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"},
- {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"},
- {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"},
- {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"},
- {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"},
- {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"},
- {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"},
- {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"},
- {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"},
- {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"},
- {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"},
- {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"},
- {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"},
- {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"},
- {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"},
- {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"},
- {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"},
- {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"},
- {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"},
- {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"},
- {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"},
- {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"},
- {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"},
- {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"},
-]
-
-[[package]]
-name = "mysql-connector"
-version = "2.2.9"
-description = "MySQL driver written in Python"
-optional = false
-python-versions = "*"
-files = [
- {file = "mysql-connector-2.2.9.tar.gz", hash = "sha256:1733e6ce52a049243de3264f1fbc22a852cb35458c4ad739ba88189285efdf32"},
-]
-
-[[package]]
-name = "mysqlclient"
-version = "2.1.1"
-description = "Python interface to MySQL"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "mysqlclient-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c1ed71bd6244993b526113cca3df66428609f90e4652f37eb51c33496d478b37"},
- {file = "mysqlclient-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:c812b67e90082a840efb82a8978369e6e69fc62ce1bda4ca8f3084a9d862308b"},
- {file = "mysqlclient-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:0d1cd3a5a4d28c222fa199002810e8146cffd821410b67851af4cc80aeccd97c"},
- {file = "mysqlclient-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b355c8b5a7d58f2e909acdbb050858390ee1b0e13672ae759e5e784110022994"},
- {file = "mysqlclient-2.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:996924f3483fd36a34a5812210c69e71dea5a3d5978d01199b78b7f6d485c855"},
- {file = "mysqlclient-2.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:dea88c8d3f5a5d9293dfe7f087c16dd350ceb175f2f6631c9cf4caf3e19b7a96"},
- {file = "mysqlclient-2.1.1.tar.gz", hash = "sha256:828757e419fb11dd6c5ed2576ec92c3efaa93a0f7c39e263586d1ee779c3d782"},
-]
-
-[[package]]
-name = "ndg-httpsclient"
-version = "0.5.1"
-description = "Provides enhanced HTTPS support for httplib and urllib2 using PyOpenSSL"
-optional = false
-python-versions = ">=2.7,<3.0.dev0 || >=3.4.dev0"
-files = [
- {file = "ndg_httpsclient-0.5.1-py2-none-any.whl", hash = "sha256:d2c7225f6a1c6cf698af4ebc962da70178a99bcde24ee6d1961c4f3338130d57"},
- {file = "ndg_httpsclient-0.5.1-py3-none-any.whl", hash = "sha256:dd174c11d971b6244a891f7be2b32ca9853d3797a72edb34fa5d7b07d8fff7d4"},
- {file = "ndg_httpsclient-0.5.1.tar.gz", hash = "sha256:d72faed0376ab039736c2ba12e30695e2788c4aa569c9c3e3d72131de2592210"},
-]
-
-[package.dependencies]
-pyasn1 = ">=0.1.1"
-PyOpenSSL = "*"
-
-[[package]]
-name = "netifaces"
-version = "0.11.0"
-description = "Portable network interface information."
-optional = false
-python-versions = "*"
-files = [
- {file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eb4813b77d5df99903af4757ce980a98c4d702bbcb81f32a0b305a1537bdf0b1"},
- {file = "netifaces-0.11.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:5f9ca13babe4d845e400921973f6165a4c2f9f3379c7abfc7478160e25d196a4"},
- {file = "netifaces-0.11.0-cp27-cp27m-win32.whl", hash = "sha256:7dbb71ea26d304e78ccccf6faccef71bb27ea35e259fb883cfd7fd7b4f17ecb1"},
- {file = "netifaces-0.11.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0f6133ac02521270d9f7c490f0c8c60638ff4aec8338efeff10a1b51506abe85"},
- {file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:08e3f102a59f9eaef70948340aeb6c89bd09734e0dca0f3b82720305729f63ea"},
- {file = "netifaces-0.11.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c03fb2d4ef4e393f2e6ffc6376410a22a3544f164b336b3a355226653e5efd89"},
- {file = "netifaces-0.11.0-cp34-cp34m-win32.whl", hash = "sha256:73ff21559675150d31deea8f1f8d7e9a9a7e4688732a94d71327082f517fc6b4"},
- {file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815eafdf8b8f2e61370afc6add6194bd5a7252ae44c667e96c4c1ecf418811e4"},
- {file = "netifaces-0.11.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:50721858c935a76b83dd0dd1ab472cad0a3ef540a1408057624604002fcfb45b"},
- {file = "netifaces-0.11.0-cp35-cp35m-win32.whl", hash = "sha256:c9a3a47cd3aaeb71e93e681d9816c56406ed755b9442e981b07e3618fb71d2ac"},
- {file = "netifaces-0.11.0-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:aab1dbfdc55086c789f0eb37affccf47b895b98d490738b81f3b2360100426be"},
- {file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c37a1ca83825bc6f54dddf5277e9c65dec2f1b4d0ba44b8fd42bc30c91aa6ea1"},
- {file = "netifaces-0.11.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:28f4bf3a1361ab3ed93c5ef360c8b7d4a4ae060176a3529e72e5e4ffc4afd8b0"},
- {file = "netifaces-0.11.0-cp36-cp36m-win32.whl", hash = "sha256:2650beee182fed66617e18474b943e72e52f10a24dc8cac1db36c41ee9c041b7"},
- {file = "netifaces-0.11.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cb925e1ca024d6f9b4f9b01d83215fd00fe69d095d0255ff3f64bffda74025c8"},
- {file = "netifaces-0.11.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:84e4d2e6973eccc52778735befc01638498781ce0e39aa2044ccfd2385c03246"},
- {file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18917fbbdcb2d4f897153c5ddbb56b31fa6dd7c3fa9608b7e3c3a663df8206b5"},
- {file = "netifaces-0.11.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:48324183af7f1bc44f5f197f3dad54a809ad1ef0c78baee2c88f16a5de02c4c9"},
- {file = "netifaces-0.11.0-cp37-cp37m-win32.whl", hash = "sha256:8f7da24eab0d4184715d96208b38d373fd15c37b0dafb74756c638bd619ba150"},
- {file = "netifaces-0.11.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2479bb4bb50968089a7c045f24d120f37026d7e802ec134c4490eae994c729b5"},
- {file = "netifaces-0.11.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:3ecb3f37c31d5d51d2a4d935cfa81c9bc956687c6f5237021b36d6fdc2815b2c"},
- {file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:96c0fe9696398253f93482c84814f0e7290eee0bfec11563bd07d80d701280c3"},
- {file = "netifaces-0.11.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c92ff9ac7c2282009fe0dcb67ee3cd17978cffbe0c8f4b471c00fe4325c9b4d4"},
- {file = "netifaces-0.11.0-cp38-cp38-win32.whl", hash = "sha256:d07b01c51b0b6ceb0f09fc48ec58debd99d2c8430b09e56651addeaf5de48048"},
- {file = "netifaces-0.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:469fc61034f3daf095e02f9f1bbac07927b826c76b745207287bc594884cfd05"},
- {file = "netifaces-0.11.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:5be83986100ed1fdfa78f11ccff9e4757297735ac17391b95e17e74335c2047d"},
- {file = "netifaces-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:54ff6624eb95b8a07e79aa8817288659af174e954cca24cdb0daeeddfc03c4ff"},
- {file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:841aa21110a20dc1621e3dd9f922c64ca64dd1eb213c47267a2c324d823f6c8f"},
- {file = "netifaces-0.11.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e76c7f351e0444721e85f975ae92718e21c1f361bda946d60a214061de1f00a1"},
- {file = "netifaces-0.11.0.tar.gz", hash = "sha256:043a79146eb2907edf439899f262b3dfe41717d34124298ed281139a8b93ca32"},
-]
-
-[[package]]
-name = "nodeenv"
-version = "1.8.0"
-description = "Node.js virtual environment builder"
-optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
-files = [
- {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
- {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
-]
-
-[package.dependencies]
-setuptools = "*"
-
-[[package]]
-name = "numpy"
-version = "1.24.4"
-description = "Fundamental package for array computing in Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"},
- {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"},
- {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"},
- {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"},
- {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"},
- {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"},
- {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"},
- {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"},
- {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"},
- {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"},
- {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"},
- {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"},
- {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"},
- {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"},
- {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"},
- {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"},
- {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"},
- {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"},
- {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"},
- {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"},
- {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"},
- {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"},
- {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"},
- {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"},
- {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"},
- {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"},
- {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"},
- {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"},
-]
-
-[[package]]
-name = "numpy"
-version = "1.26.3"
-description = "Fundamental package for array computing in Python"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"},
- {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"},
- {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"},
- {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"},
- {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"},
- {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"},
- {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"},
- {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"},
- {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"},
- {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"},
- {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"},
- {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"},
- {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"},
- {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"},
- {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"},
- {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"},
- {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"},
- {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"},
- {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"},
- {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"},
- {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"},
- {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"},
- {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"},
- {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"},
- {file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"},
- {file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"},
- {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"},
- {file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"},
- {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"},
- {file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"},
- {file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"},
- {file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"},
- {file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"},
- {file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"},
- {file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"},
- {file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"},
-]
-
-[[package]]
-name = "nzalchemy"
-version = "11.0.2"
-description = "Netezza Dialect for SQLAlchemy"
-optional = false
-python-versions = "*"
-files = [
- {file = "nzalchemy-11.0.2-py3-none-any.whl", hash = "sha256:b7a7909bdab2a511f46d205598ec2b7d09cea1042f2a04e8351048c7a2811348"},
- {file = "nzalchemy-11.0.2.tar.gz", hash = "sha256:4e05a5a8df984d43b8d301f7b19fb4f4408bac9eb0421205adb1ab29827de50e"},
-]
-
-[package.dependencies]
-nzpy = "*"
-SQLAlchemy = "<=1.3.24"
-
-[[package]]
-name = "nzpy"
-version = "1.15"
-description = "IBM Netezza python driver"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "nzpy-1.15-py3-none-any.whl", hash = "sha256:ca0290046cb9117032ae022fc64d11a383f753f410f0459657419f7ff1769820"},
- {file = "nzpy-1.15.tar.gz", hash = "sha256:6e492b7e7644a5f6f14d542372b4bfed58e8fcb8c79c075e3bfd27a1f7805ba6"},
-]
-
-[package.dependencies]
-scramp = "1.1.0"
-
-[[package]]
-name = "oauth2client"
-version = "4.1.3"
-description = "OAuth 2.0 client library"
-optional = false
-python-versions = "*"
-files = [
- {file = "oauth2client-4.1.3-py2.py3-none-any.whl", hash = "sha256:b8a81cc5d60e2d364f0b1b98f958dbd472887acaf1a5b05e21c28c31a2d6d3ac"},
- {file = "oauth2client-4.1.3.tar.gz", hash = "sha256:d486741e451287f69568a4d26d70d9acd73a2bbfa275746c535b4209891cccc6"},
-]
-
-[package.dependencies]
-httplib2 = ">=0.9.1"
-pyasn1 = ">=0.1.7"
-pyasn1-modules = ">=0.0.5"
-rsa = ">=3.1.4"
-six = ">=1.6.1"
-
-[[package]]
-name = "oauthlib"
-version = "3.2.2"
-description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"},
- {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"},
-]
-
-[package.extras]
-rsa = ["cryptography (>=3.0.0)"]
-signals = ["blinker (>=1.4.0)"]
-signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"]
-
-[[package]]
-name = "openpyxl"
-version = "3.0.7"
-description = "A Python library to read/write Excel 2010 xlsx/xlsm files"
-optional = false
-python-versions = ">=3.6,"
-files = [
- {file = "openpyxl-3.0.7-py2.py3-none-any.whl", hash = "sha256:46af4eaf201a89b610fcca177eed957635f88770a5462fb6aae4a2a52b0ff516"},
- {file = "openpyxl-3.0.7.tar.gz", hash = "sha256:6456a3b472e1ef0facb1129f3c6ef00713cebf62e736cd7a75bcc3247432f251"},
-]
-
-[package.dependencies]
-et-xmlfile = "*"
-
-[[package]]
-name = "oracledb"
-version = "2.5.1"
-description = "Python interface to Oracle Database"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "oracledb-2.5.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:54ea7b4da179eb3fefad338685b44fed657a9cd733fb0bfc09d344cfb266355e"},
- {file = "oracledb-2.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:05df7a5a61f4d26c986e235fae6f64a81afaac8f1dbef60e2e9ecf9236218e58"},
- {file = "oracledb-2.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d17c80063375a5d87a7ab57c8343e5434a16ea74f7be3b56f9100300ef0b69d6"},
- {file = "oracledb-2.5.1-cp310-cp310-win32.whl", hash = "sha256:51b3911ee822319e20f2e19d816351aac747591a59a0a96cf891c62c2a5c0c0d"},
- {file = "oracledb-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:e4e884625117e50b619c93828affbcffa594029ef8c8b40205394990e6af65a8"},
- {file = "oracledb-2.5.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:85318350fa4837b7b637e436fa5f99c17919d6329065e64d1e18e5a7cae52457"},
- {file = "oracledb-2.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:676c221227159d9cee25030c56ff9782f330115cb86164d92d3360f55b07654b"},
- {file = "oracledb-2.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e78c6de57b4b5df7f932337c57e59b62e34fc4527d2460c0cab10c2ab01825f8"},
- {file = "oracledb-2.5.1-cp311-cp311-win32.whl", hash = "sha256:0d5974327a1957538a144b073367104cdf8bb39cf056940995b75cb099535589"},
- {file = "oracledb-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:541bb5a107917b9d9eba1346318b42f8b6024e7dd3bef1451f0745364f03399c"},
- {file = "oracledb-2.5.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:970a9420cc351d650cc6716122e9aa50cfb8c27f425ffc9d83651fd3edff6090"},
- {file = "oracledb-2.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6788c128af5a3a45689453fc4832f32b4a0dae2696d9917c7631a2e02865148"},
- {file = "oracledb-2.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8778daa3f08639232341d802b95ca6da4c0c798c8530e4df331b3286d32e49d5"},
- {file = "oracledb-2.5.1-cp312-cp312-win32.whl", hash = "sha256:a44613f3dfacb2b9462c3871ee333fa535fbd0ec21942e14019fcfd572487db0"},
- {file = "oracledb-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:934d02da80bfc030c644c5c43fbe58119dc170f15b4dfdb6fe04c220a1f8730d"},
- {file = "oracledb-2.5.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0374481329fa873a2af24eb12de4fd597c6c111e148065200562eb75ea0c6be7"},
- {file = "oracledb-2.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:66e885de106701d1f2a630d19e183e491e4f1ccb8d78855f60396ba15856fb66"},
- {file = "oracledb-2.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcf446f6250d8edad5367ff03ad73dbbe672a2e4b060c51a774821dd723b0283"},
- {file = "oracledb-2.5.1-cp313-cp313-win32.whl", hash = "sha256:b02b93199a7073e9b5687fe2dfa83d25ea102ab261c577f9d55820d5ef193dda"},
- {file = "oracledb-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:173b6d132b230f0617380272181e14fc53aec65aaffe68b557a9b6040716a267"},
- {file = "oracledb-2.5.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:7d5efc94ce5bb657a5f43e2683e23cc4b4c53c4783e817759869472a113dac26"},
- {file = "oracledb-2.5.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6919cb69638a7dda45380d6530b6f2f7fd21ea7bdf8d38936653f9ebc4f7e3d6"},
- {file = "oracledb-2.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44f5eb220945a6e092975ebcb9afc3f1eb10420d04d6bfeace1207ba86d60431"},
- {file = "oracledb-2.5.1-cp38-cp38-win32.whl", hash = "sha256:aa6ce0dfc64dc7b30bcf477f978538ba82fa7060ecd7a1b9227925b471ae3b50"},
- {file = "oracledb-2.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:7a3115e4d445e3430d6f34083b7eed607309411f41472b66d145508f7b0c3770"},
- {file = "oracledb-2.5.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8a2627a0d29390aaef7211c5b3f7182dfd8e76c969b39d57ee3e43c1057c6fe7"},
- {file = "oracledb-2.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:730cd03e7fbf05acd32a221ead2a43020b3b91391597eaf728d724548f418b1b"},
- {file = "oracledb-2.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42524b586733daa896f675acad8b9f2fc2f4380656d60a22a109a573861fc93"},
- {file = "oracledb-2.5.1-cp39-cp39-win32.whl", hash = "sha256:7958c7796df9f8c97484768c88817dec5c6d49220fc4cccdfde12a1a883f3d46"},
- {file = "oracledb-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:92e0d176e3c76a1916f4e34fc3d84994ad74cce6b8664656c4dbecb8fa7e8c37"},
- {file = "oracledb-2.5.1.tar.gz", hash = "sha256:63d17ebb95f9129d0ab9386cb632c9e667e3be2c767278cc11a8e4585468de33"},
-]
-
-[package.dependencies]
-cryptography = ">=3.2.1"
-
-[[package]]
-name = "ordered-set"
-version = "4.1.0"
-description = "An OrderedSet is a custom MutableSet that remembers its order, so that every"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"},
- {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"},
-]
-
-[package.extras]
-dev = ["black", "mypy", "pytest"]
-
-[[package]]
-name = "packaging"
-version = "23.2"
-description = "Core utilities for Python packages"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
- {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
-]
-
-[[package]]
-name = "pandas"
-version = "1.3.4"
-description = "Powerful data structures for data analysis, time series, and statistics"
-optional = false
-python-versions = ">=3.7.1"
-files = [
- {file = "pandas-1.3.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9707bdc1ea9639c886b4d3be6e2a45812c1ac0c2080f94c31b71c9fa35556f9b"},
- {file = "pandas-1.3.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c2f44425594ae85e119459bb5abb0748d76ef01d9c08583a667e3339e134218e"},
- {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:372d72a3d8a5f2dbaf566a5fa5fa7f230842ac80f29a931fb4b071502cf86b9a"},
- {file = "pandas-1.3.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99d2350adb7b6c3f7f8f0e5dfb7d34ff8dd4bc0a53e62c445b7e43e163fce63"},
- {file = "pandas-1.3.4-cp310-cp310-win_amd64.whl", hash = "sha256:4acc28364863127bca1029fb72228e6f473bb50c32e77155e80b410e2068eeac"},
- {file = "pandas-1.3.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c2646458e1dce44df9f71a01dc65f7e8fa4307f29e5c0f2f92c97f47a5bf22f5"},
- {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5298a733e5bfbb761181fd4672c36d0c627320eb999c59c65156c6a90c7e1b4f"},
- {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22808afb8f96e2269dcc5b846decacb2f526dd0b47baebc63d913bf847317c8f"},
- {file = "pandas-1.3.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b528e126c13816a4374e56b7b18bfe91f7a7f6576d1aadba5dee6a87a7f479ae"},
- {file = "pandas-1.3.4-cp37-cp37m-win32.whl", hash = "sha256:fe48e4925455c964db914b958f6e7032d285848b7538a5e1b19aeb26ffaea3ec"},
- {file = "pandas-1.3.4-cp37-cp37m-win_amd64.whl", hash = "sha256:eaca36a80acaacb8183930e2e5ad7f71539a66805d6204ea88736570b2876a7b"},
- {file = "pandas-1.3.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:42493f8ae67918bf129869abea8204df899902287a7f5eaf596c8e54e0ac7ff4"},
- {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a388960f979665b447f0847626e40f99af8cf191bce9dc571d716433130cb3a7"},
- {file = "pandas-1.3.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ba0aac1397e1d7b654fccf263a4798a9e84ef749866060d19e577e927d66e1b"},
- {file = "pandas-1.3.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f567e972dce3bbc3a8076e0b675273b4a9e8576ac629149cf8286ee13c259ae5"},
- {file = "pandas-1.3.4-cp38-cp38-win32.whl", hash = "sha256:c1aa4de4919358c5ef119f6377bc5964b3a7023c23e845d9db7d9016fa0c5b1c"},
- {file = "pandas-1.3.4-cp38-cp38-win_amd64.whl", hash = "sha256:dd324f8ee05925ee85de0ea3f0d66e1362e8c80799eb4eb04927d32335a3e44a"},
- {file = "pandas-1.3.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d47750cf07dee6b55d8423471be70d627314277976ff2edd1381f02d52dbadf9"},
- {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d1dc09c0013d8faa7474574d61b575f9af6257ab95c93dcf33a14fd8d2c1bab"},
- {file = "pandas-1.3.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10e10a2527db79af6e830c3d5842a4d60383b162885270f8cffc15abca4ba4a9"},
- {file = "pandas-1.3.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:35c77609acd2e4d517da41bae0c11c70d31c87aae8dd1aabd2670906c6d2c143"},
- {file = "pandas-1.3.4-cp39-cp39-win32.whl", hash = "sha256:003ba92db58b71a5f8add604a17a059f3068ef4e8c0c365b088468d0d64935fd"},
- {file = "pandas-1.3.4-cp39-cp39-win_amd64.whl", hash = "sha256:a51528192755f7429c5bcc9e80832c517340317c861318fea9cea081b57c9afd"},
- {file = "pandas-1.3.4.tar.gz", hash = "sha256:a2aa18d3f0b7d538e21932f637fbfe8518d085238b429e4790a35e1e44a96ffc"},
-]
-
-[package.dependencies]
-numpy = [
- {version = ">=1.17.3", markers = "(platform_machine != \"aarch64\" and platform_machine != \"arm64\") and python_version < \"3.10\""},
- {version = ">=1.19.2", markers = "platform_machine == \"aarch64\" and python_version < \"3.10\""},
- {version = ">=1.20.0", markers = "platform_machine == \"arm64\" and python_version < \"3.10\""},
- {version = ">=1.21.0", markers = "python_version >= \"3.10\""},
-]
-python-dateutil = ">=2.7.3"
-pytz = ">=2017.3"
-
-[package.extras]
-test = ["hypothesis (>=3.58)", "pytest (>=6.0)", "pytest-xdist"]
-
-[[package]]
-name = "paramiko"
-version = "3.4.1"
-description = "SSH2 protocol library"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "paramiko-3.4.1-py3-none-any.whl", hash = "sha256:8e49fd2f82f84acf7ffd57c64311aa2b30e575370dc23bdb375b10262f7eac32"},
- {file = "paramiko-3.4.1.tar.gz", hash = "sha256:8b15302870af7f6652f2e038975c1d2973f06046cb5d7d65355668b3ecbece0c"},
-]
-
-[package.dependencies]
-bcrypt = ">=3.2"
-cryptography = ">=3.3"
-pynacl = ">=1.5"
-
-[package.extras]
-all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
-gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
-invoke = ["invoke (>=2.0)"]
-
-[[package]]
-name = "parsedatetime"
-version = "2.4"
-description = "Parse human-readable date/time text."
-optional = false
-python-versions = "*"
-files = [
- {file = "parsedatetime-2.4-py2-none-any.whl", hash = "sha256:9ee3529454bf35c40a77115f5a596771e59e1aee8c53306f346c461b8e913094"},
- {file = "parsedatetime-2.4.tar.gz", hash = "sha256:3d817c58fb9570d1eec1dd46fa9448cd644eeed4fb612684b02dfda3a79cb84b"},
-]
-
-[package.dependencies]
-future = "*"
-
-[[package]]
-name = "parso"
-version = "0.8.3"
-description = "A Python Parser"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
- {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
-]
-
-[package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["docopt", "pytest (<6.0.0)"]
-
-[[package]]
-name = "passlib"
-version = "1.7.3"
-description = "comprehensive password hashing framework supporting over 30 schemes"
-optional = false
-python-versions = "*"
-files = [
- {file = "passlib-1.7.3-py2.py3-none-any.whl", hash = "sha256:a203263a2dbb97f3103603c780dce7a7253722951c717b424ed8cd7c72587ae1"},
- {file = "passlib-1.7.3.tar.gz", hash = "sha256:0fe8b86a900b2885fed00cf5e96f040c7abd61496d65dec4c814e462f8499d8a"},
-]
-
-[package.extras]
-argon2 = ["argon2-cffi (>=18.2.0)"]
-bcrypt = ["bcrypt (>=3.1.0)"]
-build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"]
-totp = ["cryptography"]
-
-[[package]]
-name = "phoenixdb"
-version = "0.7"
-description = "Phoenix database adapter for Python"
-optional = false
-python-versions = "*"
-files = [
- {file = "phoenixdb-0.7.tar.gz", hash = "sha256:0a51c72b1f70d74d9c06772a97257c77f9540a252f780c31592b5dc36ff5c892"},
-]
-
-[package.dependencies]
-protobuf = ">=3.0.0"
-
-[[package]]
-name = "pinotdb"
-version = "5.1.2"
-description = "Python DB-API and SQLAlchemy dialect for Pinot."
-optional = false
-python-versions = ">=3.7,<4"
-files = [
- {file = "pinotdb-5.1.2-py3-none-any.whl", hash = "sha256:80026d57b2a6e3ae0c0645bd93fb9166d472756d65bbb5a00bbd4f1c93ef8cf1"},
- {file = "pinotdb-5.1.2.tar.gz", hash = "sha256:70bfa0456aecc8e1029161def20dd8897b084414a888f926014324df152a40bb"},
-]
-
-[package.dependencies]
-ciso8601 = ">=2.1.3,<3.0.0"
-httpx = ">=0.23.0,<0.24.0"
-
-[package.extras]
-sqlalchemy = ["requests (>=2.25.0,<3.0.0)", "sqlalchemy (>=1.4,<2)"]
-
-[[package]]
-name = "platformdirs"
-version = "3.11.0"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"},
- {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"},
-]
-
-[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"]
-
-[[package]]
-name = "pluggy"
-version = "1.3.0"
-description = "plugin and hook calling mechanisms for python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"},
- {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"},
-]
-
-[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
-
-[[package]]
-name = "ply"
-version = "3.11"
-description = "Python Lex & Yacc"
-optional = false
-python-versions = "*"
-files = [
- {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"},
- {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"},
-]
-
-[[package]]
-name = "pre-commit"
-version = "3.3.3"
-description = "A framework for managing and maintaining multi-language pre-commit hooks."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pre_commit-3.3.3-py2.py3-none-any.whl", hash = "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb"},
- {file = "pre_commit-3.3.3.tar.gz", hash = "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023"},
-]
-
-[package.dependencies]
-cfgv = ">=2.0.0"
-identify = ">=1.0.0"
-nodeenv = ">=0.11.1"
-pyyaml = ">=5.1"
-virtualenv = ">=20.10.0"
-
-[[package]]
-name = "prompt-toolkit"
-version = "3.0.43"
-description = "Library for building powerful interactive command lines in Python"
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"},
- {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"},
-]
-
-[package.dependencies]
-wcwidth = "*"
-
-[[package]]
-name = "protobuf"
-version = "3.20.2"
-description = "Protocol Buffers"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "protobuf-3.20.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:09e25909c4297d71d97612f04f41cea8fa8510096864f2835ad2f3b3df5a5559"},
- {file = "protobuf-3.20.2-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e8fbc522303e09036c752a0afcc5c0603e917222d8bedc02813fd73b4b4ed804"},
- {file = "protobuf-3.20.2-cp310-cp310-win32.whl", hash = "sha256:84a1544252a933ef07bb0b5ef13afe7c36232a774affa673fc3636f7cee1db6c"},
- {file = "protobuf-3.20.2-cp310-cp310-win_amd64.whl", hash = "sha256:2c0b040d0b5d5d207936ca2d02f00f765906622c07d3fa19c23a16a8ca71873f"},
- {file = "protobuf-3.20.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3cb608e5a0eb61b8e00fe641d9f0282cd0eedb603be372f91f163cbfbca0ded0"},
- {file = "protobuf-3.20.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:84fe5953b18a383fd4495d375fe16e1e55e0a3afe7b4f7b4d01a3a0649fcda9d"},
- {file = "protobuf-3.20.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:384164994727f274cc34b8abd41a9e7e0562801361ee77437099ff6dfedd024b"},
- {file = "protobuf-3.20.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e39cf61bb8582bda88cdfebc0db163b774e7e03364bbf9ce1ead13863e81e359"},
- {file = "protobuf-3.20.2-cp37-cp37m-win32.whl", hash = "sha256:18e34a10ae10d458b027d7638a599c964b030c1739ebd035a1dfc0e22baa3bfe"},
- {file = "protobuf-3.20.2-cp37-cp37m-win_amd64.whl", hash = "sha256:8228e56a865c27163d5d1d1771d94b98194aa6917bcfb6ce139cbfa8e3c27334"},
- {file = "protobuf-3.20.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d76b7bd42ac4a6e109742a4edf81ffe26ffd87c5993126d894fe48a120396a"},
- {file = "protobuf-3.20.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:f52dabc96ca99ebd2169dadbe018824ebda08a795c7684a0b7d203a290f3adb0"},
- {file = "protobuf-3.20.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f34464ab1207114e73bba0794d1257c150a2b89b7a9faf504e00af7c9fd58978"},
- {file = "protobuf-3.20.2-cp38-cp38-win32.whl", hash = "sha256:5d9402bf27d11e37801d1743eada54372f986a372ec9679673bfcc5c60441151"},
- {file = "protobuf-3.20.2-cp38-cp38-win_amd64.whl", hash = "sha256:9c673c8bfdf52f903081816b9e0e612186684f4eb4c17eeb729133022d6032e3"},
- {file = "protobuf-3.20.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:291fb4307094bf5ccc29f424b42268640e00d5240bf0d9b86bf3079f7576474d"},
- {file = "protobuf-3.20.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b4fdb29c5a7406e3f7ef176b2a7079baa68b5b854f364c21abe327bbeec01cdb"},
- {file = "protobuf-3.20.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7a5037af4e76c975b88c3becdf53922b5ffa3f2cddf657574a4920a3b33b80f3"},
- {file = "protobuf-3.20.2-cp39-cp39-win32.whl", hash = "sha256:a9e5ae5a8e8985c67e8944c23035a0dff2c26b0f5070b2f55b217a1c33bbe8b1"},
- {file = "protobuf-3.20.2-cp39-cp39-win_amd64.whl", hash = "sha256:c184485e0dfba4dfd451c3bd348c2e685d6523543a0f91b9fd4ae90eb09e8422"},
- {file = "protobuf-3.20.2-py2.py3-none-any.whl", hash = "sha256:c9cdf251c582c16fd6a9f5e95836c90828d51b0069ad22f463761d27c6c19019"},
- {file = "protobuf-3.20.2.tar.gz", hash = "sha256:712dca319eee507a1e7df3591e639a2b112a2f4a62d40fe7832a16fd19151750"},
-]
-
-[[package]]
-name = "psutil"
-version = "5.9.7"
-description = "Cross-platform lib for process and system monitoring in Python."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
-files = [
- {file = "psutil-5.9.7-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0bd41bf2d1463dfa535942b2a8f0e958acf6607ac0be52265ab31f7923bcd5e6"},
- {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:5794944462509e49d4d458f4dbfb92c47539e7d8d15c796f141f474010084056"},
- {file = "psutil-5.9.7-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:fe361f743cb3389b8efda21980d93eb55c1f1e3898269bc9a2a1d0bb7b1f6508"},
- {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e469990e28f1ad738f65a42dcfc17adaed9d0f325d55047593cb9033a0ab63df"},
- {file = "psutil-5.9.7-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:3c4747a3e2ead1589e647e64aad601981f01b68f9398ddf94d01e3dc0d1e57c7"},
- {file = "psutil-5.9.7-cp27-none-win32.whl", hash = "sha256:1d4bc4a0148fdd7fd8f38e0498639ae128e64538faa507df25a20f8f7fb2341c"},
- {file = "psutil-5.9.7-cp27-none-win_amd64.whl", hash = "sha256:4c03362e280d06bbbfcd52f29acd79c733e0af33d707c54255d21029b8b32ba6"},
- {file = "psutil-5.9.7-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ea36cc62e69a13ec52b2f625c27527f6e4479bca2b340b7a452af55b34fcbe2e"},
- {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1132704b876e58d277168cd729d64750633d5ff0183acf5b3c986b8466cd0284"},
- {file = "psutil-5.9.7-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe8b7f07948f1304497ce4f4684881250cd859b16d06a1dc4d7941eeb6233bfe"},
- {file = "psutil-5.9.7-cp36-cp36m-win32.whl", hash = "sha256:b27f8fdb190c8c03914f908a4555159327d7481dac2f01008d483137ef3311a9"},
- {file = "psutil-5.9.7-cp36-cp36m-win_amd64.whl", hash = "sha256:44969859757f4d8f2a9bd5b76eba8c3099a2c8cf3992ff62144061e39ba8568e"},
- {file = "psutil-5.9.7-cp37-abi3-win32.whl", hash = "sha256:c727ca5a9b2dd5193b8644b9f0c883d54f1248310023b5ad3e92036c5e2ada68"},
- {file = "psutil-5.9.7-cp37-abi3-win_amd64.whl", hash = "sha256:f37f87e4d73b79e6c5e749440c3113b81d1ee7d26f21c19c47371ddea834f414"},
- {file = "psutil-5.9.7-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:032f4f2c909818c86cea4fe2cc407f1c0f0cde8e6c6d702b28b8ce0c0d143340"},
- {file = "psutil-5.9.7.tar.gz", hash = "sha256:3f02134e82cfb5d089fddf20bb2e03fd5cd52395321d1c8458a9e58500ff417c"},
-]
-
-[package.extras]
-test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
-
-[[package]]
-name = "psycopg2-binary"
-version = "2.9.6"
-description = "psycopg2 - Python-PostgreSQL Database Adapter"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "psycopg2-binary-2.9.6.tar.gz", hash = "sha256:1f64dcfb8f6e0c014c7f55e51c9759f024f70ea572fbdef123f85318c297947c"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d26e0342183c762de3276cca7a530d574d4e25121ca7d6e4a98e4f05cb8e4df7"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c48d8f2db17f27d41fb0e2ecd703ea41984ee19362cbce52c097963b3a1b4365"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe9dc0a884a8848075e576c1de0290d85a533a9f6e9c4e564f19adf8f6e54a7"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a76e027f87753f9bd1ab5f7c9cb8c7628d1077ef927f5e2446477153a602f2c"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6460c7a99fc939b849431f1e73e013d54aa54293f30f1109019c56a0b2b2ec2f"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae102a98c547ee2288637af07393dd33f440c25e5cd79556b04e3fca13325e5f"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9972aad21f965599ed0106f65334230ce826e5ae69fda7cbd688d24fa922415e"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a40c00dbe17c0af5bdd55aafd6ff6679f94a9be9513a4c7e071baf3d7d22a70"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cacbdc5839bdff804dfebc058fe25684cae322987f7a38b0168bc1b2df703fb1"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f0438fa20fb6c7e202863e0d5ab02c246d35efb1d164e052f2f3bfe2b152bd0"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-win32.whl", hash = "sha256:b6c8288bb8a84b47e07013bb4850f50538aa913d487579e1921724631d02ea1b"},
- {file = "psycopg2_binary-2.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:61b047a0537bbc3afae10f134dc6393823882eb263088c271331602b672e52e9"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:964b4dfb7c1c1965ac4c1978b0f755cc4bd698e8aa2b7667c575fb5f04ebe06b"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afe64e9b8ea66866a771996f6ff14447e8082ea26e675a295ad3bdbffdd72afb"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15e2ee79e7cf29582ef770de7dab3d286431b01c3bb598f8e05e09601b890081"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa74c903a3c1f0d9b1c7e7b53ed2d929a4910e272add6700c38f365a6002820"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83456c2d4979e08ff56180a76429263ea254c3f6552cd14ada95cff1dec9bb8"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0645376d399bfd64da57148694d78e1f431b1e1ee1054872a5713125681cf1be"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e99e34c82309dd78959ba3c1590975b5d3c862d6f279f843d47d26ff89d7d7e1"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4ea29fc3ad9d91162c52b578f211ff1c931d8a38e1f58e684c45aa470adf19e2"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4ac30da8b4f57187dbf449294d23b808f8f53cad6b1fc3623fa8a6c11d176dd0"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78e6e2a00c223e164c417628572a90093c031ed724492c763721c2e0bc2a8df"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-win32.whl", hash = "sha256:1876843d8e31c89c399e31b97d4b9725a3575bb9c2af92038464231ec40f9edb"},
- {file = "psycopg2_binary-2.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:b4b24f75d16a89cc6b4cdff0eb6a910a966ecd476d1e73f7ce5985ff1328e9a6"},
- {file = "psycopg2_binary-2.9.6-cp36-cp36m-win32.whl", hash = "sha256:498807b927ca2510baea1b05cc91d7da4718a0f53cb766c154c417a39f1820a0"},
- {file = "psycopg2_binary-2.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0d236c2825fa656a2d98bbb0e52370a2e852e5a0ec45fc4f402977313329174d"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:34b9ccdf210cbbb1303c7c4db2905fa0319391bd5904d32689e6dd5c963d2ea8"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d2222e61f313c4848ff05353653bf5f5cf6ce34df540e4274516880d9c3763"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30637a20623e2a2eacc420059be11527f4458ef54352d870b8181a4c3020ae6b"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8122cfc7cae0da9a3077216528b8bb3629c43b25053284cc868744bfe71eb141"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38601cbbfe600362c43714482f43b7c110b20cb0f8172422c616b09b85a750c5"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c7e62ab8b332147a7593a385d4f368874d5fe4ad4e341770d4983442d89603e3"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2ab652e729ff4ad76d400df2624d223d6e265ef81bb8aa17fbd63607878ecbee"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c83a74b68270028dc8ee74d38ecfaf9c90eed23c8959fca95bd703d25b82c88e"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d4e6036decf4b72d6425d5b29bbd3e8f0ff1059cda7ac7b96d6ac5ed34ffbacd"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-win32.whl", hash = "sha256:a8c28fd40a4226b4a84bdf2d2b5b37d2c7bd49486b5adcc200e8c7ec991dfa7e"},
- {file = "psycopg2_binary-2.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:51537e3d299be0db9137b321dfb6a5022caaab275775680e0c3d281feefaca6b"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4499e0a83b7b7edcb8dabecbd8501d0d3a5ef66457200f77bde3d210d5debb"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7e13a5a2c01151f1208d5207e42f33ba86d561b7a89fca67c700b9486a06d0e2"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e0f754d27fddcfd74006455b6e04e6705d6c31a612ec69ddc040a5468e44b4e"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d57c3fd55d9058645d26ae37d76e61156a27722097229d32a9e73ed54819982a"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71f14375d6f73b62800530b581aed3ada394039877818b2d5f7fc77e3bb6894d"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:441cc2f8869a4f0f4bb408475e5ae0ee1f3b55b33f350406150277f7f35384fc"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:65bee1e49fa6f9cf327ce0e01c4c10f39165ee76d35c846ade7cb0ec6683e303"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af335bac6b666cc6aea16f11d486c3b794029d9df029967f9938a4bed59b6a19"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cfec476887aa231b8548ece2e06d28edc87c1397ebd83922299af2e051cf2827"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65c07febd1936d63bfde78948b76cd4c2a411572a44ac50719ead41947d0f26b"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-win32.whl", hash = "sha256:4dfb4be774c4436a4526d0c554af0cc2e02082c38303852a36f6456ece7b3503"},
- {file = "psycopg2_binary-2.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:02c6e3cf3439e213e4ee930308dc122d6fb4d4bea9aef4a12535fbd605d1a2fe"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9182eb20f41417ea1dd8e8f7888c4d7c6e805f8a7c98c1081778a3da2bee3e4"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a6979cf527e2603d349a91060f428bcb135aea2be3201dff794813256c274f1"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8338a271cb71d8da40b023a35d9c1e919eba6cbd8fa20a54b748a332c355d896"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ed340d2b858d6e6fb5083f87c09996506af483227735de6964a6100b4e6a54"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f81e65376e52f03422e1fb475c9514185669943798ed019ac50410fb4c4df232"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb13af3c5dd3a9588000910178de17010ebcccd37b4f9794b00595e3a8ddad3"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4c727b597c6444a16e9119386b59388f8a424223302d0c06c676ec8b4bc1f963"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d67fbdaf177da06374473ef6f7ed8cc0a9dc640b01abfe9e8a2ccb1b1402c1f"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0892ef645c2fabb0c75ec32d79f4252542d0caec1d5d949630e7d242ca4681a3"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:02c0f3757a4300cf379eb49f543fb7ac527fb00144d39246ee40e1df684ab514"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-win32.whl", hash = "sha256:c3dba7dab16709a33a847e5cd756767271697041fbe3fe97c215b1fc1f5c9848"},
- {file = "psycopg2_binary-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249"},
-]
-
-[[package]]
-name = "ptpython"
-version = "3.0.23"
-description = "Python REPL build on top of prompt_toolkit"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "ptpython-3.0.23-py2.py3-none-any.whl", hash = "sha256:51069503684169b21e1980734a9ba2e104643b7e6a50d3ca0e5669ea70d9e21c"},
- {file = "ptpython-3.0.23.tar.gz", hash = "sha256:9fc9bec2cc51bc4000c1224d8c56241ce8a406b3d49ec8dc266f78cd3cd04ba4"},
-]
-
-[package.dependencies]
-appdirs = "*"
-jedi = ">=0.16.0"
-prompt-toolkit = ">=3.0.28,<3.1.0"
-pygments = "*"
-
-[package.extras]
-all = ["black"]
-ptipython = ["ipython"]
-
-[[package]]
-name = "pure-sasl"
-version = "0.6.2"
-description = "Pure Python client SASL implementation"
-optional = false
-python-versions = "*"
-files = [
- {file = "pure-sasl-0.6.2.tar.gz", hash = "sha256:53c1355f5da95e2b85b2cc9a6af435518edc20c81193faa0eea65fdc835138f4"},
- {file = "pure_sasl-0.6.2-py2-none-any.whl", hash = "sha256:edb33b1a46eb3c602c0166de0442c0fb41f5ac2bfccbde4775183b105ad89ab2"},
-]
-
-[package.extras]
-gssapi = ["kerberos (>=1.3.0)"]
-
-[[package]]
-name = "pyasn1"
-version = "0.5.1"
-description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-files = [
- {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"},
- {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"},
-]
-
-[[package]]
-name = "pyasn1-modules"
-version = "0.3.0"
-description = "A collection of ASN.1-based protocols modules"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-files = [
- {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"},
- {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"},
-]
-
-[package.dependencies]
-pyasn1 = ">=0.4.6,<0.6.0"
-
-[[package]]
-name = "pyathena"
-version = "2.25.2"
-description = "Python DB API 2.0 (PEP 249) client for Amazon Athena"
-optional = false
-python-versions = ">=3.7.1,<4.0.0"
-files = [
- {file = "pyathena-2.25.2-py3-none-any.whl", hash = "sha256:df7855fec5cc675511431d7c72b814346ebd7e51ed32181ec95847154f79210b"},
- {file = "pyathena-2.25.2.tar.gz", hash = "sha256:aebb8254dd7b2a450841ee3552bf443002a2deaed93fae0ae6f4258b5eb2d367"},
-]
-
-[package.dependencies]
-boto3 = ">=1.26.4"
-botocore = ">=1.29.4"
-fsspec = "*"
-tenacity = ">=4.1.0"
-
-[package.extras]
-arrow = ["pyarrow (>=7.0.0)"]
-fastparquet = ["fastparquet (>=0.4.0)"]
-pandas = ["pandas (>=1.3.0)"]
-sqlalchemy = ["sqlalchemy (>=1.0.0,<2.0.0)"]
-
-[[package]]
-name = "pycparser"
-version = "2.21"
-description = "C parser in Python"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
- {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
-]
-
-[[package]]
-name = "pycryptodome"
-version = "3.20.0"
-description = "Cryptographic library for Python"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:417a276aaa9cb3be91f9014e9d18d10e840a7a9b9a9be64a42f553c5b50b4d1d"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a1250b7ea809f752b68e3e6f3fd946b5939a52eaeea18c73bdab53e9ba3c2dd"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:d5954acfe9e00bc83ed9f5cb082ed22c592fbbef86dc48b907238be64ead5c33"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-win32.whl", hash = "sha256:06d6de87c19f967f03b4cf9b34e538ef46e99a337e9a61a77dbe44b2cbcf0690"},
- {file = "pycryptodome-3.20.0-cp27-cp27m-win_amd64.whl", hash = "sha256:ec0bb1188c1d13426039af8ffcb4dbe3aad1d7680c35a62d8eaf2a529b5d3d4f"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:5601c934c498cd267640b57569e73793cb9a83506f7c73a8ec57a516f5b0b091"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d29daa681517f4bc318cd8a23af87e1f2a7bad2fe361e8aa29c77d652a065de4"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3427d9e5310af6680678f4cce149f54e0bb4af60101c7f2c16fdf878b39ccccc"},
- {file = "pycryptodome-3.20.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:3cd3ef3aee1079ae44afaeee13393cf68b1058f70576b11439483e34f93cf818"},
- {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac1c7c0624a862f2e53438a15c9259d1655325fc2ec4392e66dc46cdae24d044"},
- {file = "pycryptodome-3.20.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76658f0d942051d12a9bd08ca1b6b34fd762a8ee4240984f7c06ddfb55eaf15a"},
- {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f35d6cee81fa145333137009d9c8ba90951d7d77b67c79cbe5f03c7eb74d8fe2"},
- {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76cb39afede7055127e35a444c1c041d2e8d2f1f9c121ecef573757ba4cd2c3c"},
- {file = "pycryptodome-3.20.0-cp35-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49a4c4dc60b78ec41d2afa392491d788c2e06edf48580fbfb0dd0f828af49d25"},
- {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fb3b87461fa35afa19c971b0a2b7456a7b1db7b4eba9a8424666104925b78128"},
- {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_i686.whl", hash = "sha256:acc2614e2e5346a4a4eab6e199203034924313626f9620b7b4b38e9ad74b7e0c"},
- {file = "pycryptodome-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:210ba1b647837bfc42dd5a813cdecb5b86193ae11a3f5d972b9a0ae2c7e9e4b4"},
- {file = "pycryptodome-3.20.0-cp35-abi3-win32.whl", hash = "sha256:8d6b98d0d83d21fb757a182d52940d028564efe8147baa9ce0f38d057104ae72"},
- {file = "pycryptodome-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:9b3ae153c89a480a0ec402e23db8d8d84a3833b65fa4b15b81b83be9d637aab9"},
- {file = "pycryptodome-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4401564ebf37dfde45d096974c7a159b52eeabd9969135f0426907db367a652a"},
- {file = "pycryptodome-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:ec1f93feb3bb93380ab0ebf8b859e8e5678c0f010d2d78367cf6bc30bfeb148e"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:acae12b9ede49f38eb0ef76fdec2df2e94aad85ae46ec85be3648a57f0a7db04"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f47888542a0633baff535a04726948e876bf1ed880fddb7c10a736fa99146ab3"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e0e4a987d38cfc2e71b4a1b591bae4891eeabe5fa0f56154f576e26287bfdea"},
- {file = "pycryptodome-3.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c18b381553638414b38705f07d1ef0a7cf301bc78a5f9bc17a957eb19446834b"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a60fedd2b37b4cb11ccb5d0399efe26db9e0dd149016c1cc6c8161974ceac2d6"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:405002eafad114a2f9a930f5db65feef7b53c4784495dd8758069b89baf68eab"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab6ab0cb755154ad14e507d1df72de9897e99fd2d4922851a276ccc14f4f1a5"},
- {file = "pycryptodome-3.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acf6e43fa75aca2d33e93409f2dafe386fe051818ee79ee8a3e21de9caa2ac9e"},
- {file = "pycryptodome-3.20.0.tar.gz", hash = "sha256:09609209ed7de61c2b560cc5c8c4fbf892f8b15b1faf7e4cbffac97db1fffda7"},
-]
-
-[[package]]
-name = "pydgraph"
-version = "2.0.2"
-description = "Official Dgraph client implementation for Python"
-optional = false
-python-versions = "*"
-files = [
- {file = "pydgraph-2.0.2-py2.py3-none-any.whl", hash = "sha256:b4c91156aef5ef0899bee52224dab7ad8cf8ae63a82d16011d372f18f91090f7"},
- {file = "pydgraph-2.0.2.tar.gz", hash = "sha256:38bfa37b785bf5bcf53f58a02ce8417d40c83723c6b2cdbf428437fbc517ce61"},
-]
-
-[package.dependencies]
-grpcio = ">=1.18.0"
-protobuf = ">=3.6.1"
-
-[[package]]
-name = "pydruid"
-version = "0.5.7"
-description = "A Python connector for Druid."
-optional = false
-python-versions = "*"
-files = [
- {file = "pydruid-0.5.7.tar.gz", hash = "sha256:88d4fa608c96969dc8f8e03cc0cfc4a38c4c3039b4f899db0af896ab0fa6ff23"},
-]
-
-[package.dependencies]
-requests = "*"
-six = ">=1.9.0"
-
-[package.extras]
-async = ["tornado"]
-cli = ["prompt_toolkit (<2.0.0)", "pygments", "tabulate"]
-pandas = ["pandas"]
-sqlalchemy = ["sqlalchemy"]
-
-[[package]]
-name = "pyexasol"
-version = "0.12.0"
-description = "Exasol python driver with extra features"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "pyexasol-0.12.0-py3-none-any.whl", hash = "sha256:5b5d2907a570c321a89c2e5bd3c9bcea46b46cea76abf6adc4d92f4929f63fd4"},
- {file = "pyexasol-0.12.0.tar.gz", hash = "sha256:e15f4eccf7bdef7a4063a8280e625d8913cb2f6e4971088c67f3a33fadbffdd9"},
-]
-
-[package.dependencies]
-rsa = "*"
-websocket-client = ">=0.47.0"
-
-[package.extras]
-encrypt = ["pyopenssl (>=17.5.0)"]
-examples = ["pproxy", "psutil"]
-pandas = ["pandas (>=0.22,!=0.23.1)"]
-rapidjson = ["python-rapidjson"]
-ujson = ["ujson"]
-
-[[package]]
-name = "pygments"
-version = "2.17.2"
-description = "Pygments is a syntax highlighting package written in Python."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
- {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
-]
-
-[package.extras]
-plugins = ["importlib-metadata"]
-windows-terminal = ["colorama (>=0.4.6)"]
-
-[[package]]
-name = "pyhive"
-version = "0.6.1"
-description = "Python interface to Hive"
-optional = false
-python-versions = "*"
-files = [
- {file = "PyHive-0.6.1.tar.gz", hash = "sha256:a5f2b2f8bcd85a8cd80ab64ff8fbfe1c09515d266650a56f789a8d89ad66d7f4"},
-]
-
-[package.dependencies]
-future = "*"
-python-dateutil = "*"
-
-[package.extras]
-hive = ["sasl (>=0.2.1)", "thrift (>=0.10.0)", "thrift_sasl (>=0.1.0)"]
-presto = ["requests (>=1.0.0)"]
-sqlalchemy = ["sqlalchemy (>=0.8.7)"]
-
-[[package]]
-name = "pyignite"
-version = "0.6.1"
-description = "Apache Ignite binary client Python API"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pyignite-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0da286f6c01a5ecd5ea7d8d5d1d3886af1ae393c003b066ca06f5c274ddac428"},
- {file = "pyignite-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d5b8e2f3c222d1fb50be6de62860b7eeb117378666e67db7cd358c5b14918e"},
- {file = "pyignite-0.6.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c63ec63e812274cd7a0fe611b0eac4e41da9ba829cb47a3b1dc28a66f1dd9a77"},
- {file = "pyignite-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5185f346b4c5561eda9df15b7365d1b3b08e5ffb11e8f306259aff71c8516175"},
- {file = "pyignite-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:03f4701474f5ade8983d21c70999b8ebb021c6f5a7b0fb70a7f1339e52d3bf31"},
- {file = "pyignite-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e50401db905556d46ad1029fa9d6752897c29fe7be0bf671aa8edcfb176f8453"},
- {file = "pyignite-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2212d7e00cfd3a307696bc3423af1d906963da54fcece24a7913a0ae5ea34ea3"},
- {file = "pyignite-0.6.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d9511943dd652a6bd602c923a896a024491d37d5f69ea718c595b3cd3b01236c"},
- {file = "pyignite-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:50f213a3dca8dfbb6534d8e421ea1ac6d65b782cefb6d3d55c750b352be3779b"},
- {file = "pyignite-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5aaa1aa32dfb6305d39153f930a4fd952b43347366a73e6643b699b8bdf73d48"},
- {file = "pyignite-0.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:699261fb4f68a3d470370cb2ac507086aae8a84305bb440f38c7508f4a4ef288"},
- {file = "pyignite-0.6.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9c24ed39b85a492993fbf33238bcea5cf390020bf3d484b55fb78fb78bf9c10"},
- {file = "pyignite-0.6.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ccb29dfd55510f1cba174f0bdd205470a794b79beb0ad2a6655a8a197a667365"},
- {file = "pyignite-0.6.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:842aabb7fc3e32e81ae7f2b0a1406688748d209b3b22b2df9055be5812feb3da"},
- {file = "pyignite-0.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:48ebcc470c464de155a08495ede09338c5479a2e25e1a92ea3008d7dc4d8bcd7"},
- {file = "pyignite-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4fd49d3c849cb4159916a6c050bd6e083b58841b8a8cc6eab5b94ec0fb7463f6"},
- {file = "pyignite-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fbd6a919833fcf83e97bd9f79de1771f7249778daf1585b7e159d2506a7534d"},
- {file = "pyignite-0.6.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:39ad2c07bb5ff1983b7e78edd32d4e9975d6de4a3966b171ea1d78d39a04b238"},
- {file = "pyignite-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:da7da4486aea27c5e4927d224a31acb1bc058d2397a60a9f3ed9537755dc75da"},
- {file = "pyignite-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:dee6efd47860bf21042a8bdf5ad3c6bdf9a55beedfc9431cacb57bda37045a93"},
- {file = "pyignite-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6bf352c5a8547350d618a8e66059675437249d2ed633655273e9c34d2421aa"},
- {file = "pyignite-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f60ce18bb64d1031e3366a454a9f344c3ffe95ebc0d86251c6981a23479cbdb"},
- {file = "pyignite-0.6.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf717838df0b8c9b86664f761d0cb43dab26f037e6aae404054deaa9ad60590b"},
- {file = "pyignite-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:73f145c70b99e47f38b94fc89109261e0a5ad284db46531955a4610bf9706d9f"},
- {file = "pyignite-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:6c327eb1261c26b41c64ff36d290ca5c3aa9dc35b4ffbbb39ae57935fe1914f1"},
- {file = "pyignite-0.6.1.zip", hash = "sha256:e143d7a907e2662da11f63d2e9e34234dc722cd63c9aeb74486280e3aef3369d"},
-]
-
-[package.dependencies]
-attrs = ">=20.3.0"
-
-[[package]]
-name = "pyjwt"
-version = "2.4.0"
-description = "JSON Web Token implementation in Python"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "PyJWT-2.4.0-py3-none-any.whl", hash = "sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf"},
- {file = "PyJWT-2.4.0.tar.gz", hash = "sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba"},
-]
-
-[package.extras]
-crypto = ["cryptography (>=3.3.1)"]
-dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.3.1)", "mypy", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx", "sphinx-rtd-theme", "zope.interface"]
-docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
-tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
-
-[[package]]
-name = "pymongo"
-version = "4.6.3"
-description = "Python driver for MongoDB "
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pymongo-4.6.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e344d0afdd7c06c1f1e66a4736593293f432defc2191e6b411fc9c82fa8c5adc"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux1_i686.whl", hash = "sha256:731a92dfc4022db763bfa835c6bd160f2d2cba6ada75749c2ed500e13983414b"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:c4726e36a2f7e92f09f5b8e92ba4db7525daffe31a0dcbcf0533edc0ade8c7d8"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_i686.whl", hash = "sha256:00e6cfce111883ca63a3c12878286e0b89871f4b840290e61fb6f88ee0e687be"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_ppc64le.whl", hash = "sha256:cc7a26edf79015c58eea46feb5b262cece55bc1d4929a8a9e0cbe7e6d6a9b0eb"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_s390x.whl", hash = "sha256:4955be64d943b30f2a7ff98d818ca530f7cb37450bc6b32c37e0e74821907ef8"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:af039afc6d787502c02089759778b550cb2f25dbe2780f5b050a2e37031c3fbf"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc15a7c7a99aed7d0831eaf78a607f1db0c7a255f96e3d18984231acd72f70c"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e97c138d811e9367723fcd07c4402a9211caae20479fdd6301d57762778a69f"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ebcc145c74d06296ce0cad35992185064e5cb2aadef719586778c144f0cd4d37"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:664c64b6bdb31aceb80f0556951e5e2bf50d359270732268b4e7af00a1cf5d6c"},
- {file = "pymongo-4.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4056bc421d4df2c61db4e584415f2b0f1eebb92cbf9222f7f38303467c37117"},
- {file = "pymongo-4.6.3-cp310-cp310-win32.whl", hash = "sha256:cdbea2aac1a4caa66ee912af3601557d2bda2f9f69feec83601c78c7e53ece64"},
- {file = "pymongo-4.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:6cec7279e5a1b74b257d0270a8c97943d745811066630a6bc6beb413c68c6a33"},
- {file = "pymongo-4.6.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:138b9fa18d40401c217bc038a48bcde4160b02d36d8632015b1804971a2eaa2f"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60931b0e07448afe8866ffff764cd5bf4b1a855dc84c7dcb3974c6aa6a377a59"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9b35f8bded43ff91475305445fedf0613f880ff7e25c75ae1028e1260a9b7a86"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:872bad5c83f7eec9da11e1fef5f858c6a4c79fe4a83c7780e7b0fe95d560ae3f"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2ad3e5bfcd345c0bfe9af69a82d720860b5b043c1657ffb513c18a0dee19c19"},
- {file = "pymongo-4.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e208f2ab7b495eff8fd175022abfb0abce6307ac5aee3f4de51fc1a459b71c9"},
- {file = "pymongo-4.6.3-cp311-cp311-win32.whl", hash = "sha256:4670edbb5ddd71a4d555668ef99b032a5f81b59e4145d66123aa0d831eac7883"},
- {file = "pymongo-4.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:1c2761302b6cbfd12e239ce1b8061d4cf424a361d199dcb32da534985cae9350"},
- {file = "pymongo-4.6.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:722f2b709b63311c0efda4fa4c603661faa4bec6bad24a6cc41a3bc6d841bf09"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994386a4d6ad39e18bcede6dc8d1d693ec3ed897b88f86b1841fbc37227406da"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:391aea047bba928006114282f175bc8d09c53fe1b7d8920bf888325e229302fe"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4330c022024e7994b630199cdae909123e4b0e9cf15335de71b146c0f6a2435"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01277a7e183c59081368e4efbde2b8f577014431b257959ca98d3a4e8682dd51"},
- {file = "pymongo-4.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d30d5d7963453b478016bf7b0d87d7089ca24d93dbdecfbc9aa32f1b4772160a"},
- {file = "pymongo-4.6.3-cp312-cp312-win32.whl", hash = "sha256:a023804a3ac0f85d4510265b60978522368b5815772262e61e3a2222a8b315c9"},
- {file = "pymongo-4.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2a6ae9a600bbc2dbff719c98bf5da584fb8a4f2bb23729a09be2e9c3dbc61c8a"},
- {file = "pymongo-4.6.3-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:3b909e5b1864de01510079b39bbdc480720c37747be5552b354bc73f02c24a3c"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:48c60bd32ec141c0d45d8471179430003d9fb4490da181b8165fb1dce9cc255c"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:36d7049fc183fe4edda3eae7f66ea14c660921429e082fe90b4b7f4dc6664a70"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:18e5c161b18660f1c9d1f78236de45520a436be65e42b7bb51f25f74ad22bdde"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:e458e6fc2b7dd40d15cda04898bd2d8c9ff7ae086c516bc261628d54eb4e3158"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux2014_ppc64le.whl", hash = "sha256:e420e74c6db4594a6d09f39b58c0772679006cb0b4fc40901ba608794d87dad2"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:9c9340c7161e112e36ebb97fbba1cdbe7db3dfacb694d2918b1f155a01f3d859"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:26d036e0f5de09d0b21d0fc30314fcf2ae6359e4d43ae109aa6cf27b4ce02d30"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7cf28d9c90e40d4e385b858e4095739829f466f23e08674085161d86bb4bb10"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9066dff9dc0a182478ca5885d0b8a2b820b462e19459ada109df7a3ced31b272"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1e1586ebdebe0447a24842480defac17c496430a218486c96e2da3f164c0f05"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3853fb66bf34ce1b6e573e1bbb3cb28763be9d1f57758535757faf1ab2f24a"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:462684a6f5ce6f2661c30eab4d1d459231e0eed280f338e716e31a24fc09ccb3"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0a4ea44e5a913bdb7c9abd34c69e9fcfac10dfaf49765463e0dc1ea922dd2a9d"},
- {file = "pymongo-4.6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:098d420a8214ad25f872de7e8b309441995d12ece0376218a04d9ed5d2222cf3"},
- {file = "pymongo-4.6.3-cp37-cp37m-win32.whl", hash = "sha256:7330245253fbe2e09845069d2f4d35dd27f63e377034c94cb0ddac18bc8b0d82"},
- {file = "pymongo-4.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:151361c101600a85cb1c1e0db4e4b28318b521fcafa9b62d389f7342faaaee80"},
- {file = "pymongo-4.6.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:4d167d546352869125dc86f6fda6dffc627d8a9c8963eaee665825f2520d542b"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:eaf3d594ebfd5e1f3503d81e06a5d78e33cda27418b36c2491c3d4ad4fca5972"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7ee79e02a7c5ed34706ecb5dad19e6c7d267cf86d28c075ef3127c58f3081279"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af5c5112db04cf62a5d9d224a24f289aaecb47d152c08a457cca81cee061d5bd"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:6b5aec78aa4840e8d6c3881900259892ab5733a366696ca10d99d68c3d73eaaf"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_ppc64le.whl", hash = "sha256:9757602fb45c8ecc1883fe6db7c59c19d87eb3c645ec9342d28a6026837da931"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:dde9fb6e105ce054339256a8b7a9775212ebb29596ef4e402d7bbc63b354d202"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:7df8b166d3db6cfead4cf55b481408d8f0935d8bd8d6dbf64507c49ef82c7200"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53451190b8628e1ce7d1fe105dc376c3f10705127bd3b51fe3e107b9ff1851e6"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75107a386d4ccf5291e75cce8ca3898430e7907f4cc1208a17c9efad33a1ea84"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4a0660ce32d8459b7f12dc3ca0141528fead62d3cce31b548f96f30902074cc0"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa310096450e9c461b7dfd66cbc1c41771fe36c06200440bb3e062b1d4a06b6e"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f465cca9b178e7bb782f952dd58e9e92f8ba056e585959465f2bb50feddef5f"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c67c19f653053ef2ebd7f1837c2978400058d6d7f66ec5760373a21eaf660158"},
- {file = "pymongo-4.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c701de8e483fb5e53874aab642235361aac6de698146b02c644389eaa8c137b6"},
- {file = "pymongo-4.6.3-cp38-cp38-win32.whl", hash = "sha256:90525454546536544307e6da9c81f331a71a1b144e2d038fec587cc9f9250285"},
- {file = "pymongo-4.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:3e1ba5a037c526a3f4060c28f8d45d71ed9626e2bf954b0cd9a8dcc3b45172ee"},
- {file = "pymongo-4.6.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:14a82593528cddc93cfea5ee78fac95ae763a3a4e124ca79ee0b24fbbc6da1c9"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cd6c15242d9306ff1748681c3235284cbe9f807aeaa86cd17d85e72af626e9a7"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:6de33f1b2eed91b802ec7abeb92ffb981d052f3604b45588309aae9e0f6e3c02"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0182899aafe830f25cf96c5976d724efeaaf7b6646c15424ad8dd25422b2efe1"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:8d0ea740a2faa56f930dc82c5976d96c017ece26b29a1cddafb58721c7aab960"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_ppc64le.whl", hash = "sha256:5c8a4982f5eb767c6fbfb8fb378683d09bcab7c3251ba64357eef600d43f6c23"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:becfa816545a48c8e740ac2fd624c1c121e1362072d68ffcf37a6b1be8ea187e"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:ff7d1f449fcad23d9bc8e8dc2b9972be38bcd76d99ea5f7d29b2efa929c2a7ff"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e097f877de4d6af13a33ef938bf2a2350f424be5deabf8b857da95f5b080487a"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:705a9bfd619301ee7e985d6f91f68b15dfcb2f6f36b8cc225cc82d4260d2bce5"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2ef1b4992ee1cb8bb16745e70afa0c02c5360220a7a8bb4775888721f052d0a6"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d10bdd46cbc35a2109737d36ffbef32e7420569a87904738ad444ccb7ac2c5"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:17c1c143ba77d6e21fc8b48e93f0a5ed982a23447434e9ee4fbb6d633402506b"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e51e30d67b468a2a634ade928b30cb3e420127f148a9aec60de33f39087bdc4"},
- {file = "pymongo-4.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:bec8e4e88984be157408f1923d25869e1b575c07711cdbdde596f66931800934"},
- {file = "pymongo-4.6.3-cp39-cp39-win32.whl", hash = "sha256:98877a9c4ad42df8253a12d8d17a3265781d1feb5c91c767bd153f88feb0b670"},
- {file = "pymongo-4.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:6d5b35da9e16cda630baed790ffc3d0d01029d269523a7cec34d2ec7e6823e75"},
- {file = "pymongo-4.6.3.tar.gz", hash = "sha256:400074090b9a631f120b42c61b222fd743490c133a5d2f99c0208cefcccc964e"},
-]
-
-[package.dependencies]
-dnspython = ">=1.16.0,<3.0.0"
-
-[package.extras]
-aws = ["pymongo-auth-aws (<2.0.0)"]
-encryption = ["certifi", "pymongo[aws]", "pymongocrypt (>=1.6.0,<2.0.0)"]
-gssapi = ["pykerberos", "winkerberos (>=0.5.0)"]
-ocsp = ["certifi", "cryptography (>=2.5)", "pyopenssl (>=17.2.0)", "requests (<3.0.0)", "service-identity (>=18.1.0)"]
-snappy = ["python-snappy"]
-test = ["pytest (>=7)"]
-zstd = ["zstandard"]
-
-[[package]]
-name = "pymssql"
-version = "2.3.1"
-description = "DB-API interface to Microsoft SQL Server for Python. (new Cython-based version)"
-optional = false
-python-versions = "*"
-files = [
- {file = "pymssql-2.3.1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:001b3321a5f620b80d1427933fcca11b05f29a808d7772a84d18d01e640ee60a"},
- {file = "pymssql-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15466dd41be5e32302f0c4791f612aadd608a0e6ec0b10d769e76cbb4c86aa97"},
- {file = "pymssql-2.3.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74349040d4ff6f05894aefb5109ecffcd416e1e366d9951085d3225a9d09c46b"},
- {file = "pymssql-2.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc79dbe5eca8825b73830c8bb147b6f588300dc7510393822682162dc4ff003f"},
- {file = "pymssql-2.3.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:0b93ebe2feb45e772ca708bc4cd70f3e4c72796ec1b157fd5d80cdc589c786aa"},
- {file = "pymssql-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:44b1c8752c0fc6750902c1c521f258bdf4271bfbf7b2a5fee469b6ad00631aab"},
- {file = "pymssql-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fdfadb055a9ecad58356decfecc41626999ad7b548cc7ea898cf159e2217f7bb"},
- {file = "pymssql-2.3.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:46f1074c6763e9a899128f22a0f72e9fb0035535f48efabd6a294db1c149e6f1"},
- {file = "pymssql-2.3.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ebb11b61d99ec5bbe0b8c411ff748a90263cdaf474881de231da8184e721c42c"},
- {file = "pymssql-2.3.1-cp310-cp310-win32.whl", hash = "sha256:2ef07fdee3e9652d39b4c081c5c5e1a1031abd122b402ed66813bceb3874ccea"},
- {file = "pymssql-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:791522339215cb7f88db54c831a2347e0c4d69dd3092a343eea5b9339adf4412"},
- {file = "pymssql-2.3.1-cp311-cp311-macosx_13_0_universal2.whl", hash = "sha256:0433ffa1c86290a93e81176f377621cb70405be66ade8f3070d3f5ec9cfebdba"},
- {file = "pymssql-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6182d82ebfbe46f0e7748d068c6a1c16c0f4fe1f34f1c390f63375cee79b44b0"},
- {file = "pymssql-2.3.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfbe07dcf0aaee8ce630624669cb2fb77b76743d4dd925f99331422be8704de3"},
- {file = "pymssql-2.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d999c8e5d5d48e9305c4132392825de402f13feea15694e4e7103029b6eae06"},
- {file = "pymssql-2.3.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2dced0a76d8e99c283103a2e3c825ca22c67f1f8fc5cff657510f4d2ffb9d188"},
- {file = "pymssql-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:880d3173025dea3babf5ab862875b3c76a5cf8df5b292418050c7793c651c0b2"},
- {file = "pymssql-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9f89c698e29ce5c576e4980ded89c00b45e482ec02759bfbfc1aa326648cf64a"},
- {file = "pymssql-2.3.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3f4f2a38ce6e39ed2414c20ca16deaea4340868033a4bb23d5e4e30c72290caf"},
- {file = "pymssql-2.3.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e34e8aa1d3da555dbf23141b02f401267c0be32104b4f030afd0bae62d26d735"},
- {file = "pymssql-2.3.1-cp311-cp311-win32.whl", hash = "sha256:72e57e20802bf97399e050a0760a4541996fc27bc605a1a25e48ca6fe4913c48"},
- {file = "pymssql-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:b5d3604bca2fa8d5ba2eed1582a3c8a83970a8d2edabfcfd87c1edecb7617d16"},
- {file = "pymssql-2.3.1-cp312-cp312-macosx_13_0_universal2.whl", hash = "sha256:c28f1b9560b82fe1a1e51d8c56f6d36bca7c507a8cdf2caa2a0642503c220d5c"},
- {file = "pymssql-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3509b75747eb22ae89f3d47ae316a4b9eac7d952269e88b356ef117a1b8e3b8"},
- {file = "pymssql-2.3.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cca3bed27e1ab867e482fa8b529d408489ad57e8b60452f75ef288da90573db6"},
- {file = "pymssql-2.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4fe3276915e6040daec409203e3143aa2826984adb8d223c155dab91010110a4"},
- {file = "pymssql-2.3.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d36d566d0d6997c95442c3d2902800e6b072ccc017c6284e5b1bd4e17dc8fada"},
- {file = "pymssql-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3564df40a678623a769acd9677dc68228b2694170132c6f296eb62bf766d31e4"},
- {file = "pymssql-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3dbd4106faabf97f028d0ac59b30d132cfb5e48cf5314b0476f293123dbf3422"},
- {file = "pymssql-2.3.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:acd1690d9b1b2ece9d0e1fd7d68571fc9fa56b6ba8697a3132446419ff7fb3f4"},
- {file = "pymssql-2.3.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:126e0b78773975136e6385da7286c277e2e0320c1f4bee0e4dc61a5edcf98c41"},
- {file = "pymssql-2.3.1-cp312-cp312-win32.whl", hash = "sha256:21803b731b8c8780fc974d9b4931fa8f1ca29c227502a4c317e12773c8bdef43"},
- {file = "pymssql-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:6b0224fc5ce4cf0703278859f145e3e921c04d9feb59739a104d3020bbf0c0c1"},
- {file = "pymssql-2.3.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:709c1df3134e330ee9590437253be363b558154bde5bb54856fc5fe68a03c971"},
- {file = "pymssql-2.3.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9381eafaf529815f2d61f22b99e0538e744b31234f17d4384f5b0496bd1fbed"},
- {file = "pymssql-2.3.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3bf78789014f202855f5d00de982bbcd95177fe8bcf920f0ce730b72456c173"},
- {file = "pymssql-2.3.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4b44280eedd0a3f031e9464d4fc632a215fadcfb375bb479065b61a6337df402"},
- {file = "pymssql-2.3.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:922f536b925880c260968c8f2130b1c9d6315b83f300f18365b5421933f034a2"},
- {file = "pymssql-2.3.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f00f618d1c0f58617de548e5094f7d55ab6034b94068d7eebba60a034866b10b"},
- {file = "pymssql-2.3.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b363db86a1a3fe16df9b4253e17b02a268d0f2e2753679b8e85cee268e2fe8c4"},
- {file = "pymssql-2.3.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:396a26cf576196cc4a3d77890b2b8eb62655ff02846288757dd8b587352cc4f5"},
- {file = "pymssql-2.3.1-cp36-cp36m-win32.whl", hash = "sha256:5a1a1c697596f23058697709144d00a44e7af6ecab6a517f2ecf28dcf8fb4280"},
- {file = "pymssql-2.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:4f92e8657d42341dce01f7f57d03f84b35c0ed00a7bef24533ff80a37ffcfb4e"},
- {file = "pymssql-2.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:095b50e43bfbc4d6f953810175ba275bb3e6136206f3a7146bdd1031e3f0dd9b"},
- {file = "pymssql-2.3.1-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47ac89098732c327725b53464932c6a532367271a3d5c5a988f61e23e0e0e286"},
- {file = "pymssql-2.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2f284fc052cf1dbc702a2f4d13442d87fc6847ba9054faccfc8d8446fcf00894"},
- {file = "pymssql-2.3.1-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:40778b65c09eef9e7c25c444b96e76f81d8b5cf1828cb555123d052b7d3b5661"},
- {file = "pymssql-2.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:22c8609bc7f8b13d383729ba09042b4d796a607c93779c616be51b37caa6b384"},
- {file = "pymssql-2.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ab2aea2ae8bc1aba0105fccbf9e4f6716648b2b8f9421fd3418c6cc798fca43e"},
- {file = "pymssql-2.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e594de69832ad13761412f4d5c981a6e5d931b22f25136c8cd3531d9c6cfdf63"},
- {file = "pymssql-2.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:68f879b4ec4b2191a1d8b3bb24db04c3631737653785369c275bd5a574e54093"},
- {file = "pymssql-2.3.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:9ef157e63a1c19e7ab4823237b5f03a3bca45e1e94a4d5ed73baab6d019830c7"},
- {file = "pymssql-2.3.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:66afe6ee539e37cdfea0c6b2d596ec0d2a6223f09450c4df7cf872bad12691fe"},
- {file = "pymssql-2.3.1-cp37-cp37m-win32.whl", hash = "sha256:b9cc14a9f63e632200f54311da9868ece2715fa9560f6272c9bb82c57edc0543"},
- {file = "pymssql-2.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54bc10f28c0acc1347d3c7056e702ad21f128e6bf7737b4edc8c267372db9ce8"},
- {file = "pymssql-2.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8d955e751fb125be2a8513b5a338457a3fe73e5daa094815f96a86e496f7149"},
- {file = "pymssql-2.3.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c13ca6eaf0d7f16af9edf87d58070329bfacb7f27b90e1de16318d64c7b873b"},
- {file = "pymssql-2.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ecb0cdea24e2c019fb403fd642c04a64e8767c79f8dd38451eb5d72ceffce34"},
- {file = "pymssql-2.3.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:afd57a728e81d73a0f43f3d28216c402fea03bd06a382da881dfc8215fb4080d"},
- {file = "pymssql-2.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5e6f6d9de73309cda602bbb769cb707f08d6899664f3ac6e9ed3e3b1ad472cee"},
- {file = "pymssql-2.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:02b808dbb86bbe751dd3fd117e83926b0a19ca9d9b833fae945bf2e31be66bf6"},
- {file = "pymssql-2.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b0f1ba9befe23e6c4e75c2a626ffe59d159ab3a425a0208515888ec8670bf5bf"},
- {file = "pymssql-2.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8eecb4f3b41b8b29a0cbe502ae37b6477063d690151f668c410328f101f6198b"},
- {file = "pymssql-2.3.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:a36c8b089e2d7b606aee823eefdfd72f5df110241fc5d913094b0b9da2692794"},
- {file = "pymssql-2.3.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:425de7d3f38cd1867c30b7c352d66020f38fdcdf804282ee232f5e25672930c1"},
- {file = "pymssql-2.3.1-cp38-cp38-win32.whl", hash = "sha256:ce397eb6a2a90fcd2a83d8812c1b8752af3b5362e630da49aa556c947e32ce3d"},
- {file = "pymssql-2.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:02c4ab7a58bfb57edb2deee7e2aceed2512960e7c2c1fd2cb23c647471a36ba2"},
- {file = "pymssql-2.3.1-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:750078568dafc1e0a24cf0f51eecfe548b13440976a2c8b19cc6e5d38e7b10bc"},
- {file = "pymssql-2.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a651dd98f67eef98f429c949fb50ea0a92fcf8668834cc35909237c24c1b906"},
- {file = "pymssql-2.3.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a1ecedaeec8f4d8643d088b4985f0b742d9669bff701153a845b0d1900260b81"},
- {file = "pymssql-2.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:015f6ccd1bcb53f22a3226653d0d8155da40f4afbc1fd0cec25de5fe8decf126"},
- {file = "pymssql-2.3.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:da44761ca2f996d88f90c0f972b583dfe9c389db84888bd8209cdb83508f7c7a"},
- {file = "pymssql-2.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9557b738475e06dfd53f97d8a2c2b259b9b9fd79bf1a4e084ae4e9f164be644d"},
- {file = "pymssql-2.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a1f3f2e2792364a50417f3c2dc0d8f125955c1b641f36eb313daf666045b9748"},
- {file = "pymssql-2.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:be8af4dea025f171ffb1e5b17cb0c9cbc92b0e3c32d0517bc678fff6f660e5fb"},
- {file = "pymssql-2.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a87950fb1a2b1c4028064fac971f3e191adebb58657ca985330f70e02f95223e"},
- {file = "pymssql-2.3.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9ea04bf8e13d567650631a944c88886c99a5622d9491e896a9b5a9ffbef2e352"},
- {file = "pymssql-2.3.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4d93a82f8ad7d3606354b81bbbe7e7832f70fd6e9ccb2e04a2975117da5df973"},
- {file = "pymssql-2.3.1-cp39-cp39-win32.whl", hash = "sha256:6a2657152d4007314b66f353a25fc2742155c2770083320b5255fc576103661e"},
- {file = "pymssql-2.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6c9ffb3ef110bf0fc2a41c845f231cf749162b1d71e02b0aceb6c0ebc603e2e9"},
- {file = "pymssql-2.3.1.tar.gz", hash = "sha256:ddee15c4c193e14c92fe2cd720ca9be1dba1e0f4178240380b8f5f6f00da04c6"},
-]
-
-[[package]]
-name = "pynacl"
-version = "1.5.0"
-description = "Python binding to the Networking and Cryptography (NaCl) library"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "PyNaCl-1.5.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:401002a4aaa07c9414132aaed7f6836ff98f59277a234704ff66878c2ee4a0d1"},
- {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cb72a79269189d4e0dc537556f4740f7f0a9ec41c1322598799b0bdad4ef92"},
- {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a36d4a9dda1f19ce6e03c9a784a2921a4b726b02e1c736600ca9c22029474394"},
- {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d"},
- {file = "PyNaCl-1.5.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858"},
- {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:a422368fc821589c228f4c49438a368831cb5bbc0eab5ebe1d7fac9dded6567b"},
- {file = "PyNaCl-1.5.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:61f642bf2378713e2c2e1de73444a3778e5f0a38be6fee0fe532fe30060282ff"},
- {file = "PyNaCl-1.5.0-cp36-abi3-win32.whl", hash = "sha256:e46dae94e34b085175f8abb3b0aaa7da40767865ac82c928eeb9e57e1ea8a543"},
- {file = "PyNaCl-1.5.0-cp36-abi3-win_amd64.whl", hash = "sha256:20f42270d27e1b6a29f54032090b972d97f0a1b0948cc52392041ef7831fee93"},
- {file = "PyNaCl-1.5.0.tar.gz", hash = "sha256:8ac7448f09ab85811607bdd21ec2464495ac8b7c66d146bf545b0f08fb9220ba"},
-]
-
-[package.dependencies]
-cffi = ">=1.4.1"
-
-[package.extras]
-docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"]
-tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"]
-
-[[package]]
-name = "pyodbc"
-version = "5.1.0"
-description = "DB API module for ODBC"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pyodbc-5.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:02fe9821711a2d14415eaeb4deab471d2c8b7034b107e524e414c0e133c42248"},
- {file = "pyodbc-5.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2cbdbd019756285dc44bc35238a3ed8dfaa454e8c8b2c3462f1710cfeebfb290"},
- {file = "pyodbc-5.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84df3bbce9bafe65abd25788d55c9f1da304f6115d70f25758ff8c85f3ce0517"},
- {file = "pyodbc-5.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:218bb75d4bc67075529a65ce8ec7daeed1d83c33dd7410450fbf68d43d184d28"},
- {file = "pyodbc-5.1.0-cp310-cp310-win32.whl", hash = "sha256:eae576b3b67d21d6f237e18bb5f3df8323a2258f52c3e3afeef79269704072a9"},
- {file = "pyodbc-5.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:c3b65343557f4c7753204e06f4c82c97ed212a636501f4bc27c5ce0e549eb3e8"},
- {file = "pyodbc-5.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aa6f46377da303bf79bcb4b559899507df4b2559f30dcfdf191358ee4b99f3ab"},
- {file = "pyodbc-5.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b19d7f44cfee89901e482f554a88177e83fae76b03c3f830e0023a195d840220"},
- {file = "pyodbc-5.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c36448322f8d6479d87c528cf52401a6ea4f509b9637750b67340382b4e1b40"},
- {file = "pyodbc-5.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c5e0cb79222aad4b31a3602e39b242683c29c6221a16ed43f45f18fd0b73659"},
- {file = "pyodbc-5.1.0-cp311-cp311-win32.whl", hash = "sha256:92caed9d445815ed3f7e5a1249e29a4600ebc1e99404df81b6ed7671074c9227"},
- {file = "pyodbc-5.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a1bd14633e91b7a9814f4fd944c9ebb89fb7f1fd4710c4e3999b5ef041536347"},
- {file = "pyodbc-5.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3d9cc4af703c4817b6e604315910b0cf5dcb68056d52b25ca072dd59c52dcbc"},
- {file = "pyodbc-5.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:406b8fa2133a7b6a713aa5187dba2d08cf763b5884606bed77610a7660fdfabe"},
- {file = "pyodbc-5.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8488c3818f12207650836c5c6f7352f9ff9f56a05a05512145995e497c0bbb1"},
- {file = "pyodbc-5.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0df69e3a500791b70b5748c68a79483b24428e4c16027b56aa0305e95c143a4"},
- {file = "pyodbc-5.1.0-cp312-cp312-win32.whl", hash = "sha256:aa4e02d3a9bf819394510b726b25f1566f8b3f0891ca400ad2d4c8b86b535b78"},
- {file = "pyodbc-5.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:33f4984af38872e7bdec78007a34e4d43ae72bf9d0bae3344e79d9d0db157c0e"},
- {file = "pyodbc-5.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29425e2d366e7f5828b76c7993f412a3db4f18bd5bcee00186c00b5a5965e205"},
- {file = "pyodbc-5.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2bbd2e75c77dee9f3cd100c3246110abaeb9af3f7fa304ccc2934ff9c6a4fa4"},
- {file = "pyodbc-5.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3602136a936bc0c1bb9722eb2fbf2042b3ff1ddccdc4688e514b82d4b831563b"},
- {file = "pyodbc-5.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bed1c843565d3a4fd8c332ebceaf33efe817657a0505eacb97dd1b786a985b0b"},
- {file = "pyodbc-5.1.0-cp38-cp38-win32.whl", hash = "sha256:735f6da3762e5856b5580be0ed96bb946948346ebd1e526d5169a5513626a67a"},
- {file = "pyodbc-5.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:c5bb4e43f6c72f5fa2c634570e0d761767d8ea49f39205229b812fb4d3fe05aa"},
- {file = "pyodbc-5.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:33f0f1d7764cefef6f787936bd6359670828a6086be67518ab951f1f7f503cda"},
- {file = "pyodbc-5.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be3b1c36c31ec7d73d0b34a8ad8743573763fadd8f2bceef1e84408252b48dce"},
- {file = "pyodbc-5.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e71a51c252b503b4d753e21ed31e640015fc0d00202d42ea42f2396fcc924b4a"},
- {file = "pyodbc-5.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af5282cc8b667af97d76f4955250619a53f25486cbb6b1f45a06b781006ffa0b"},
- {file = "pyodbc-5.1.0-cp39-cp39-win32.whl", hash = "sha256:96b2a8dc27693a517e3aad3944a7faa8be95d40d7ec1eda51a1885162eedfa33"},
- {file = "pyodbc-5.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:e738c5eedb4a0cbab20cc008882f49b106054499db56864057c2530ff208cf32"},
- {file = "pyodbc-5.1.0.tar.gz", hash = "sha256:397feee44561a6580be08cedbe986436859563f4bb378f48224655c8e987ea60"},
-]
-
-[[package]]
-name = "pyopenssl"
-version = "24.2.1"
-description = "Python wrapper module around the OpenSSL library"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pyOpenSSL-24.2.1-py3-none-any.whl", hash = "sha256:967d5719b12b243588573f39b0c677637145c7a1ffedcd495a487e58177fbb8d"},
- {file = "pyopenssl-24.2.1.tar.gz", hash = "sha256:4247f0dbe3748d560dcbb2ff3ea01af0f9a1a001ef5f7c4c647956ed8cbf0e95"},
-]
-
-[package.dependencies]
-cryptography = ">=41.0.5,<44"
-
-[package.extras]
-docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx-rtd-theme"]
-test = ["pretend", "pytest (>=3.0.1)", "pytest-rerunfailures"]
-
-[[package]]
-name = "pyparsing"
-version = "2.4.7"
-description = "Python parsing module"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
- {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
-]
-
-[[package]]
-name = "pypd"
-version = "1.1.0"
-description = "A python client for PagerDuty API"
-optional = false
-python-versions = "*"
-files = [
- {file = "pypd-1.1.0-py2-none-any.whl", hash = "sha256:a16b86f5061fb272c7050d097d07868822ad2b127dad656598f8486fb3678866"},
- {file = "pypd-1.1.0.tar.gz", hash = "sha256:e955f7bd2adb059e576308ef11e437bdd8d1ddca14b599a9250f6f78a6c70694"},
-]
-
-[package.dependencies]
-requests = "*"
-six = "*"
-
-[[package]]
-name = "pyrsistent"
-version = "0.20.0"
-description = "Persistent/Functional/Immutable data structures"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pyrsistent-0.20.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c3aba3e01235221e5b229a6c05f585f344734bd1ad42a8ac51493d74722bbce"},
- {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1beb78af5423b879edaf23c5591ff292cf7c33979734c99aa66d5914ead880f"},
- {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21cc459636983764e692b9eba7144cdd54fdec23ccdb1e8ba392a63666c60c34"},
- {file = "pyrsistent-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5ac696f02b3fc01a710427585c855f65cd9c640e14f52abe52020722bb4906b"},
- {file = "pyrsistent-0.20.0-cp310-cp310-win32.whl", hash = "sha256:0724c506cd8b63c69c7f883cc233aac948c1ea946ea95996ad8b1380c25e1d3f"},
- {file = "pyrsistent-0.20.0-cp310-cp310-win_amd64.whl", hash = "sha256:8441cf9616d642c475684d6cf2520dd24812e996ba9af15e606df5f6fd9d04a7"},
- {file = "pyrsistent-0.20.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f3b1bcaa1f0629c978b355a7c37acd58907390149b7311b5db1b37648eb6958"},
- {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cdd7ef1ea7a491ae70d826b6cc64868de09a1d5ff9ef8d574250d0940e275b8"},
- {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cae40a9e3ce178415040a0383f00e8d68b569e97f31928a3a8ad37e3fde6df6a"},
- {file = "pyrsistent-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6288b3fa6622ad8a91e6eb759cfc48ff3089e7c17fb1d4c59a919769314af224"},
- {file = "pyrsistent-0.20.0-cp311-cp311-win32.whl", hash = "sha256:7d29c23bdf6e5438c755b941cef867ec2a4a172ceb9f50553b6ed70d50dfd656"},
- {file = "pyrsistent-0.20.0-cp311-cp311-win_amd64.whl", hash = "sha256:59a89bccd615551391f3237e00006a26bcf98a4d18623a19909a2c48b8e986ee"},
- {file = "pyrsistent-0.20.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:09848306523a3aba463c4b49493a760e7a6ca52e4826aa100ee99d8d39b7ad1e"},
- {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a14798c3005ec892bbada26485c2eea3b54109cb2533713e355c806891f63c5e"},
- {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b14decb628fac50db5e02ee5a35a9c0772d20277824cfe845c8a8b717c15daa3"},
- {file = "pyrsistent-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e2c116cc804d9b09ce9814d17df5edf1df0c624aba3b43bc1ad90411487036d"},
- {file = "pyrsistent-0.20.0-cp312-cp312-win32.whl", hash = "sha256:e78d0c7c1e99a4a45c99143900ea0546025e41bb59ebc10182e947cf1ece9174"},
- {file = "pyrsistent-0.20.0-cp312-cp312-win_amd64.whl", hash = "sha256:4021a7f963d88ccd15b523787d18ed5e5269ce57aa4037146a2377ff607ae87d"},
- {file = "pyrsistent-0.20.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:79ed12ba79935adaac1664fd7e0e585a22caa539dfc9b7c7c6d5ebf91fb89054"},
- {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f920385a11207dc372a028b3f1e1038bb244b3ec38d448e6d8e43c6b3ba20e98"},
- {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f5c2d012671b7391803263419e31b5c7c21e7c95c8760d7fc35602353dee714"},
- {file = "pyrsistent-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef3992833fbd686ee783590639f4b8343a57f1f75de8633749d984dc0eb16c86"},
- {file = "pyrsistent-0.20.0-cp38-cp38-win32.whl", hash = "sha256:881bbea27bbd32d37eb24dd320a5e745a2a5b092a17f6debc1349252fac85423"},
- {file = "pyrsistent-0.20.0-cp38-cp38-win_amd64.whl", hash = "sha256:6d270ec9dd33cdb13f4d62c95c1a5a50e6b7cdd86302b494217137f760495b9d"},
- {file = "pyrsistent-0.20.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ca52d1ceae015859d16aded12584c59eb3825f7b50c6cfd621d4231a6cc624ce"},
- {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b318ca24db0f0518630e8b6f3831e9cba78f099ed5c1d65ffe3e023003043ba0"},
- {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fed2c3216a605dc9a6ea50c7e84c82906e3684c4e80d2908208f662a6cbf9022"},
- {file = "pyrsistent-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e14c95c16211d166f59c6611533d0dacce2e25de0f76e4c140fde250997b3ca"},
- {file = "pyrsistent-0.20.0-cp39-cp39-win32.whl", hash = "sha256:f058a615031eea4ef94ead6456f5ec2026c19fb5bd6bfe86e9665c4158cf802f"},
- {file = "pyrsistent-0.20.0-cp39-cp39-win_amd64.whl", hash = "sha256:58b8f6366e152092194ae68fefe18b9f0b4f89227dfd86a07770c3d86097aebf"},
- {file = "pyrsistent-0.20.0-py3-none-any.whl", hash = "sha256:c55acc4733aad6560a7f5f818466631f07efc001fd023f34a6c203f8b6df0f0b"},
- {file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"},
-]
-
-[[package]]
-name = "pysaml2"
-version = "7.3.1"
-description = "Python implementation of SAML Version 2 Standard"
-optional = false
-python-versions = ">=3.6.2,<4.0.0"
-files = [
- {file = "pysaml2-7.3.1-py3-none-any.whl", hash = "sha256:2cc66e7a371d3f5ff9601f0ed93b5276cca816fce82bb38447d5a0651f2f5193"},
- {file = "pysaml2-7.3.1.tar.gz", hash = "sha256:eab22d187c6dd7707c58b5bb1688f9b8e816427667fc99d77f54399e15cd0a0a"},
-]
-
-[package.dependencies]
-cryptography = ">=3.1"
-defusedxml = "*"
-importlib-resources = {version = "*", markers = "python_version < \"3.9\""}
-pyopenssl = "*"
-python-dateutil = "*"
-pytz = "*"
-requests = ">=2,<3"
-xmlschema = ">=1.2.1"
-
-[package.extras]
-s2repoze = ["paste", "repoze.who", "zope.interface"]
-
-[[package]]
-name = "pystache"
-version = "0.6.0"
-description = "Mustache for Python"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "pystache-0.6.0.tar.gz", hash = "sha256:93bf92b2149a4c4b58d12142e2c4c6dd5c08d89e4c95afccd4b6efe2ee1d470d"},
-]
-
-[package.extras]
-cov = ["coverage", "coverage_python_version"]
-test = ["nose"]
-
-[[package]]
-name = "pytest"
-version = "7.4.0"
-description = "pytest: simple powerful testing with Python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"},
- {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
-iniconfig = "*"
-packaging = "*"
-pluggy = ">=0.12,<2.0"
-tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
-
-[package.extras]
-testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
-
-[[package]]
-name = "pytest-cov"
-version = "4.1.0"
-description = "Pytest plugin for measuring coverage."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
- {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
-]
-
-[package.dependencies]
-coverage = {version = ">=5.2.1", extras = ["toml"]}
-pytest = ">=4.6"
-
-[package.extras]
-testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
-
-[[package]]
-name = "python-arango"
-version = "6.1.0"
-description = "Python Driver for ArangoDB"
-optional = false
-python-versions = "*"
-files = [
- {file = "python-arango-6.1.0.tar.gz", hash = "sha256:04dbb017945105925c01d05ac98a62d718d29586cf39ef85ae3a44f032923b1c"},
- {file = "python_arango-6.1.0-py2.py3-none-any.whl", hash = "sha256:ca31ceb555ff7c9671ada61c6a81abdcd24b308e76a27d91398a98320f9b27db"},
-]
-
-[package.dependencies]
-PyJWT = "*"
-requests = "*"
-requests-toolbelt = "*"
-six = "*"
-
-[[package]]
-name = "python-dateutil"
-version = "2.8.0"
-description = "Extensions to the standard Python datetime module"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "python-dateutil-2.8.0.tar.gz", hash = "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e"},
- {file = "python_dateutil-2.8.0-py2.py3-none-any.whl", hash = "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb"},
-]
-
-[package.dependencies]
-six = ">=1.5"
-
-[[package]]
-name = "python-dotenv"
-version = "0.19.2"
-description = "Read key-value pairs from a .env file and set them as environment variables"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "python-dotenv-0.19.2.tar.gz", hash = "sha256:a5de49a31e953b45ff2d2fd434bbc2670e8db5273606c1e737cc6b93eff3655f"},
- {file = "python_dotenv-0.19.2-py2.py3-none-any.whl", hash = "sha256:32b2bdc1873fd3a3c346da1c6db83d0053c3c62f28f1f38516070c4c8971b1d3"},
-]
-
-[package.extras]
-cli = ["click (>=5.0)"]
-
-[[package]]
-name = "python-rapidjson"
-version = "1.20"
-description = "Python wrapper around rapidjson"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "python_rapidjson-1.20-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeaa8487fdd8db409bd2e0c41c59cee3b9f1d08401fc75520f7d35c7a22d8789"},
- {file = "python_rapidjson-1.20-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:425c2bb8e778a04497953482c251944b2736f61012d897f17b73da3eca060c27"},
- {file = "python_rapidjson-1.20-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7cbbff9696ea01dd8a29502cb314471c9a5d4239f2f3b7e35b6adbde2cc620"},
- {file = "python_rapidjson-1.20-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:83a48f96d0abb8349a4d42f029259b755d8c6fd347f5de2d640e164c3f45e63b"},
- {file = "python_rapidjson-1.20-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6cb3ad353ec083a6dcf0552f1fce3c490f92e2fccf9a81eac42835297a8431a1"},
- {file = "python_rapidjson-1.20-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f7b6574887d8828f34eb3384092d6e6c290e8fbb12703c409dbdde814612657"},
- {file = "python_rapidjson-1.20-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:403e4986484f01f79fdce00b48c12a1b39d16e822cd37c60843ab26455ab0680"},
- {file = "python_rapidjson-1.20-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e3f89a58d7709d5879586e9dbfd11be76a799e8fbdbb5eddaffaeba9b572fba3"},
- {file = "python_rapidjson-1.20-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:b0d07d4f0ebbb2228d5140463f11ac519147b9d791f7e40b3edf518a806be3cc"},
- {file = "python_rapidjson-1.20-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a5fb413414b92763a54d53b732df3c9de1b114012c8881a3d1215a19b9fca494"},
- {file = "python_rapidjson-1.20-cp310-cp310-win32.whl", hash = "sha256:9831430f17101a6a249e07db9c42d26c3263e6009450722cce0c14726421f434"},
- {file = "python_rapidjson-1.20-cp310-cp310-win_amd64.whl", hash = "sha256:fbff5caf127c5bed4d6620f95a039dd9e293784d844af50782aaf278a743acb4"},
- {file = "python_rapidjson-1.20-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:328095d6d558090c29d24d889482b10dcc3ade3b77c93a61ea86794623046628"},
- {file = "python_rapidjson-1.20-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fc7a095f77eb3bb6acff94acf868a100faaf06028c4b513428f161cd55030476"},
- {file = "python_rapidjson-1.20-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce4cee141c924300cbedba1e5bea05b13484598d1e550afc5b50209ba73c62f2"},
- {file = "python_rapidjson-1.20-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4355bcfc8629d15f6246011b40e84cc368d842518a91adb15c5eba211305ee5b"},
- {file = "python_rapidjson-1.20-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dd9c5e661d17eafa44b2875f6ce55178cc87388575ce3cd3c606d5a33772b49"},
- {file = "python_rapidjson-1.20-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd978c7669cc844f669a48d2a6019fb9134a2385536f806fe265a1e374c3573a"},
- {file = "python_rapidjson-1.20-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fc52405435ce875aa000afa2637ea267eb0d4ab9622f9b97c92d92cb1a9c440"},
- {file = "python_rapidjson-1.20-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bef1eca712fb9fd5d2edd724dd1dd8a608215d6afcaee4f351b3e99e3f73f720"},
- {file = "python_rapidjson-1.20-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6355cb690bf64629767206524d4d00da909970d46d8fc0b367f339975e4eb419"},
- {file = "python_rapidjson-1.20-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f974c4e11be833221062fc4c3129bed172082792b33ef9fc1b8104f49c514f1d"},
- {file = "python_rapidjson-1.20-cp311-cp311-win32.whl", hash = "sha256:06ee7bcf660ebbdf1953aa7bf74214b722d934928c7b9f2a23b12e0713b61fa4"},
- {file = "python_rapidjson-1.20-cp311-cp311-win_amd64.whl", hash = "sha256:9df543521fa4b69589c42772b2f32a6c334b3b5fc612cd6dc3705136d0788da3"},
- {file = "python_rapidjson-1.20-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6056fcc8caeb9b04775bf655568bba362c7670ab792c1b438671bb056db954cd"},
- {file = "python_rapidjson-1.20-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:225bd4cbabfe7910261cbcebb8b811d4ff98e90cdd17c233b916c6aa71a9553f"},
- {file = "python_rapidjson-1.20-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:026077b663acf93a3f2b1adb87282e611a30214b8ae8001b7e4863a3b978e646"},
- {file = "python_rapidjson-1.20-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:884e1dd4c0770ed424737941af4d5dc9014995f9c33595f151af13f83ce282c3"},
- {file = "python_rapidjson-1.20-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f55531c8197cb7a21a5ef0ffa46f2b8fc8c5fe7c6fd08bdbd2063ae65d2ff65"},
- {file = "python_rapidjson-1.20-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c60121d155562dc694c05ed7df4e39e42ee1d3adff2a060c64a004498e6451f7"},
- {file = "python_rapidjson-1.20-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3a6620eed0b04196f37fab7048c1d672d03391bb29d7f09ee8fee8dea33f11f4"},
- {file = "python_rapidjson-1.20-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ddb63eff401ce7cf20cdd5e21942fc23fbe0e1dc1d96d7ae838645fb1f74fb47"},
- {file = "python_rapidjson-1.20-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:05e28c3dbb4a0d74ec13af9668ef2b9f302edf83cf7ce1d8316a95364720eec0"},
- {file = "python_rapidjson-1.20-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b733978ecd84fc5df9a778ce821dc1f3113f7bfc2493cac0bb17efb4ae0bb8fa"},
- {file = "python_rapidjson-1.20-cp312-cp312-win32.whl", hash = "sha256:d87041448cec00e2db5d858625a76dc1b59eef6691a039acff6d92ad8581cfc1"},
- {file = "python_rapidjson-1.20-cp312-cp312-win_amd64.whl", hash = "sha256:5d3be149ce5475f9605f01240487541057792abad94d3fd0cd56af363cf5a4dc"},
- {file = "python_rapidjson-1.20-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:daee815b4c20ca6e4dbc6bde373dd3f65b53813d775f1c94b765b33b402513a7"},
- {file = "python_rapidjson-1.20-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:083df379c769b30f9bc40041c91fd9d8f7bb8ca2b3c7170258842aced2098e05"},
- {file = "python_rapidjson-1.20-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9399ad75a2e3377f9e6208caabe73eb9354cd01b732407475ccadcd42c577df"},
- {file = "python_rapidjson-1.20-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:599ab208ccf6172d6cfac1abe048c837e62612f91f97d198e32773c45346a0b4"},
- {file = "python_rapidjson-1.20-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf3c0e2a5b97b0d07311f15f0dce4434e43dec865c3794ad1b10d968460fd665"},
- {file = "python_rapidjson-1.20-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8064b8edb57ddd9e3ffa539cf2ec2f03515751fb0698b40ba5cb66a2123af19"},
- {file = "python_rapidjson-1.20-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bc79d7f00f7538e027960ca6bcd1e03ed99fcf660d4d882d1c22f641155d0db0"},
- {file = "python_rapidjson-1.20-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:87aa0b01b8c20984844f1440b8ff6bdb32de911a1750fed344b9daed33b4b52b"},
- {file = "python_rapidjson-1.20-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4099cb9eae8a0ce19c09e02729eb6d69d5180424f13a2641a6c407d053e47a82"},
- {file = "python_rapidjson-1.20-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c680cd2b4de760ff6875de71fe6a87bd610aa116593d62e4f81a563be86ae18"},
- {file = "python_rapidjson-1.20-cp313-cp313-win32.whl", hash = "sha256:9e431a7afc77aa874fed537c9f6bf5fcecaef124ebeae2a2379d3b9e9adce74b"},
- {file = "python_rapidjson-1.20-cp313-cp313-win_amd64.whl", hash = "sha256:7444bc7e6a04c03d6ed748b5dab0798fa2b3f2b303be8c38d3af405b2cac6d63"},
- {file = "python_rapidjson-1.20-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:69e702fe74fe8c44c6253bb91364a270dc49f704920c90e01040155bd600a5fd"},
- {file = "python_rapidjson-1.20-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b9496b1e9d6247e8802ac559b7eebb5f3cae426d1c1dbde4049c63dff0941370"},
- {file = "python_rapidjson-1.20-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1446e902b6c781f271bf8556da636c1375cbb208e25f92e1af4cc2d92cf0cf15"},
- {file = "python_rapidjson-1.20-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:368ecdf4031abbde9c94aac40981d9a1238e6bcfef9fbfee441047b4757d6033"},
- {file = "python_rapidjson-1.20-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:924f9ea302494d4a4d540d3509f8f1f15622ea7d614c6f29df3188d52c6cb546"},
- {file = "python_rapidjson-1.20-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:632acb2dfa29883723e24bb2ce47c726edd5f672341553a5184db68f78d3bd09"},
- {file = "python_rapidjson-1.20-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:c2f85da53286e67778d4061ef32ff44ca9b5f945030463716e046ee8985319f8"},
- {file = "python_rapidjson-1.20-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c05c8602c019cc0db19601fdc4927755a9d33f21d01beb3d5767313d7a81360d"},
- {file = "python_rapidjson-1.20-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7d36aab758bfb1b59e0a849cd20e971eda951a04d3586bb5f6cb460bfc7c103d"},
- {file = "python_rapidjson-1.20-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e5774c905034362298312116f9b58c181e91a09800e4e5cede7b3d460a6a9fde"},
- {file = "python_rapidjson-1.20-cp38-cp38-win32.whl", hash = "sha256:488d0c6155004b5177225eaf331bb1838616da05ae966dd24a7d442751c1d193"},
- {file = "python_rapidjson-1.20-cp38-cp38-win_amd64.whl", hash = "sha256:00183c4938cd491b98b1a43626bc5a381842ceba87644cb91b25555f3fc3c0bf"},
- {file = "python_rapidjson-1.20-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f510ffe32fec319699f0c1ea9cee5bde47c33202b034b85c5d1b9ace682aa96a"},
- {file = "python_rapidjson-1.20-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2b624b3613fb7b8dfef4adc709bf39489be8c655cd9d24dc4e2cc16fc5def83"},
- {file = "python_rapidjson-1.20-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9f813a37d1f708a221f1f7d8c97c437d10597261810c1d3b52cf8f248d66c0"},
- {file = "python_rapidjson-1.20-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0c3f7085c52259c56af72462df7620c3b8bb95575fd9b8c3a073728855e93269"},
- {file = "python_rapidjson-1.20-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:871f2eeb0907f3d7ab09efe04c5b5e2886c275ea568f7867c97468ae14cdd52f"},
- {file = "python_rapidjson-1.20-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7c0408e7f52f32cf4bdd5aa305f005914b0143cac69d42575e2d40e8678cd72"},
- {file = "python_rapidjson-1.20-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ec17a18df700e1f956fc5a0c41cbb3cc746c44c0fef38988efba9b2cb607ecfa"},
- {file = "python_rapidjson-1.20-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1c0303bd445312a78485a9adba06dfdb84561c5157a9cda7999fefb36df4c6cc"},
- {file = "python_rapidjson-1.20-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:303b079ef268a996242be51ae80c8b563ee2d73489ab4f16199fef2216e80765"},
- {file = "python_rapidjson-1.20-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5adcef7a27abafbb2b3d0b02c822dfd9b4b329769cb97810b7f9733e1fda0498"},
- {file = "python_rapidjson-1.20-cp39-cp39-win32.whl", hash = "sha256:3e963e78fff6ab5ab2ae847b65683774c48b9b192307380f2175540d6423fd73"},
- {file = "python_rapidjson-1.20-cp39-cp39-win_amd64.whl", hash = "sha256:1fc3bba6632ecffeb1897fdf98858dc50a677237f4241853444c70a041158a90"},
- {file = "python_rapidjson-1.20.tar.gz", hash = "sha256:115f08c86d2df7543c02605e77c84727cdabc4b08310d2f097e953efeaaa73eb"},
-]
-
-[[package]]
-name = "pytz"
-version = "2023.3.post1"
-description = "World timezone definitions, modern and historical"
-optional = false
-python-versions = "*"
-files = [
- {file = "pytz-2023.3.post1-py2.py3-none-any.whl", hash = "sha256:ce42d816b81b68506614c11e8937d3aa9e41007ceb50bfdcb0749b921bf646c7"},
- {file = "pytz-2023.3.post1.tar.gz", hash = "sha256:7b4fddbeb94a1eba4b557da24f19fdf9db575192544270a9101d8509f9f43d7b"},
-]
-
-[[package]]
-name = "pytz-deprecation-shim"
-version = "0.1.0.post0"
-description = "Shims to make deprecation of pytz easier"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-files = [
- {file = "pytz_deprecation_shim-0.1.0.post0-py2.py3-none-any.whl", hash = "sha256:8314c9692a636c8eb3bda879b9f119e350e93223ae83e70e80c31675a0fdc1a6"},
- {file = "pytz_deprecation_shim-0.1.0.post0.tar.gz", hash = "sha256:af097bae1b616dde5c5744441e2ddc69e74dfdcb0c263129610d85b87445a59d"},
-]
-
-[package.dependencies]
-"backports.zoneinfo" = {version = "*", markers = "python_version >= \"3.6\" and python_version < \"3.9\""}
-tzdata = {version = "*", markers = "python_version >= \"3.6\""}
-
-[[package]]
-name = "pyyaml"
-version = "6.0.1"
-description = "YAML parser and emitter for Python"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"},
- {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"},
- {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"},
- {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"},
- {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"},
- {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"},
- {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
- {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
- {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
- {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
- {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
- {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
- {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
- {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
- {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"},
- {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
- {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
- {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
- {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
- {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"},
- {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"},
- {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"},
- {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"},
- {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"},
- {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"},
- {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"},
- {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"},
- {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"},
- {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"},
- {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"},
- {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"},
- {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"},
- {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"},
- {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"},
- {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"},
- {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"},
- {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"},
- {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"},
- {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
-]
-
-[[package]]
-name = "rdflib"
-version = "6.3.2"
-description = "RDFLib is a Python library for working with RDF, a simple yet powerful language for representing information."
-optional = false
-python-versions = ">=3.7,<4.0"
-files = [
- {file = "rdflib-6.3.2-py3-none-any.whl", hash = "sha256:36b4e74a32aa1e4fa7b8719876fb192f19ecd45ff932ea5ebbd2e417a0247e63"},
- {file = "rdflib-6.3.2.tar.gz", hash = "sha256:72af591ff704f4caacea7ecc0c5a9056b8553e0489dd4f35a9bc52dbd41522e0"},
-]
-
-[package.dependencies]
-isodate = ">=0.6.0,<0.7.0"
-pyparsing = ">=2.1.0,<4"
-
-[package.extras]
-berkeleydb = ["berkeleydb (>=18.1.0,<19.0.0)"]
-html = ["html5lib (>=1.0,<2.0)"]
-lxml = ["lxml (>=4.3.0,<5.0.0)"]
-networkx = ["networkx (>=2.0.0,<3.0.0)"]
-
-[[package]]
-name = "reactivex"
-version = "4.0.4"
-description = "ReactiveX (Rx) for Python"
-optional = false
-python-versions = ">=3.7,<4.0"
-files = [
- {file = "reactivex-4.0.4-py3-none-any.whl", hash = "sha256:0004796c420bd9e68aad8e65627d85a8e13f293de76656165dffbcb3a0e3fb6a"},
- {file = "reactivex-4.0.4.tar.gz", hash = "sha256:e912e6591022ab9176df8348a653fe8c8fa7a301f26f9931c9d8c78a650e04e8"},
-]
-
-[package.dependencies]
-typing-extensions = ">=4.1.1,<5.0.0"
-
-[[package]]
-name = "redis"
-version = "4.6.0"
-description = "Python client for Redis database and key-value store"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"},
- {file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"},
-]
-
-[package.dependencies]
-async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""}
-
-[package.extras]
-hiredis = ["hiredis (>=1.0.0)"]
-ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"]
-
-[[package]]
-name = "regex"
-version = "2023.8.8"
-description = "Alternative regular expression module, to replace re."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "regex-2023.8.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:88900f521c645f784260a8d346e12a1590f79e96403971241e64c3a265c8ecdb"},
- {file = "regex-2023.8.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3611576aff55918af2697410ff0293d6071b7e00f4b09e005d614686ac4cd57c"},
- {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a0ccc8f2698f120e9e5742f4b38dc944c38744d4bdfc427616f3a163dd9de5"},
- {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c662a4cbdd6280ee56f841f14620787215a171c4e2d1744c9528bed8f5816c96"},
- {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cf0633e4a1b667bfe0bb10b5e53fe0d5f34a6243ea2530eb342491f1adf4f739"},
- {file = "regex-2023.8.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:551ad543fa19e94943c5b2cebc54c73353ffff08228ee5f3376bd27b3d5b9800"},
- {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54de2619f5ea58474f2ac211ceea6b615af2d7e4306220d4f3fe690c91988a61"},
- {file = "regex-2023.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ec4b3f0aebbbe2fc0134ee30a791af522a92ad9f164858805a77442d7d18570"},
- {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3ae646c35cb9f820491760ac62c25b6d6b496757fda2d51be429e0e7b67ae0ab"},
- {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ca339088839582d01654e6f83a637a4b8194d0960477b9769d2ff2cfa0fa36d2"},
- {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:d9b6627408021452dcd0d2cdf8da0534e19d93d070bfa8b6b4176f99711e7f90"},
- {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:bd3366aceedf274f765a3a4bc95d6cd97b130d1dda524d8f25225d14123c01db"},
- {file = "regex-2023.8.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7aed90a72fc3654fba9bc4b7f851571dcc368120432ad68b226bd593f3f6c0b7"},
- {file = "regex-2023.8.8-cp310-cp310-win32.whl", hash = "sha256:80b80b889cb767cc47f31d2b2f3dec2db8126fbcd0cff31b3925b4dc6609dcdb"},
- {file = "regex-2023.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:b82edc98d107cbc7357da7a5a695901b47d6eb0420e587256ba3ad24b80b7d0b"},
- {file = "regex-2023.8.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1e7d84d64c84ad97bf06f3c8cb5e48941f135ace28f450d86af6b6512f1c9a71"},
- {file = "regex-2023.8.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ce0f9fbe7d295f9922c0424a3637b88c6c472b75eafeaff6f910494a1fa719ef"},
- {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06c57e14ac723b04458df5956cfb7e2d9caa6e9d353c0b4c7d5d54fcb1325c46"},
- {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7a9aaa5a1267125eef22cef3b63484c3241aaec6f48949b366d26c7250e0357"},
- {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b7408511fca48a82a119d78a77c2f5eb1b22fe88b0d2450ed0756d194fe7a9a"},
- {file = "regex-2023.8.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14dc6f2d88192a67d708341f3085df6a4f5a0c7b03dec08d763ca2cd86e9f559"},
- {file = "regex-2023.8.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48c640b99213643d141550326f34f0502fedb1798adb3c9eb79650b1ecb2f177"},
- {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0085da0f6c6393428bf0d9c08d8b1874d805bb55e17cb1dfa5ddb7cfb11140bf"},
- {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:964b16dcc10c79a4a2be9f1273fcc2684a9eedb3906439720598029a797b46e6"},
- {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7ce606c14bb195b0e5108544b540e2c5faed6843367e4ab3deb5c6aa5e681208"},
- {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:40f029d73b10fac448c73d6eb33d57b34607f40116e9f6e9f0d32e9229b147d7"},
- {file = "regex-2023.8.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3b8e6ea6be6d64104d8e9afc34c151926f8182f84e7ac290a93925c0db004bfd"},
- {file = "regex-2023.8.8-cp311-cp311-win32.whl", hash = "sha256:942f8b1f3b223638b02df7df79140646c03938d488fbfb771824f3d05fc083a8"},
- {file = "regex-2023.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:51d8ea2a3a1a8fe4f67de21b8b93757005213e8ac3917567872f2865185fa7fb"},
- {file = "regex-2023.8.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e951d1a8e9963ea51efd7f150450803e3b95db5939f994ad3d5edac2b6f6e2b4"},
- {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:704f63b774218207b8ccc6c47fcef5340741e5d839d11d606f70af93ee78e4d4"},
- {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22283c769a7b01c8ac355d5be0715bf6929b6267619505e289f792b01304d898"},
- {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:91129ff1bb0619bc1f4ad19485718cc623a2dc433dff95baadbf89405c7f6b57"},
- {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de35342190deb7b866ad6ba5cbcccb2d22c0487ee0cbb251efef0843d705f0d4"},
- {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b993b6f524d1e274a5062488a43e3f9f8764ee9745ccd8e8193df743dbe5ee61"},
- {file = "regex-2023.8.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3026cbcf11d79095a32d9a13bbc572a458727bd5b1ca332df4a79faecd45281c"},
- {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:293352710172239bf579c90a9864d0df57340b6fd21272345222fb6371bf82b3"},
- {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d909b5a3fff619dc7e48b6b1bedc2f30ec43033ba7af32f936c10839e81b9217"},
- {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3d370ff652323c5307d9c8e4c62efd1956fb08051b0e9210212bc51168b4ff56"},
- {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:b076da1ed19dc37788f6a934c60adf97bd02c7eea461b73730513921a85d4235"},
- {file = "regex-2023.8.8-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e9941a4ada58f6218694f382e43fdd256e97615db9da135e77359da257a7168b"},
- {file = "regex-2023.8.8-cp36-cp36m-win32.whl", hash = "sha256:a8c65c17aed7e15a0c824cdc63a6b104dfc530f6fa8cb6ac51c437af52b481c7"},
- {file = "regex-2023.8.8-cp36-cp36m-win_amd64.whl", hash = "sha256:aadf28046e77a72f30dcc1ab185639e8de7f4104b8cb5c6dfa5d8ed860e57236"},
- {file = "regex-2023.8.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:423adfa872b4908843ac3e7a30f957f5d5282944b81ca0a3b8a7ccbbfaa06103"},
- {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ae594c66f4a7e1ea67232a0846649a7c94c188d6c071ac0210c3e86a5f92109"},
- {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e51c80c168074faa793685656c38eb7a06cbad7774c8cbc3ea05552d615393d8"},
- {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:09b7f4c66aa9d1522b06e31a54f15581c37286237208df1345108fcf4e050c18"},
- {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e73e5243af12d9cd6a9d6a45a43570dbe2e5b1cdfc862f5ae2b031e44dd95a8"},
- {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941460db8fe3bd613db52f05259c9336f5a47ccae7d7def44cc277184030a116"},
- {file = "regex-2023.8.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f0ccf3e01afeb412a1a9993049cb160d0352dba635bbca7762b2dc722aa5742a"},
- {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2e9216e0d2cdce7dbc9be48cb3eacb962740a09b011a116fd7af8c832ab116ca"},
- {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:5cd9cd7170459b9223c5e592ac036e0704bee765706445c353d96f2890e816c8"},
- {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:4873ef92e03a4309b3ccd8281454801b291b689f6ad45ef8c3658b6fa761d7ac"},
- {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:239c3c2a339d3b3ddd51c2daef10874410917cd2b998f043c13e2084cb191684"},
- {file = "regex-2023.8.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1005c60ed7037be0d9dea1f9c53cc42f836188227366370867222bda4c3c6bd7"},
- {file = "regex-2023.8.8-cp37-cp37m-win32.whl", hash = "sha256:e6bd1e9b95bc5614a7a9c9c44fde9539cba1c823b43a9f7bc11266446dd568e3"},
- {file = "regex-2023.8.8-cp37-cp37m-win_amd64.whl", hash = "sha256:9a96edd79661e93327cfeac4edec72a4046e14550a1d22aa0dd2e3ca52aec921"},
- {file = "regex-2023.8.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f2181c20ef18747d5f4a7ea513e09ea03bdd50884a11ce46066bb90fe4213675"},
- {file = "regex-2023.8.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a2ad5add903eb7cdde2b7c64aaca405f3957ab34f16594d2b78d53b8b1a6a7d6"},
- {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9233ac249b354c54146e392e8a451e465dd2d967fc773690811d3a8c240ac601"},
- {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920974009fb37b20d32afcdf0227a2e707eb83fe418713f7a8b7de038b870d0b"},
- {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2b6c5dfe0929b6c23dde9624483380b170b6e34ed79054ad131b20203a1a63"},
- {file = "regex-2023.8.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96979d753b1dc3b2169003e1854dc67bfc86edf93c01e84757927f810b8c3c93"},
- {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ae54a338191e1356253e7883d9d19f8679b6143703086245fb14d1f20196be9"},
- {file = "regex-2023.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2162ae2eb8b079622176a81b65d486ba50b888271302190870b8cc488587d280"},
- {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c884d1a59e69e03b93cf0dfee8794c63d7de0ee8f7ffb76e5f75be8131b6400a"},
- {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf9273e96f3ee2ac89ffcb17627a78f78e7516b08f94dc435844ae72576a276e"},
- {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:83215147121e15d5f3a45d99abeed9cf1fe16869d5c233b08c56cdf75f43a504"},
- {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f7454aa427b8ab9101f3787eb178057c5250478e39b99540cfc2b889c7d0586"},
- {file = "regex-2023.8.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0640913d2c1044d97e30d7c41728195fc37e54d190c5385eacb52115127b882"},
- {file = "regex-2023.8.8-cp38-cp38-win32.whl", hash = "sha256:0c59122ceccb905a941fb23b087b8eafc5290bf983ebcb14d2301febcbe199c7"},
- {file = "regex-2023.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:c12f6f67495ea05c3d542d119d270007090bad5b843f642d418eb601ec0fa7be"},
- {file = "regex-2023.8.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:82cd0a69cd28f6cc3789cc6adeb1027f79526b1ab50b1f6062bbc3a0ccb2dbc3"},
- {file = "regex-2023.8.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bb34d1605f96a245fc39790a117ac1bac8de84ab7691637b26ab2c5efb8f228c"},
- {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:987b9ac04d0b38ef4f89fbc035e84a7efad9cdd5f1e29024f9289182c8d99e09"},
- {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dd6082f4e2aec9b6a0927202c85bc1b09dcab113f97265127c1dc20e2e32495"},
- {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7eb95fe8222932c10d4436e7a6f7c99991e3fdd9f36c949eff16a69246dee2dc"},
- {file = "regex-2023.8.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7098c524ba9f20717a56a8d551d2ed491ea89cbf37e540759ed3b776a4f8d6eb"},
- {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b694430b3f00eb02c594ff5a16db30e054c1b9589a043fe9174584c6efa8033"},
- {file = "regex-2023.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b2aeab3895d778155054abea5238d0eb9a72e9242bd4b43f42fd911ef9a13470"},
- {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:988631b9d78b546e284478c2ec15c8a85960e262e247b35ca5eaf7ee22f6050a"},
- {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:67ecd894e56a0c6108ec5ab1d8fa8418ec0cff45844a855966b875d1039a2e34"},
- {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:14898830f0a0eb67cae2bbbc787c1a7d6e34ecc06fbd39d3af5fe29a4468e2c9"},
- {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:f2200e00b62568cfd920127782c61bc1c546062a879cdc741cfcc6976668dfcf"},
- {file = "regex-2023.8.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9691a549c19c22d26a4f3b948071e93517bdf86e41b81d8c6ac8a964bb71e5a6"},
- {file = "regex-2023.8.8-cp39-cp39-win32.whl", hash = "sha256:6ab2ed84bf0137927846b37e882745a827458689eb969028af8032b1b3dac78e"},
- {file = "regex-2023.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5543c055d8ec7801901e1193a51570643d6a6ab8751b1f7dd9af71af467538bb"},
- {file = "regex-2023.8.8.tar.gz", hash = "sha256:fcbdc5f2b0f1cd0f6a56cdb46fe41d2cce1e644e3b68832f3eeebc5fb0f7712e"},
-]
-
-[[package]]
-name = "requests"
-version = "2.32.3"
-description = "Python HTTP for Humans."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
- {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
-]
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = ">=2,<4"
-idna = ">=2.5,<4"
-urllib3 = ">=1.21.1,<3"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)"]
-use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
-
-[[package]]
-name = "requests-aws-sign"
-version = "0.1.5"
-description = "This package provides AWS V4 request signing using the requests library."
-optional = false
-python-versions = "*"
-files = [
- {file = "requests_aws_sign-0.1.5-py3-none-any.whl", hash = "sha256:a60e8eb6f53a7cfaa8cb7702852dbbfe41f518d5bc6d187e0d82d31e6528da0c"},
- {file = "requests_aws_sign-0.1.5.tar.gz", hash = "sha256:35f66c4db95ee82427309481a90bd84d1385ae2e32b7537d9d24b1d3acd069ec"},
-]
-
-[package.dependencies]
-boto3 = "*"
-requests = ">=2.0.0"
-
-[[package]]
-name = "requests-oauthlib"
-version = "1.3.1"
-description = "OAuthlib authentication support for Requests."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"},
- {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"},
-]
-
-[package.dependencies]
-oauthlib = ">=3.0.0"
-requests = ">=2.0.0"
-
-[package.extras]
-rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
-
-[[package]]
-name = "requests-toolbelt"
-version = "1.0.0"
-description = "A utility belt for advanced users of python-requests"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"},
- {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"},
-]
-
-[package.dependencies]
-requests = ">=2.0.1,<3.0.0"
-
-[[package]]
-name = "restrictedpython"
-version = "7.3"
-description = "RestrictedPython is a defined subset of the Python language which allows to provide a program input into a trusted environment."
-optional = false
-python-versions = "<3.13,>=3.7"
-files = [
- {file = "RestrictedPython-7.3-py3-none-any.whl", hash = "sha256:40a6170bbcfc48b32962831d9281a61608c8e56e7c02fd8e2397225f516a6ed4"},
- {file = "RestrictedPython-7.3.tar.gz", hash = "sha256:8888304c7858fdcfd86c50b58561797375ba40319d2b6ffb5d24b08b6a2dcd61"},
-]
-
-[package.extras]
-docs = ["Sphinx", "sphinx-rtd-theme"]
-test = ["pytest", "pytest-mock"]
-
-[[package]]
-name = "rfc3986"
-version = "1.5.0"
-description = "Validating URI References per RFC 3986"
-optional = false
-python-versions = "*"
-files = [
- {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
- {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
-]
-
-[package.dependencies]
-idna = {version = "*", optional = true, markers = "extra == \"idna2008\""}
-
-[package.extras]
-idna2008 = ["idna"]
-
-[[package]]
-name = "rich"
-version = "13.7.0"
-description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"},
- {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"},
-]
-
-[package.dependencies]
-markdown-it-py = ">=2.2.0"
-pygments = ">=2.13.0,<3.0.0"
-typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""}
-
-[package.extras]
-jupyter = ["ipywidgets (>=7.5.1,<9)"]
-
-[[package]]
-name = "rq"
-version = "1.16.1"
-description = "RQ is a simple, lightweight, library for creating background jobs, and processing them."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "rq-1.16.1-py3-none-any.whl", hash = "sha256:273de33f10bb9f18cd1e8ccc0a4e8dba2b8eb86a6ab2a91ae674f99bd68025f1"},
- {file = "rq-1.16.1.tar.gz", hash = "sha256:d9a6314bc759a743b4a5d89aa467eaa3a31dbbc0a34bcd0ee82e8852d9ec166d"},
-]
-
-[package.dependencies]
-click = ">=5"
-redis = ">=3.5"
-
-[[package]]
-name = "rq-scheduler"
-version = "0.13.1"
-description = "Provides job scheduling capabilities to RQ (Redis Queue)"
-optional = false
-python-versions = "*"
-files = [
- {file = "rq-scheduler-0.13.1.tar.gz", hash = "sha256:89d6a18f215536362b22c0548db7dbb8678bc520c18dc18a82fd0bb2b91695ce"},
- {file = "rq_scheduler-0.13.1-py2.py3-none-any.whl", hash = "sha256:c2b19c3aedfc7de4d405183c98aa327506e423bf4cdc556af55aaab9bbe5d1a1"},
-]
-
-[package.dependencies]
-crontab = ">=0.23.0"
-freezegun = "*"
-python-dateutil = "*"
-rq = ">=0.13"
-
-[[package]]
-name = "rsa"
-version = "4.9"
-description = "Pure-Python RSA implementation"
-optional = false
-python-versions = ">=3.6,<4"
-files = [
- {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"},
- {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"},
-]
-
-[package.dependencies]
-pyasn1 = ">=0.1.3"
-
-[[package]]
-name = "ruff"
-version = "0.0.289"
-description = "An extremely fast Python linter, written in Rust."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "ruff-0.0.289-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:c9a89d748e90c840bac9c37afe90cf13a5bfd460ca02ea93dad9d7bee3af03b4"},
- {file = "ruff-0.0.289-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:7f7396c6ea01ba332a6ad9d47642bac25d16bd2076aaa595b001f58b2f32ff05"},
- {file = "ruff-0.0.289-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7180de86c8ecd39624dec1699136f941c07e723201b4ce979bec9e7c67b40ad2"},
- {file = "ruff-0.0.289-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:73f37c65508203dd01a539926375a10243769c20d4fcab3fa6359cd3fbfc54b7"},
- {file = "ruff-0.0.289-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c14abcd7563b5c80be2dd809eeab20e4aa716bf849860b60a22d87ddf19eb88"},
- {file = "ruff-0.0.289-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:91b6d63b6b46d4707916472c91baa87aa0592e73f62a80ff55efdf6c0668cfd6"},
- {file = "ruff-0.0.289-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6479b8c4be3c36046c6c92054762b276fa0fddb03f6b9a310fbbf4c4951267fd"},
- {file = "ruff-0.0.289-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c5424318c254bcb091cb67e140ec9b9f7122074e100b06236f252923fb41e767"},
- {file = "ruff-0.0.289-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4daa90865796aedcedf0d8897fdd4cd09bf0ddd3504529a4ccf211edcaff3c7d"},
- {file = "ruff-0.0.289-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:8057e8ab0016c13b9419bad119e854f881e687bd96bc5e2d52c8baac0f278a44"},
- {file = "ruff-0.0.289-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:7eebfab2e6a6991908ff1bf82f2dc1e5095fc7e316848e62124526837b445f4d"},
- {file = "ruff-0.0.289-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ebc7af550018001a7fb39ca22cdce20e1a0de4388ea4a007eb5c822f6188c297"},
- {file = "ruff-0.0.289-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6e4e6eccb753efe760ba354fc8e9f783f6bba71aa9f592756f5bd0d78db898ed"},
- {file = "ruff-0.0.289-py3-none-win32.whl", hash = "sha256:bbb3044f931c09cf17dbe5b339896eece0d6ac10c9a86e172540fcdb1974f2b7"},
- {file = "ruff-0.0.289-py3-none-win_amd64.whl", hash = "sha256:6d043c5456b792be2615a52f16056c3cf6c40506ce1f2d6f9d3083cfcb9eeab6"},
- {file = "ruff-0.0.289-py3-none-win_arm64.whl", hash = "sha256:04a720bcca5e987426bb14ad8b9c6f55e259ea774da1cbeafe71569744cfd20a"},
- {file = "ruff-0.0.289.tar.gz", hash = "sha256:2513f853b0fc42f0339b7ab0d2751b63ce7a50a0032d2689b54b2931b3b866d7"},
-]
-
-[[package]]
-name = "s3transfer"
-version = "0.6.2"
-description = "An Amazon S3 Transfer Manager"
-optional = false
-python-versions = ">= 3.7"
-files = [
- {file = "s3transfer-0.6.2-py3-none-any.whl", hash = "sha256:b014be3a8a2aab98cfe1abc7229cc5a9a0cf05eb9c1f2b86b230fd8df3f78084"},
- {file = "s3transfer-0.6.2.tar.gz", hash = "sha256:cab66d3380cca3e70939ef2255d01cd8aece6a4907a9528740f668c4b0611861"},
-]
-
-[package.dependencies]
-botocore = ">=1.12.36,<2.0a.0"
-
-[package.extras]
-crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"]
-
-[[package]]
-name = "sasl"
-version = "0.3.1"
-description = "Cyrus-SASL bindings for Python"
-optional = false
-python-versions = "*"
-files = [
- {file = "sasl-0.3.1.tar.gz", hash = "sha256:0695030b23faa65aab2b462ce6f067d61caeb406de22d1ca7f9253fd9ebe127e"},
-]
-
-[package.dependencies]
-six = "*"
-
-[[package]]
-name = "scramp"
-version = "1.1.0"
-description = "An implementation of the SCRAM protocol."
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "scramp-1.1.0-py3-none-any.whl", hash = "sha256:e09d2a9be5adeb94cbeb56fc54a61fc5f5b6e140e679b2b60d1f7a8d6478d906"},
- {file = "scramp-1.1.0.tar.gz", hash = "sha256:475aa6296deb2737b86e9df9098e8eca0f30c8ad1cc0a8adadb99ef012a5ceba"},
-]
-
-[[package]]
-name = "semver"
-version = "2.8.1"
-description = "Python helper for Semantic Versioning (http://semver.org/)"
-optional = false
-python-versions = "*"
-files = [
- {file = "semver-2.8.1-py2.py3-none-any.whl", hash = "sha256:41c9aa26c67dc16c54be13074c352ab666bce1fa219c7110e8f03374cd4206b0"},
- {file = "semver-2.8.1.tar.gz", hash = "sha256:5b09010a66d9a3837211bb7ae5a20d10ba88f8cb49e92cb139a69ef90d5060d8"},
-]
-
-[[package]]
-name = "sentry-sdk"
-version = "1.45.1"
-description = "Python client for Sentry (https://sentry.io)"
-optional = false
-python-versions = "*"
-files = [
- {file = "sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1"},
- {file = "sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26"},
-]
-
-[package.dependencies]
-certifi = "*"
-urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""}
-
-[package.extras]
-aiohttp = ["aiohttp (>=3.5)"]
-arq = ["arq (>=0.23)"]
-asyncpg = ["asyncpg (>=0.23)"]
-beam = ["apache-beam (>=2.12)"]
-bottle = ["bottle (>=0.12.13)"]
-celery = ["celery (>=3)"]
-celery-redbeat = ["celery-redbeat (>=2)"]
-chalice = ["chalice (>=1.16.0)"]
-clickhouse-driver = ["clickhouse-driver (>=0.2.0)"]
-django = ["django (>=1.8)"]
-falcon = ["falcon (>=1.4)"]
-fastapi = ["fastapi (>=0.79.0)"]
-flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"]
-grpcio = ["grpcio (>=1.21.1)"]
-httpx = ["httpx (>=0.16.0)"]
-huey = ["huey (>=2)"]
-loguru = ["loguru (>=0.5)"]
-openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"]
-opentelemetry = ["opentelemetry-distro (>=0.35b0)"]
-opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"]
-pure-eval = ["asttokens", "executing", "pure-eval"]
-pymongo = ["pymongo (>=3.1)"]
-pyspark = ["pyspark (>=2.4.4)"]
-quart = ["blinker (>=1.1)", "quart (>=0.16.1)"]
-rq = ["rq (>=0.6)"]
-sanic = ["sanic (>=0.8)"]
-sqlalchemy = ["sqlalchemy (>=1.2)"]
-starlette = ["starlette (>=0.19.1)"]
-starlite = ["starlite (>=1.48)"]
-tornado = ["tornado (>=5)"]
-
-[[package]]
-name = "setuptools"
-version = "70.0.0"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "setuptools-70.0.0-py3-none-any.whl", hash = "sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4"},
- {file = "setuptools-70.0.0.tar.gz", hash = "sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0"},
-]
-
-[package.extras]
-docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
-testing = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mypy (==1.9)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.1)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
-
-[[package]]
-name = "simple-salesforce"
-version = "0.74.3"
-description = "A basic Salesforce.com REST API client."
-optional = false
-python-versions = "*"
-files = [
- {file = "simple_salesforce-0.74.3-py2.py3-none-any.whl", hash = "sha256:0bf4065a7769388d8f830bfc31200e6d2d6de50d19034e4113b59831dd72a438"},
-]
-
-[package.dependencies]
-requests = {version = "*", extras = ["security"]}
-
-[[package]]
-name = "simplejson"
-version = "3.19.2"
-description = "Simple, fast, extensible JSON encoder/decoder for Python"
-optional = false
-python-versions = ">=2.5, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "simplejson-3.19.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3471e95110dcaf901db16063b2e40fb394f8a9e99b3fe9ee3acc6f6ef72183a2"},
- {file = "simplejson-3.19.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3194cd0d2c959062b94094c0a9f8780ffd38417a5322450a0db0ca1a23e7fbd2"},
- {file = "simplejson-3.19.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:8a390e56a7963e3946ff2049ee1eb218380e87c8a0e7608f7f8790ba19390867"},
- {file = "simplejson-3.19.2-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1537b3dd62d8aae644f3518c407aa8469e3fd0f179cdf86c5992792713ed717a"},
- {file = "simplejson-3.19.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a8617625369d2d03766413bff9e64310feafc9fc4f0ad2b902136f1a5cd8c6b0"},
- {file = "simplejson-3.19.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:2c433a412e96afb9a3ce36fa96c8e61a757af53e9c9192c97392f72871e18e69"},
- {file = "simplejson-3.19.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:f1c70249b15e4ce1a7d5340c97670a95f305ca79f376887759b43bb33288c973"},
- {file = "simplejson-3.19.2-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:287e39ba24e141b046812c880f4619d0ca9e617235d74abc27267194fc0c7835"},
- {file = "simplejson-3.19.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6f0a0b41dd05eefab547576bed0cf066595f3b20b083956b1405a6f17d1be6ad"},
- {file = "simplejson-3.19.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2f98d918f7f3aaf4b91f2b08c0c92b1774aea113334f7cde4fe40e777114dbe6"},
- {file = "simplejson-3.19.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d74beca677623481810c7052926365d5f07393c72cbf62d6cce29991b676402"},
- {file = "simplejson-3.19.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f2398361508c560d0bf1773af19e9fe644e218f2a814a02210ac2c97ad70db0"},
- {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ad331349b0b9ca6da86064a3599c425c7a21cd41616e175ddba0866da32df48"},
- {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:332c848f02d71a649272b3f1feccacb7e4f7e6de4a2e6dc70a32645326f3d428"},
- {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25785d038281cd106c0d91a68b9930049b6464288cea59ba95b35ee37c2d23a5"},
- {file = "simplejson-3.19.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18955c1da6fc39d957adfa346f75226246b6569e096ac9e40f67d102278c3bcb"},
- {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:11cc3afd8160d44582543838b7e4f9aa5e97865322844b75d51bf4e0e413bb3e"},
- {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b01fda3e95d07a6148702a641e5e293b6da7863f8bc9b967f62db9461330562c"},
- {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:778331444917108fa8441f59af45886270d33ce8a23bfc4f9b192c0b2ecef1b3"},
- {file = "simplejson-3.19.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9eb117db8d7ed733a7317c4215c35993b815bf6aeab67523f1f11e108c040672"},
- {file = "simplejson-3.19.2-cp310-cp310-win32.whl", hash = "sha256:39b6d79f5cbfa3eb63a869639cfacf7c41d753c64f7801efc72692c1b2637ac7"},
- {file = "simplejson-3.19.2-cp310-cp310-win_amd64.whl", hash = "sha256:5675e9d8eeef0aa06093c1ff898413ade042d73dc920a03e8cea2fb68f62445a"},
- {file = "simplejson-3.19.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ed628c1431100b0b65387419551e822987396bee3c088a15d68446d92f554e0c"},
- {file = "simplejson-3.19.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:adcb3332979cbc941b8fff07181f06d2b608625edc0a4d8bc3ffc0be414ad0c4"},
- {file = "simplejson-3.19.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:08889f2f597ae965284d7b52a5c3928653a9406d88c93e3161180f0abc2433ba"},
- {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7938a78447174e2616be223f496ddccdbf7854f7bf2ce716dbccd958cc7d13"},
- {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a970a2e6d5281d56cacf3dc82081c95c1f4da5a559e52469287457811db6a79b"},
- {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:554313db34d63eac3b3f42986aa9efddd1a481169c12b7be1e7512edebff8eaf"},
- {file = "simplejson-3.19.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d36081c0b1c12ea0ed62c202046dca11438bee48dd5240b7c8de8da62c620e9"},
- {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a3cd18e03b0ee54ea4319cdcce48357719ea487b53f92a469ba8ca8e39df285e"},
- {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:66e5dc13bfb17cd6ee764fc96ccafd6e405daa846a42baab81f4c60e15650414"},
- {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:972a7833d4a1fcf7a711c939e315721a88b988553fc770a5b6a5a64bd6ebeba3"},
- {file = "simplejson-3.19.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3e74355cb47e0cd399ead3477e29e2f50e1540952c22fb3504dda0184fc9819f"},
- {file = "simplejson-3.19.2-cp311-cp311-win32.whl", hash = "sha256:1dd4f692304854352c3e396e9b5f0a9c9e666868dd0bdc784e2ac4c93092d87b"},
- {file = "simplejson-3.19.2-cp311-cp311-win_amd64.whl", hash = "sha256:9300aee2a8b5992d0f4293d88deb59c218989833e3396c824b69ba330d04a589"},
- {file = "simplejson-3.19.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b8d940fd28eb34a7084877747a60873956893e377f15a32ad445fe66c972c3b8"},
- {file = "simplejson-3.19.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4969d974d9db826a2c07671273e6b27bc48e940738d768fa8f33b577f0978378"},
- {file = "simplejson-3.19.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c594642d6b13d225e10df5c16ee15b3398e21a35ecd6aee824f107a625690374"},
- {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2f5a398b5e77bb01b23d92872255e1bcb3c0c719a3be40b8df146570fe7781a"},
- {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176a1b524a3bd3314ed47029a86d02d5a95cc0bee15bd3063a1e1ec62b947de6"},
- {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3c7363a8cb8c5238878ec96c5eb0fc5ca2cb11fc0c7d2379863d342c6ee367a"},
- {file = "simplejson-3.19.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:346820ae96aa90c7d52653539a57766f10f33dd4be609206c001432b59ddf89f"},
- {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de9a2792612ec6def556d1dc621fd6b2073aff015d64fba9f3e53349ad292734"},
- {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1c768e7584c45094dca4b334af361e43b0aaa4844c04945ac7d43379eeda9bc2"},
- {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:9652e59c022e62a5b58a6f9948b104e5bb96d3b06940c6482588176f40f4914b"},
- {file = "simplejson-3.19.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9c1a4393242e321e344213a90a1e3bf35d2f624aa8b8f6174d43e3c6b0e8f6eb"},
- {file = "simplejson-3.19.2-cp312-cp312-win32.whl", hash = "sha256:7cb98be113911cb0ad09e5523d0e2a926c09a465c9abb0784c9269efe4f95917"},
- {file = "simplejson-3.19.2-cp312-cp312-win_amd64.whl", hash = "sha256:6779105d2fcb7fcf794a6a2a233787f6bbd4731227333a072d8513b252ed374f"},
- {file = "simplejson-3.19.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:061e81ea2d62671fa9dea2c2bfbc1eec2617ae7651e366c7b4a2baf0a8c72cae"},
- {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4280e460e51f86ad76dc456acdbfa9513bdf329556ffc8c49e0200878ca57816"},
- {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11c39fbc4280d7420684494373b7c5904fa72a2b48ef543a56c2d412999c9e5d"},
- {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bccb3e88ec26ffa90f72229f983d3a5d1155e41a1171190fa723d4135523585b"},
- {file = "simplejson-3.19.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb5b50dc6dd671eb46a605a3e2eb98deb4a9af787a08fcdddabe5d824bb9664"},
- {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:d94245caa3c61f760c4ce4953cfa76e7739b6f2cbfc94cc46fff6c050c2390c5"},
- {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d0e5ffc763678d48ecc8da836f2ae2dd1b6eb2d27a48671066f91694e575173c"},
- {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d222a9ed082cd9f38b58923775152003765016342a12f08f8c123bf893461f28"},
- {file = "simplejson-3.19.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8434dcdd347459f9fd9c526117c01fe7ca7b016b6008dddc3c13471098f4f0dc"},
- {file = "simplejson-3.19.2-cp36-cp36m-win32.whl", hash = "sha256:c9ac1c2678abf9270e7228133e5b77c6c3c930ad33a3c1dfbdd76ff2c33b7b50"},
- {file = "simplejson-3.19.2-cp36-cp36m-win_amd64.whl", hash = "sha256:92c4a4a2b1f4846cd4364855cbac83efc48ff5a7d7c06ba014c792dd96483f6f"},
- {file = "simplejson-3.19.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0d551dc931638e2102b8549836a1632e6e7cf620af3d093a7456aa642bff601d"},
- {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:73a8a4653f2e809049999d63530180d7b5a344b23a793502413ad1ecea9a0290"},
- {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:40847f617287a38623507d08cbcb75d51cf9d4f9551dd6321df40215128325a3"},
- {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be893258d5b68dd3a8cba8deb35dc6411db844a9d35268a8d3793b9d9a256f80"},
- {file = "simplejson-3.19.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9eb3cff1b7d71aa50c89a0536f469cb8d6dcdd585d8f14fb8500d822f3bdee4"},
- {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d0f402e787e6e7ee7876c8b05e2fe6464820d9f35ba3f172e95b5f8b699f6c7f"},
- {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbbcc6b0639aa09b9649f36f1bcb347b19403fe44109948392fbb5ea69e48c3e"},
- {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:2fc697be37585eded0c8581c4788fcfac0e3f84ca635b73a5bf360e28c8ea1a2"},
- {file = "simplejson-3.19.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b0a3eb6dd39cce23801a50c01a0976971498da49bc8a0590ce311492b82c44b"},
- {file = "simplejson-3.19.2-cp37-cp37m-win32.whl", hash = "sha256:49f9da0d6cd17b600a178439d7d2d57c5ef01f816b1e0e875e8e8b3b42db2693"},
- {file = "simplejson-3.19.2-cp37-cp37m-win_amd64.whl", hash = "sha256:c87c22bd6a987aca976e3d3e23806d17f65426191db36d40da4ae16a6a494cbc"},
- {file = "simplejson-3.19.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9e4c166f743bb42c5fcc60760fb1c3623e8fda94f6619534217b083e08644b46"},
- {file = "simplejson-3.19.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0a48679310e1dd5c9f03481799311a65d343748fe86850b7fb41df4e2c00c087"},
- {file = "simplejson-3.19.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0521e0f07cb56415fdb3aae0bbd8701eb31a9dfef47bb57206075a0584ab2a2"},
- {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d2d5119b1d7a1ed286b8af37357116072fc96700bce3bec5bb81b2e7057ab41"},
- {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c1467d939932901a97ba4f979e8f2642415fcf02ea12f53a4e3206c9c03bc17"},
- {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49aaf4546f6023c44d7e7136be84a03a4237f0b2b5fb2b17c3e3770a758fc1a0"},
- {file = "simplejson-3.19.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60848ab779195b72382841fc3fa4f71698a98d9589b0a081a9399904487b5832"},
- {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0436a70d8eb42bea4fe1a1c32d371d9bb3b62c637969cb33970ad624d5a3336a"},
- {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:49e0e3faf3070abdf71a5c80a97c1afc059b4f45a5aa62de0c2ca0444b51669b"},
- {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ff836cd4041e16003549449cc0a5e372f6b6f871eb89007ab0ee18fb2800fded"},
- {file = "simplejson-3.19.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3848427b65e31bea2c11f521b6fc7a3145d6e501a1038529da2391aff5970f2f"},
- {file = "simplejson-3.19.2-cp38-cp38-win32.whl", hash = "sha256:3f39bb1f6e620f3e158c8b2eaf1b3e3e54408baca96a02fe891794705e788637"},
- {file = "simplejson-3.19.2-cp38-cp38-win_amd64.whl", hash = "sha256:0405984f3ec1d3f8777c4adc33eac7ab7a3e629f3b1c05fdded63acc7cf01137"},
- {file = "simplejson-3.19.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:445a96543948c011a3a47c8e0f9d61e9785df2544ea5be5ab3bc2be4bd8a2565"},
- {file = "simplejson-3.19.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a8c3cc4f9dfc33220246760358c8265dad6e1104f25f0077bbca692d616d358"},
- {file = "simplejson-3.19.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af9c7e6669c4d0ad7362f79cb2ab6784d71147503e62b57e3d95c4a0f222c01c"},
- {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:064300a4ea17d1cd9ea1706aa0590dcb3be81112aac30233823ee494f02cb78a"},
- {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9453419ea2ab9b21d925d0fd7e3a132a178a191881fab4169b6f96e118cc25bb"},
- {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e038c615b3906df4c3be8db16b3e24821d26c55177638ea47b3f8f73615111c"},
- {file = "simplejson-3.19.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16ca9c90da4b1f50f089e14485db8c20cbfff2d55424062791a7392b5a9b3ff9"},
- {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1018bd0d70ce85f165185d2227c71e3b1e446186f9fa9f971b69eee223e1e3cd"},
- {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e8dd53a8706b15bc0e34f00e6150fbefb35d2fd9235d095b4f83b3c5ed4fa11d"},
- {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:2d022b14d7758bfb98405672953fe5c202ea8a9ccf9f6713c5bd0718eba286fd"},
- {file = "simplejson-3.19.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:febffa5b1eda6622d44b245b0685aff6fb555ce0ed734e2d7b1c3acd018a2cff"},
- {file = "simplejson-3.19.2-cp39-cp39-win32.whl", hash = "sha256:4edcd0bf70087b244ba77038db23cd98a1ace2f91b4a3ecef22036314d77ac23"},
- {file = "simplejson-3.19.2-cp39-cp39-win_amd64.whl", hash = "sha256:aad7405c033d32c751d98d3a65801e2797ae77fac284a539f6c3a3e13005edc4"},
- {file = "simplejson-3.19.2-py3-none-any.whl", hash = "sha256:bcedf4cae0d47839fee7de344f96b5694ca53c786f28b5f773d4f0b265a159eb"},
- {file = "simplejson-3.19.2.tar.gz", hash = "sha256:9eb442a2442ce417801c912df68e1f6ccfcd41577ae7274953ab3ad24ef7d82c"},
-]
-
-[[package]]
-name = "six"
-version = "1.16.0"
-description = "Python 2 and 3 compatibility utilities"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-files = [
- {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
- {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
-]
-
-[[package]]
-name = "sniffio"
-version = "1.3.0"
-description = "Sniff out which async library your code is running under"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"},
- {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"},
-]
-
-[[package]]
-name = "snowflake-connector-python"
-version = "3.12.3"
-description = "Snowflake Connector for Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "snowflake_connector_python-3.12.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:497a096fc379ef0846b2f1cf11a8d7620f0d090f08a77d9e93473845014d57d1"},
- {file = "snowflake_connector_python-3.12.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:055c5808d524497213e4cc9ae91ec3e46cb8342b314e78bc3e139d733dc16741"},
- {file = "snowflake_connector_python-3.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a5dc512d62ef693041ed2ad82931231caddc16e14ffc2842da3e3dd4240b83d"},
- {file = "snowflake_connector_python-3.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a46448f7279d444084eb84a9cddea67662e80ccfaddf41713b9e9aab2b1242e9"},
- {file = "snowflake_connector_python-3.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:821b774b77129ce9f03729456ac1f21d69fedb50e5ce957178131c7bb3d8279f"},
- {file = "snowflake_connector_python-3.12.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82290134978d11628026b447052219ce8d880e36937204f1f0332dfc3f2e92e9"},
- {file = "snowflake_connector_python-3.12.3-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:20b5c8000ee9cee11b0f9a6ae26640f0d498ce77f7e2ec649a2f0d306523792d"},
- {file = "snowflake_connector_python-3.12.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca6500d16bdbd37da88e589cc3e82b90272471d3aabfe4a79ec1cf4696675acf"},
- {file = "snowflake_connector_python-3.12.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b455ba117a68da436e253899674fae1a93669eaefdde8a903c03eb65b7e87c86"},
- {file = "snowflake_connector_python-3.12.3-cp311-cp311-win_amd64.whl", hash = "sha256:205219fcaeee2d33db5d0d023d60518e3bd8272ce1679be2199d7f362d255054"},
- {file = "snowflake_connector_python-3.12.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d830ca32c864b730cba5d92900d850752199635c4fb0ae0a70ee677f62aee70"},
- {file = "snowflake_connector_python-3.12.3-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:597b0c74ec57ba693191ae2de8db9536e349ee32cab152df657473e498b6fd87"},
- {file = "snowflake_connector_python-3.12.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2215d8a4c5e25ea0d2183fe693c3fdf058cd6035e5c84710d532dc04ab4ffd31"},
- {file = "snowflake_connector_python-3.12.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ba9c261904c1ba7cae6035c7881224cf979da39c8b7c7cb10236fdfc57e505"},
- {file = "snowflake_connector_python-3.12.3-cp312-cp312-win_amd64.whl", hash = "sha256:f0d0fcb948ef0812ab162ec9767622f345554043a07439c0c1a9474c86772320"},
- {file = "snowflake_connector_python-3.12.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fe742a0b2fb1c79a21e95b97c49a05783bc00314d1184d227c5fe5b57688af12"},
- {file = "snowflake_connector_python-3.12.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:a8584a44a6bb41d2056cf1b833e629c76e28c5303d2c875c1a23bda46a1cd43a"},
- {file = "snowflake_connector_python-3.12.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd990db8e4886c32ba5c63758e8dc4814e2e75f5fd3fe79d43f7e5ee0fc46793"},
- {file = "snowflake_connector_python-3.12.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4fe7f91f6e44bda877e77403a586d7487ca2c52dc1a32a705b2fea33f9c763a"},
- {file = "snowflake_connector_python-3.12.3-cp38-cp38-win_amd64.whl", hash = "sha256:4994e95eff593dc44c28243ef0ae8d27b8b1aeb96dd64cbcea5bcf0e4dfb77fb"},
- {file = "snowflake_connector_python-3.12.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ac33a7dd54b35f94c4b91369971dbd6467a914dff4b01c46e77e7e6901d7eca4"},
- {file = "snowflake_connector_python-3.12.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a26876322811fe2b93f6d814dcfe016f1df680a12624026ecf57a6bcdf20f969"},
- {file = "snowflake_connector_python-3.12.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0bb390be2e15b6b7cccab7fbe1ef94e1e9ab13790c974aa44761298cdc2641"},
- {file = "snowflake_connector_python-3.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7340f73af4ae72e6af8fe28a1b8e196a0c99943071afc96ce419efb4da80035"},
- {file = "snowflake_connector_python-3.12.3-cp39-cp39-win_amd64.whl", hash = "sha256:c314749bd0151218b654a7d4646a39067ab650bdc86dfebb1884b056b0bdb4b4"},
- {file = "snowflake_connector_python-3.12.3.tar.gz", hash = "sha256:02873c7f7a3b10322e28dddc2be6907f8ab8ecad93d6d6af14c77c2f53091b88"},
-]
-
-[package.dependencies]
-asn1crypto = ">0.24.0,<2.0.0"
-certifi = ">=2017.4.17"
-cffi = ">=1.9,<2.0.0"
-charset-normalizer = ">=2,<4"
-cryptography = ">=3.1.0"
-filelock = ">=3.5,<4"
-idna = ">=2.5,<4"
-packaging = "*"
-platformdirs = ">=2.6.0,<5.0.0"
-pyjwt = "<3.0.0"
-pyOpenSSL = ">=16.2.0,<25.0.0"
-pytz = "*"
-requests = "<3.0.0"
-sortedcontainers = ">=2.4.0"
-tomlkit = "*"
-typing-extensions = ">=4.3,<5"
-urllib3 = {version = ">=1.21.1,<2.0.0", markers = "python_version < \"3.10\""}
-
-[package.extras]
-development = ["Cython", "coverage", "more-itertools", "numpy (<1.27.0)", "pendulum (!=2.1.1)", "pexpect", "pytest (<7.5.0)", "pytest-cov", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist", "pytzdata"]
-pandas = ["pandas (>=1.0.0,<3.0.0)", "pyarrow"]
-secure-local-storage = ["keyring (>=23.1.0,<26.0.0)"]
-
-[[package]]
-name = "sortedcontainers"
-version = "2.4.0"
-description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
-optional = false
-python-versions = "*"
-files = [
- {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
- {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
-]
-
-[[package]]
-name = "sqlalchemy"
-version = "1.3.24"
-description = "Database Abstraction Library"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "SQLAlchemy-1.3.24-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:87a2725ad7d41cd7376373c15fd8bf674e9c33ca56d0b8036add2d634dba372e"},
- {file = "SQLAlchemy-1.3.24-cp27-cp27m-win32.whl", hash = "sha256:f597a243b8550a3a0b15122b14e49d8a7e622ba1c9d29776af741f1845478d79"},
- {file = "SQLAlchemy-1.3.24-cp27-cp27m-win_amd64.whl", hash = "sha256:fc4cddb0b474b12ed7bdce6be1b9edc65352e8ce66bc10ff8cbbfb3d4047dbf4"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:f1149d6e5c49d069163e58a3196865e4321bad1803d7886e07d8710de392c548"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:14f0eb5db872c231b20c18b1e5806352723a3a89fb4254af3b3e14f22eaaec75"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:e98d09f487267f1e8d1179bf3b9d7709b30a916491997137dd24d6ae44d18d79"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:fc1f2a5a5963e2e73bac4926bdaf7790c4d7d77e8fc0590817880e22dd9d0b8b"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-win32.whl", hash = "sha256:f3c5c52f7cb8b84bfaaf22d82cb9e6e9a8297f7c2ed14d806a0f5e4d22e83fb7"},
- {file = "SQLAlchemy-1.3.24-cp35-cp35m-win_amd64.whl", hash = "sha256:0352db1befcbed2f9282e72843f1963860bf0e0472a4fa5cf8ee084318e0e6ab"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:2ed6343b625b16bcb63c5b10523fd15ed8934e1ed0f772c534985e9f5e73d894"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:34fcec18f6e4b24b4a5f6185205a04f1eab1e56f8f1d028a2a03694ebcc2ddd4"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:e47e257ba5934550d7235665eee6c911dc7178419b614ba9e1fbb1ce6325b14f"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:816de75418ea0953b5eb7b8a74933ee5a46719491cd2b16f718afc4b291a9658"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-win32.whl", hash = "sha256:26155ea7a243cbf23287f390dba13d7927ffa1586d3208e0e8d615d0c506f996"},
- {file = "SQLAlchemy-1.3.24-cp36-cp36m-win_amd64.whl", hash = "sha256:f03bd97650d2e42710fbe4cf8a59fae657f191df851fc9fc683ecef10746a375"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a006d05d9aa052657ee3e4dc92544faae5fcbaafc6128217310945610d862d39"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1e2f89d2e5e3c7a88e25a3b0e43626dba8db2aa700253023b82e630d12b37109"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0d5d862b1cfbec5028ce1ecac06a3b42bc7703eb80e4b53fceb2738724311443"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:0172423a27fbcae3751ef016663b72e1a516777de324a76e30efa170dbd3dd2d"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-win32.whl", hash = "sha256:d37843fb8df90376e9e91336724d78a32b988d3d20ab6656da4eb8ee3a45b63c"},
- {file = "SQLAlchemy-1.3.24-cp37-cp37m-win_amd64.whl", hash = "sha256:c10ff6112d119f82b1618b6dc28126798481b9355d8748b64b9b55051eb4f01b"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:861e459b0e97673af6cc5e7f597035c2e3acdfb2608132665406cded25ba64c7"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5de2464c254380d8a6c20a2746614d5a436260be1507491442cf1088e59430d2"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d375d8ccd3cebae8d90270f7aa8532fe05908f79e78ae489068f3b4eee5994e8"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:014ea143572fee1c18322b7908140ad23b3994036ef4c0d630110faf942652f8"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-win32.whl", hash = "sha256:6607ae6cd3a07f8a4c3198ffbf256c261661965742e2b5265a77cd5c679c9bba"},
- {file = "SQLAlchemy-1.3.24-cp38-cp38-win_amd64.whl", hash = "sha256:fcb251305fa24a490b6a9ee2180e5f8252915fb778d3dafc70f9cc3f863827b9"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:01aa5f803db724447c1d423ed583e42bf5264c597fd55e4add4301f163b0be48"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4d0e3515ef98aa4f0dc289ff2eebb0ece6260bbf37c2ea2022aad63797eacf60"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:bce28277f308db43a6b4965734366f533b3ff009571ec7ffa583cb77539b84d6"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:8110e6c414d3efc574543109ee618fe2c1f96fa31833a1ff36cc34e968c4f233"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-win32.whl", hash = "sha256:ee5f5188edb20a29c1cc4a039b074fdc5575337c9a68f3063449ab47757bb064"},
- {file = "SQLAlchemy-1.3.24-cp39-cp39-win_amd64.whl", hash = "sha256:09083c2487ca3c0865dc588e07aeaa25416da3d95f7482c07e92f47e080aa17b"},
- {file = "SQLAlchemy-1.3.24.tar.gz", hash = "sha256:ebbb777cbf9312359b897bf81ba00dae0f5cb69fba2a18265dcc18a6f5ef7519"},
-]
-
-[package.extras]
-mssql = ["pyodbc"]
-mssql-pymssql = ["pymssql"]
-mssql-pyodbc = ["pyodbc"]
-mysql = ["mysqlclient"]
-oracle = ["cx-oracle"]
-postgresql = ["psycopg2"]
-postgresql-pg8000 = ["pg8000 (<1.16.6)"]
-postgresql-psycopg2binary = ["psycopg2-binary"]
-postgresql-psycopg2cffi = ["psycopg2cffi"]
-pymysql = ["pymysql", "pymysql (<1)"]
-
-[[package]]
-name = "sqlalchemy-searchable"
-version = "1.2.0"
-description = "Provides fulltext search capabilities for declarative SQLAlchemy models."
-optional = false
-python-versions = "*"
-files = [
- {file = "SQLAlchemy-Searchable-1.2.0.tar.gz", hash = "sha256:597de9d1356e8a0a8b3be7be892adee422e7419603f25c40a7ab5c16bd75f77d"},
-]
-
-[package.dependencies]
-SQLAlchemy = ">=0.9.0"
-SQLAlchemy-Utils = ">=0.29.0"
-validators = ">=0.3.0"
-
-[package.extras]
-test = ["flake8 (>=2.4.0)", "isort (>=3.9.6)", "psycopg2 (>=2.4.6)", "pytest (>=2.2.3)"]
-
-[[package]]
-name = "sqlalchemy-utils"
-version = "0.38.3"
-description = "Various utility functions for SQLAlchemy."
-optional = false
-python-versions = "~=3.6"
-files = [
- {file = "SQLAlchemy-Utils-0.38.3.tar.gz", hash = "sha256:9f9afba607a40455cf703adfa9846584bf26168a0c5a60a70063b70d65051f4d"},
- {file = "SQLAlchemy_Utils-0.38.3-py3-none-any.whl", hash = "sha256:5c13b5d08adfaa85f3d4e8ec09a75136216fad41346980d02974a70a77988bf9"},
-]
-
-[package.dependencies]
-SQLAlchemy = ">=1.3"
-
-[package.extras]
-arrow = ["arrow (>=0.3.4)"]
-babel = ["Babel (>=1.3)"]
-color = ["colour (>=0.0.4)"]
-encrypted = ["cryptography (>=0.6)"]
-intervals = ["intervals (>=0.7.1)"]
-password = ["passlib (>=1.6,<2.0)"]
-pendulum = ["pendulum (>=2.0.5)"]
-phone = ["phonenumbers (>=5.9.2)"]
-test = ["Jinja2 (>=2.3)", "Pygments (>=1.2)", "backports.zoneinfo", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "isort (>=4.2.2)", "pg8000 (>=1.12.4)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil (>=2.6)", "pytz (>=2014.2)"]
-test-all = ["Babel (>=1.3)", "Jinja2 (>=2.3)", "Pygments (>=1.2)", "arrow (>=0.3.4)", "backports.zoneinfo", "colour (>=0.0.4)", "cryptography (>=0.6)", "docutils (>=0.10)", "flake8 (>=2.4.0)", "flexmock (>=0.9.7)", "furl (>=0.4.1)", "intervals (>=0.7.1)", "isort (>=4.2.2)", "passlib (>=1.6,<2.0)", "pendulum (>=2.0.5)", "pg8000 (>=1.12.4)", "phonenumbers (>=5.9.2)", "psycopg2 (>=2.5.1)", "psycopg2cffi (>=2.8.1)", "pymysql", "pyodbc", "pytest (>=2.7.1)", "python-dateutil", "python-dateutil (>=2.6)", "pytz (>=2014.2)"]
-timezone = ["python-dateutil"]
-url = ["furl (>=0.4.1)"]
-
-[[package]]
-name = "sqlparse"
-version = "0.5.0"
-description = "A non-validating SQL parser."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "sqlparse-0.5.0-py3-none-any.whl", hash = "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663"},
- {file = "sqlparse-0.5.0.tar.gz", hash = "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93"},
-]
-
-[package.extras]
-dev = ["build", "hatch"]
-doc = ["sphinx"]
-
-[[package]]
-name = "sshtunnel"
-version = "0.1.5"
-description = "Pure python SSH tunnels"
-optional = false
-python-versions = "*"
-files = [
- {file = "sshtunnel-0.1.5-py2.py3-none-any.whl", hash = "sha256:5eee2e414c3fd9e9ef5d058bebece272a6aae928849ef7f2d9561b7fffab7aea"},
- {file = "sshtunnel-0.1.5.tar.gz", hash = "sha256:c813fdcda8e81c3936ffeac47cb69cfb2d1f5e77ad0de656c6dab56aeebd9249"},
-]
-
-[package.dependencies]
-paramiko = ">=1.15.2"
-
-[package.extras]
-build-sphinx = ["sphinx", "sphinxcontrib-napoleon"]
-dev = ["check-manifest"]
-test = ["tox (>=1.8.1)"]
-
-[[package]]
-name = "statsd"
-version = "3.3.0"
-description = "A simple statsd client."
-optional = false
-python-versions = "*"
-files = [
- {file = "statsd-3.3.0-py2.py3-none-any.whl", hash = "sha256:c610fb80347fca0ef62666d241bce64184bd7cc1efe582f9690e045c25535eaa"},
- {file = "statsd-3.3.0.tar.gz", hash = "sha256:e3e6db4c246f7c59003e51c9720a51a7f39a396541cb9b147ff4b14d15b5dd1f"},
-]
-
-[[package]]
-name = "supervisor"
-version = "4.1.0"
-description = "A system for controlling process state under UNIX"
-optional = false
-python-versions = "*"
-files = [
- {file = "supervisor-4.1.0-py2.py3-none-any.whl", hash = "sha256:a76b2f77a560f2dc411c0254a4eb15f555e99faac48621b0f1fc9ab013944f47"},
- {file = "supervisor-4.1.0.tar.gz", hash = "sha256:2dc86fe0476e945e61483d614ceb2cf4f93b95282eb243bdf792621994360383"},
-]
-
-[package.extras]
-testing = ["mock", "pytest", "pytest-cov"]
-
-[[package]]
-name = "supervisor-checks"
-version = "0.8.1"
-description = "Framework to build health checks for Supervisor-based services."
-optional = false
-python-versions = "*"
-files = [
- {file = "supervisor_checks-0.8.1.tar.gz", hash = "sha256:1474150aed0acdea726cc9ffdf6b728e2ed8aa8ef89d8d979cd2fb8f4444d987"},
-]
-
-[package.dependencies]
-psutil = "*"
-
-[package.extras]
-test = ["psutil"]
-
-[[package]]
-name = "td-client"
-version = "1.0.0"
-description = "Treasure Data API library for Python"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "td-client-1.0.0.tar.gz", hash = "sha256:48842cb4b22dd7fece4e1d2593985b9a151796cfff504bf2de24d04dafbea1ab"},
- {file = "td_client-1.0.0-py3-none-any.whl", hash = "sha256:017dc6d21eb44e8ef5f3a9e0e313a37760e83dc0df3684b49d29cb73fdac89d6"},
-]
-
-[package.dependencies]
-msgpack = ">=0.5.2"
-python-dateutil = "*"
-urllib3 = "*"
-
-[package.extras]
-dev = ["black (==19.3b0)", "isort"]
-docs = ["sphinx", "sphinx-rtd-theme"]
-
-[[package]]
-name = "tenacity"
-version = "8.2.3"
-description = "Retry code until it succeeds"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
- {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
-]
-
-[package.extras]
-doc = ["reno", "sphinx", "tornado (>=4.5)"]
-
-[[package]]
-name = "thrift"
-version = "0.16.0"
-description = "Python bindings for the Apache Thrift RPC system"
-optional = false
-python-versions = "*"
-files = [
- {file = "thrift-0.16.0.tar.gz", hash = "sha256:2b5b6488fcded21f9d312aa23c9ff6a0195d0f6ae26ddbd5ad9e3e25dfc14408"},
-]
-
-[package.dependencies]
-six = ">=1.7.2"
-
-[package.extras]
-all = ["tornado (>=4.0)", "twisted"]
-tornado = ["tornado (>=4.0)"]
-twisted = ["twisted"]
-
-[[package]]
-name = "thrift-sasl"
-version = "0.4.3"
-description = "Thrift SASL Python module that implements SASL transports for Thrift (`TSaslClientTransport`)."
-optional = false
-python-versions = "*"
-files = [
- {file = "thrift_sasl-0.4.3-py2.py3-none-any.whl", hash = "sha256:d24b49140115e6e2a96d08335cff225a27a28ea71866fb1b2bdb30ca5afca64e"},
- {file = "thrift_sasl-0.4.3.tar.gz", hash = "sha256:5bdd5b760d90a13d9b3abfce873db0425861aa8d6bf25912d3cc0467a4f773da"},
-]
-
-[package.dependencies]
-pure-sasl = ">=0.6.2"
-six = ">=1.13.0"
-thrift = {version = ">=0.10.0", markers = "python_version >= \"3.0\""}
-
-[[package]]
-name = "thriftpy2"
-version = "0.4.17"
-description = "Pure python implementation of Apache Thrift."
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-files = [
- {file = "thriftpy2-0.4.17-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:9692cbf2025e1e20d0cb30d759589c8c13f4fa6806a38af13970c28a3bb8a2f9"},
- {file = "thriftpy2-0.4.17.tar.gz", hash = "sha256:190f35c32da9146d1fdd822f46b6a0ad543572ea405ca6853b4ec7b128efbc0d"},
-]
-
-[package.dependencies]
-ply = ">=3.4,<4.0"
-six = ">=1.15,<2.0"
-
-[package.extras]
-dev = ["flake8 (>=2.5)", "pytest (>=2.8)", "pytest (>=6.1.1)", "sphinx (>=1.3)", "sphinx-rtd-theme (>=0.1.9)", "tornado (>=4.0,<6.0)"]
-tornado = ["tornado (>=4.0,<6.0)"]
-
-[[package]]
-name = "tomli"
-version = "2.0.1"
-description = "A lil' TOML parser"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
- {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
-]
-
-[[package]]
-name = "tomlkit"
-version = "0.13.0"
-description = "Style preserving TOML library"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "tomlkit-0.13.0-py3-none-any.whl", hash = "sha256:7075d3042d03b80f603482d69bf0c8f345c2b30e41699fd8883227f89972b264"},
- {file = "tomlkit-0.13.0.tar.gz", hash = "sha256:08ad192699734149f5b97b45f1f18dad7eb1b6d16bc72ad0c2335772650d7b72"},
-]
-
-[[package]]
-name = "trino"
-version = "0.327.0"
-description = "Client for the Trino distributed SQL Engine"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "trino-0.327.0-py3-none-any.whl", hash = "sha256:56d253a814bd5da545cc68e1bc8c28c0b80f07df6411aa0424197c025c78998e"},
- {file = "trino-0.327.0.tar.gz", hash = "sha256:07370044158cb95f6f6b03720a1afb8980b75092a59025fe602af9858c2fd4a0"},
-]
-
-[package.dependencies]
-"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""}
-python-dateutil = "*"
-pytz = "*"
-requests = ">=2.31.0"
-tzlocal = "*"
-
-[package.extras]
-all = ["requests-kerberos", "sqlalchemy (>=1.3)"]
-external-authentication-token-cache = ["keyring"]
-kerberos = ["requests-kerberos"]
-sqlalchemy = ["sqlalchemy (>=1.3)"]
-tests = ["black", "httpretty (<1.1)", "isort", "pre-commit", "pytest", "pytest-runner", "requests-kerberos", "sqlalchemy (>=1.3)"]
-
-[[package]]
-name = "typing-extensions"
-version = "4.9.0"
-description = "Backported and Experimental Type Hints for Python 3.8+"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
- {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
-]
-
-[[package]]
-name = "tzdata"
-version = "2023.4"
-description = "Provider of IANA time zone data"
-optional = false
-python-versions = ">=2"
-files = [
- {file = "tzdata-2023.4-py2.py3-none-any.whl", hash = "sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3"},
- {file = "tzdata-2023.4.tar.gz", hash = "sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9"},
-]
-
-[[package]]
-name = "tzlocal"
-version = "4.3.1"
-description = "tzinfo object for the local timezone"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "tzlocal-4.3.1-py3-none-any.whl", hash = "sha256:67d7e7f4ce0a98e9dfde2e02474c60fe846ed032d78b555c554c2e9cba472d84"},
- {file = "tzlocal-4.3.1.tar.gz", hash = "sha256:ee32ef8c20803c19a96ed366addd3d4a729ef6309cb5c7359a0cc2eeeb7fa46a"},
-]
-
-[package.dependencies]
-"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""}
-pytz-deprecation-shim = "*"
-tzdata = {version = "*", markers = "platform_system == \"Windows\""}
-
-[package.extras]
-devenv = ["black", "check-manifest", "flake8", "pyroma", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"]
-
-[[package]]
-name = "ua-parser"
-version = "0.18.0"
-description = "Python port of Browserscope's user agent parser"
-optional = false
-python-versions = "*"
-files = [
- {file = "ua-parser-0.18.0.tar.gz", hash = "sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e"},
- {file = "ua_parser-0.18.0-py2.py3-none-any.whl", hash = "sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950"},
-]
-
-[[package]]
-name = "uritemplate"
-version = "3.0.1"
-description = "URI templates"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"},
- {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"},
-]
-
-[[package]]
-name = "urllib3"
-version = "1.26.19"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-files = [
- {file = "urllib3-1.26.19-py2.py3-none-any.whl", hash = "sha256:37a0344459b199fce0e80b0d3569837ec6b6937435c5244e7fd73fa6006830f3"},
- {file = "urllib3-1.26.19.tar.gz", hash = "sha256:3e3d753a8618b86d7de333b4223005f68720bcd6a7d2bcb9fbd2229ec7c1e429"},
-]
-
-[package.extras]
-brotli = ["brotli (==1.0.9)", "brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
-secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
-
-[[package]]
-name = "user-agents"
-version = "2.0"
-description = "A library to identify devices (phones, tablets) and their capabilities by parsing (browser/HTTP) user agent strings"
-optional = false
-python-versions = "*"
-files = [
- {file = "user-agents-2.0.tar.gz", hash = "sha256:792869b990a244f71efea1cb410ecaba99a270a64c5ac37d365bde5d70d6a2fa"},
- {file = "user_agents-2.0-py2-none-any.whl", hash = "sha256:7af7419d61ce8f72ad487cc77ea3b4d1dcaa4cbb7b6df533a7c7aa3ccc44e5b3"},
-]
-
-[package.dependencies]
-ua-parser = ">=0.8.0"
-
-[[package]]
-name = "validators"
-version = "0.22.0"
-description = "Python Data Validation for Humansâą"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "validators-0.22.0-py3-none-any.whl", hash = "sha256:61cf7d4a62bbae559f2e54aed3b000cea9ff3e2fdbe463f51179b92c58c9585a"},
- {file = "validators-0.22.0.tar.gz", hash = "sha256:77b2689b172eeeb600d9605ab86194641670cdb73b60afd577142a9397873370"},
-]
-
-[package.extras]
-docs-offline = ["myst-parser (>=2.0.0)", "pypandoc-binary (>=1.11)", "sphinx (>=7.1.1)"]
-docs-online = ["mkdocs (>=1.5.2)", "mkdocs-git-revision-date-localized-plugin (>=1.2.0)", "mkdocs-material (>=9.2.6)", "mkdocstrings[python] (>=0.22.0)", "pyaml (>=23.7.0)"]
-hooks = ["pre-commit (>=3.3.3)"]
-package = ["build (>=1.0.0)", "twine (>=4.0.2)"]
-runner = ["tox (>=4.11.1)"]
-sast = ["bandit[toml] (>=1.7.5)"]
-testing = ["pytest (>=7.4.0)"]
-tooling = ["black (>=23.7.0)", "pyright (>=1.1.325)", "ruff (>=0.0.287)"]
-tooling-extras = ["pyaml (>=23.7.0)", "pypandoc-binary (>=1.11)", "pytest (>=7.4.0)"]
-
-[[package]]
-name = "vertica-python"
-version = "1.1.1"
-description = "Official native Python client for the Vertica database."
-optional = false
-python-versions = "*"
-files = [
- {file = "vertica-python-1.1.1.tar.gz", hash = "sha256:dedf56d76b67673b4d57a13f7f96ebdc57b39ea650b93ebf0c05eb6d1d2c0c05"},
- {file = "vertica_python-1.1.1-py2.py3-none-any.whl", hash = "sha256:63d300832d6fe471987880f06a9590eafc46a1f896860881270f6b6645f3bec6"},
-]
-
-[package.dependencies]
-python-dateutil = ">=1.5"
-six = ">=1.10.0"
-
-[[package]]
-name = "virtualenv"
-version = "20.26.6"
-description = "Virtual Python Environment builder"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"},
- {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"},
-]
-
-[package.dependencies]
-distlib = ">=0.3.7,<1"
-filelock = ">=3.12.2,<4"
-platformdirs = ">=3.9.1,<5"
-
-[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
-
-[[package]]
-name = "watchdog"
-version = "3.0.0"
-description = "Filesystem events monitoring"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"},
- {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"},
- {file = "watchdog-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96"},
- {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae"},
- {file = "watchdog-3.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9"},
- {file = "watchdog-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7"},
- {file = "watchdog-3.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674"},
- {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f"},
- {file = "watchdog-3.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc"},
- {file = "watchdog-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3"},
- {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3"},
- {file = "watchdog-3.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0"},
- {file = "watchdog-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8"},
- {file = "watchdog-3.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100"},
- {file = "watchdog-3.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346"},
- {file = "watchdog-3.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d"},
- {file = "watchdog-3.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33"},
- {file = "watchdog-3.0.0-py3-none-win32.whl", hash = "sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f"},
- {file = "watchdog-3.0.0-py3-none-win_amd64.whl", hash = "sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c"},
- {file = "watchdog-3.0.0-py3-none-win_ia64.whl", hash = "sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759"},
- {file = "watchdog-3.0.0.tar.gz", hash = "sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9"},
-]
-
-[package.extras]
-watchmedo = ["PyYAML (>=3.10)"]
-
-[[package]]
-name = "wcwidth"
-version = "0.2.13"
-description = "Measures the displayed width of unicode strings in a terminal"
-optional = false
-python-versions = "*"
-files = [
- {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
- {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
-]
-
-[[package]]
-name = "websocket-client"
-version = "1.7.0"
-description = "WebSocket client for Python with low level API options"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "websocket-client-1.7.0.tar.gz", hash = "sha256:10e511ea3a8c744631d3bd77e61eb17ed09304c413ad42cf6ddfa4c7787e8fe6"},
- {file = "websocket_client-1.7.0-py3-none-any.whl", hash = "sha256:f4c3d22fec12a2461427a29957ff07d35098ee2d976d3ba244e688b8b4057588"},
-]
-
-[package.extras]
-docs = ["Sphinx (>=6.0)", "sphinx-rtd-theme (>=1.1.0)"]
-optional = ["python-socks", "wsaccel"]
-test = ["websockets"]
-
-[[package]]
-name = "werkzeug"
-version = "2.3.8"
-description = "The comprehensive WSGI web application library."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "werkzeug-2.3.8-py3-none-any.whl", hash = "sha256:bba1f19f8ec89d4d607a3bd62f1904bd2e609472d93cd85e9d4e178f472c3748"},
- {file = "werkzeug-2.3.8.tar.gz", hash = "sha256:554b257c74bbeb7a0d254160a4f8ffe185243f52a52035060b761ca62d977f03"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=2.1.1"
-
-[package.extras]
-watchdog = ["watchdog (>=2.3)"]
-
-[[package]]
-name = "wrapt"
-version = "1.16.0"
-description = "Module for decorators, wrappers and monkey patching."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"},
- {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"},
- {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"},
- {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"},
- {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"},
- {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"},
- {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"},
- {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"},
- {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"},
- {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"},
- {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"},
- {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"},
- {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"},
- {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"},
- {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"},
- {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"},
- {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"},
- {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"},
- {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"},
- {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"},
- {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"},
- {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"},
- {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"},
- {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"},
- {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"},
- {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"},
- {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"},
- {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"},
- {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"},
- {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"},
- {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"},
- {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"},
- {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"},
- {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"},
- {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"},
- {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"},
- {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"},
- {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"},
- {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"},
- {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"},
- {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"},
- {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"},
-]
-
-[[package]]
-name = "wraptor"
-version = "0.7.0"
-description = "Useful decorators and other utility functions."
-optional = false
-python-versions = "*"
-files = [
- {file = "Wraptor-0.7.0.tar.gz", hash = "sha256:d61182866a061fb29b7ec426db281cc9a15540766885136f35809f079d9c1dec"},
-]
-
-[[package]]
-name = "wtforms"
-version = "2.2.1"
-description = "A flexible forms validation and rendering library for Python web development."
-optional = false
-python-versions = "*"
-files = [
- {file = "WTForms-2.2.1-py2.py3-none-any.whl", hash = "sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1"},
- {file = "WTForms-2.2.1.tar.gz", hash = "sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61"},
-]
-
-[package.extras]
-locale = ["Babel (>=1.3)"]
-
-[[package]]
-name = "xlrd"
-version = "2.0.1"
-description = "Library for developers to extract data from Microsoft Excel (tm) .xls spreadsheet files"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
-files = [
- {file = "xlrd-2.0.1-py2.py3-none-any.whl", hash = "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd"},
- {file = "xlrd-2.0.1.tar.gz", hash = "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88"},
-]
-
-[package.extras]
-build = ["twine", "wheel"]
-docs = ["sphinx"]
-test = ["pytest", "pytest-cov"]
-
-[[package]]
-name = "xlsxwriter"
-version = "1.2.2"
-description = "A Python module for creating Excel XLSX files."
-optional = false
-python-versions = "*"
-files = [
- {file = "XlsxWriter-1.2.2-py2.py3-none-any.whl", hash = "sha256:00e9c337589ec67a69f1220f47409146ab1affd8eb1e8eaad23f35685bd23e47"},
- {file = "XlsxWriter-1.2.2.tar.gz", hash = "sha256:5a5e2195a4672d17db79839bbdf1006a521adb57eaceea1c335ae4b3d19f088f"},
-]
-
-[[package]]
-name = "xmlschema"
-version = "3.0.1"
-description = "An XML Schema validator and decoder"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "xmlschema-3.0.1-py3-none-any.whl", hash = "sha256:116243b2ad38cd2df9ee0606d4e4e898a6f156736b39ab0017e6f49862c0809e"},
- {file = "xmlschema-3.0.1.tar.gz", hash = "sha256:bb24a5f4738e49d85d9eb03a2b5af26bbbbfdb055517ad953d98925094b8c026"},
-]
-
-[package.dependencies]
-elementpath = ">=4.1.5,<5.0.0"
-
-[package.extras]
-codegen = ["elementpath (>=4.1.5,<5.0.0)", "jinja2"]
-dev = ["Sphinx", "coverage", "elementpath (>=4.1.5,<5.0.0)", "flake8", "jinja2", "lxml", "lxml-stubs", "memory-profiler", "mypy", "sphinx-rtd-theme", "tox"]
-docs = ["Sphinx", "elementpath (>=4.1.5,<5.0.0)", "jinja2", "sphinx-rtd-theme"]
-
-[[package]]
-name = "zipp"
-version = "3.19.1"
-description = "Backport of pathlib-compatible object wrapper for zip files"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "zipp-3.19.1-py3-none-any.whl", hash = "sha256:2828e64edb5386ea6a52e7ba7cdb17bb30a73a858f5eb6eb93d8d36f5ea26091"},
- {file = "zipp-3.19.1.tar.gz", hash = "sha256:35427f6d5594f4acf82d25541438348c26736fa9b3afa2754bcd63cdb99d8e8f"},
-]
-
-[package.extras]
-doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
-test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"]
-
-[[package]]
-name = "zope-event"
-version = "5.0"
-description = "Very basic event publishing system"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"},
- {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"},
-]
-
-[package.dependencies]
-setuptools = "*"
-
-[package.extras]
-docs = ["Sphinx"]
-test = ["zope.testrunner"]
-
-[[package]]
-name = "zope-interface"
-version = "6.1"
-description = "Interfaces for Python"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb"},
- {file = "zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92"},
- {file = "zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3"},
- {file = "zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd"},
- {file = "zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41"},
- {file = "zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f"},
- {file = "zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1"},
- {file = "zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736"},
- {file = "zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605"},
- {file = "zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8"},
- {file = "zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de"},
- {file = "zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1"},
- {file = "zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a"},
- {file = "zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7"},
- {file = "zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d"},
- {file = "zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff"},
- {file = "zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0"},
- {file = "zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b"},
- {file = "zope.interface-6.1-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d"},
- {file = "zope.interface-6.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c"},
- {file = "zope.interface-6.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83"},
- {file = "zope.interface-6.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379"},
- {file = "zope.interface-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9"},
- {file = "zope.interface-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f"},
- {file = "zope.interface-6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1"},
- {file = "zope.interface-6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56"},
- {file = "zope.interface-6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b"},
- {file = "zope.interface-6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43"},
- {file = "zope.interface-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d"},
- {file = "zope.interface-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179"},
- {file = "zope.interface-6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941"},
- {file = "zope.interface-6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3"},
- {file = "zope.interface-6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d"},
- {file = "zope.interface-6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac"},
- {file = "zope.interface-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40"},
- {file = "zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309"},
-]
-
-[package.dependencies]
-setuptools = "*"
-
-[package.extras]
-docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"]
-test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
-testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
-
-[metadata]
-lock-version = "2.0"
-python-versions = ">=3.8,<3.11"
-content-hash = "93b13c8a960e148463fba93cfd826c0f3e7bd822bbda55af7ba708baead293df"
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 24f51f3ef0..0000000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,178 +0,0 @@
-[project]
-requires-python = ">=3.8"
-
-[tool.black]
-target-version = ['py38']
-line-length = 119
-force-exclude = '''
-/(
- migrations
-)/
-'''
-
-[tool.poetry]
-name = "redash"
-version = "25.04.0-dev"
-description = "Make Your Company Data Driven. Connect to any data source, easily visualize, dashboard and share your data."
-authors = ["Arik Fraimovich "]
-# to be added to/removed from the mailing list, please reach out to Arik via the above email or Discord
-maintainers = [
- "Redash maintainers and contributors ",
-]
-readme = "README.md"
-
-[tool.poetry.dependencies]
-python = ">=3.8,<3.11"
-advocate = "1.0.0"
-aniso8601 = "8.0.0"
-authlib = "0.15.5"
-backoff = "2.2.1"
-blinker = "1.6.2"
-click = "8.1.3"
-cryptography = "43.0.1"
-disposable-email-domains = ">=0.0.52"
-flask = "2.3.2"
-flask-limiter = "3.3.1"
-flask-login = "0.6.0"
-flask-mail = "0.9.1"
-flask-migrate = "2.5.2"
-flask-restful = "0.3.10"
-flask-sqlalchemy = "2.5.1"
-flask-talisman = "0.7.0"
-flask-wtf = "1.1.1"
-funcy = "1.13"
-gevent = "23.9.1"
-greenlet = "2.0.2"
-gunicorn = "22.0.0"
-httplib2 = "0.19.0"
-itsdangerous = "2.1.2"
-jinja2 = "3.1.5"
-jsonschema = "3.1.1"
-markupsafe = "2.1.1"
-maxminddb-geolite2 = "2018.703"
-parsedatetime = "2.4"
-passlib = "1.7.3"
-psycopg2-binary = "2.9.6"
-pyjwt = "2.4.0"
-pyopenssl = "24.2.1"
-pypd = "1.1.0"
-pysaml2 = "7.3.1"
-pystache = "0.6.0"
-python-dateutil = "2.8.0"
-python-dotenv = "0.19.2"
-pytz = ">=2019.3"
-pyyaml = "6.0.1"
-redis = "4.6.0"
-regex = "2023.8.8"
-requests = "2.32.3"
-restrictedpython = "7.3"
-rq = "1.16.1"
-rq-scheduler = "0.13.1"
-semver = "2.8.1"
-sentry-sdk = "1.45.1"
-sqlalchemy = "1.3.24"
-sqlalchemy-searchable = "1.2.0"
-sqlalchemy-utils = "0.38.3"
-sqlparse = "0.5.0"
-sshtunnel = "0.1.5"
-statsd = "3.3.0"
-supervisor = "4.1.0"
-supervisor-checks = "0.8.1"
-ua-parser = "0.18.0"
-urllib3 = "1.26.19"
-user-agents = "2.0"
-werkzeug = "2.3.8"
-wtforms = "2.2.1"
-xlsxwriter = "1.2.2"
-tzlocal = "4.3.1"
-pyodbc = "5.1.0"
-debugpy = "^1.8.9"
-paramiko = "3.4.1"
-oracledb = "2.5.1"
-
-[tool.poetry.group.all_ds]
-optional = true
-
-[tool.poetry.group.all_ds.dependencies]
-atsd-client = "3.0.5"
-azure-kusto-data = "0.0.35"
-boto3 = "1.28.8"
-botocore = "1.31.8"
-cassandra-driver = "3.21.0"
-certifi = ">=2019.9.11"
-cmem-cmempy = "21.2.3"
-databend-py = "0.4.6"
-databend-sqlalchemy = "0.2.4"
-google-api-python-client = "1.7.11"
-gspread = "5.11.2"
-impyla = "0.16.0"
-influxdb = "5.2.3"
-influxdb-client = "1.38.0"
-memsql = "3.2.0"
-mysqlclient = "2.1.1"
-nzalchemy = "^11.0.2"
-nzpy = ">=1.15"
-oauth2client = "4.1.3"
-openpyxl = "3.0.7"
-pandas = "1.3.4"
-phoenixdb = "0.7"
-pinotdb = ">=0.4.5"
-protobuf = "3.20.2"
-pyathena = "2.25.2"
-pydgraph = "2.0.2"
-pydruid = "0.5.7"
-pyexasol = "0.12.0"
-pyhive = "0.6.1"
-pyignite = "0.6.1"
-pymongo = { version = "4.6.3", extras = ["srv", "tls"] }
-pymssql = "^2.3.1"
-pyodbc = "5.1.0"
-python-arango = "6.1.0"
-python-rapidjson = "1.20"
-requests-aws-sign = "0.1.5"
-sasl = ">=0.1.3"
-simple-salesforce = "0.74.3"
-snowflake-connector-python = "3.12.3"
-td-client = "1.0.0"
-thrift = ">=0.8.0"
-thrift-sasl = ">=0.1.0"
-trino = ">=0.305,<1.0"
-vertica-python = "1.1.1"
-xlrd = "2.0.1"
-e6data-python-connector = "1.1.9"
-
-[tool.poetry.group.ldap3]
-optional = true
-
-[tool.poetry.group.ldap3.dependencies]
-ldap3 = "2.9.1"
-
-[tool.poetry.group.dev]
-optional = true
-
-[tool.poetry.group.dev.dependencies]
-pytest = "7.4.0"
-coverage = "7.2.7"
-freezegun = "1.2.1"
-jwcrypto = "1.5.6"
-mock = "5.0.2"
-pre-commit = "3.3.3"
-ptpython = "3.0.23"
-pytest-cov = "4.1.0"
-watchdog = "3.0.0"
-ruff = "0.0.289"
-
-[build-system]
-requires = ["poetry-core"]
-build-backend = "poetry.core.masonry.api"
-
-[tool.ruff]
-exclude = [".git", "viz-lib", "node_modules", "migrations"]
-ignore = ["E501"]
-select = ["C9", "E", "F", "W", "I001", "UP004"]
-
-[tool.ruff.mccabe]
-max-complexity = 15
-
-[tool.ruff.per-file-ignores]
-"__init__.py" = ["F401"]
diff --git a/pytest.ini b/pytest.ini
index 0c12c50c8c..fdafc074e5 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,5 +1,2 @@
[pytest]
norecursedirs = *.egg .eggs dist build docs .tox
-filterwarnings =
- once::DeprecationWarning
- once::PendingDeprecationWarning
diff --git a/redash/__init__.py b/redash/__init__.py
index 7e19e2326d..43506ca103 100644
--- a/redash/__init__.py
+++ b/redash/__init__.py
@@ -1,27 +1,27 @@
+from __future__ import absolute_import
import logging
import os
import sys
import redis
-from flask_limiter import Limiter
-from flask_limiter.util import get_remote_address
from flask_mail import Mail
+from flask_limiter import Limiter
+from flask_limiter.util import get_ipaddr
from flask_migrate import Migrate
from statsd import StatsClient
-from redash import settings
-from redash.app import create_app # noqa
-from redash.destinations import import_destinations
-from redash.query_runner import import_query_runners
+from . import settings
+from .app import create_app # noqa
+from .query_runner import import_query_runners
+from .destinations import import_destinations
-__version__ = "25.04.0-dev"
+__version__ = "9.0.0-beta"
if os.environ.get("REMOTE_DEBUG"):
- import debugpy
+ import ptvsd
- debugpy.listen(("0.0.0.0", 5678))
- debugpy.wait_for_client()
+ ptvsd.enable_attach(address=("0.0.0.0", 5678))
def setup_logging():
@@ -47,9 +47,11 @@ def setup_logging():
redis_connection = redis.from_url(settings.REDIS_URL)
rq_redis_connection = redis.from_url(settings.RQ_REDIS_URL)
mail = Mail()
-migrate = Migrate(compare_type=True)
-statsd_client = StatsClient(host=settings.STATSD_HOST, port=settings.STATSD_PORT, prefix=settings.STATSD_PREFIX)
-limiter = Limiter(key_func=get_remote_address, storage_uri=settings.LIMITER_STORAGE)
+migrate = Migrate()
+statsd_client = StatsClient(
+ host=settings.STATSD_HOST, port=settings.STATSD_PORT, prefix=settings.STATSD_PREFIX
+)
+limiter = Limiter(key_func=get_ipaddr, storage_uri=settings.LIMITER_STORAGE)
import_query_runners(settings.QUERY_RUNNERS)
import_destinations(settings.DESTINATIONS)
diff --git a/redash/app.py b/redash/app.py
index e66cc32233..554dca309f 100644
--- a/redash/app.py
+++ b/redash/app.py
@@ -1,7 +1,7 @@
from flask import Flask
from werkzeug.middleware.proxy_fix import ProxyFix
-from redash import settings
+from . import settings
class Redash(Flask):
@@ -10,7 +10,7 @@ class Redash(Flask):
def __init__(self, *args, **kwargs):
kwargs.update(
{
- "template_folder": settings.FLASK_TEMPLATE_PATH,
+ "template_folder": settings.STATIC_ASSETS_PATH,
"static_folder": settings.STATIC_ASSETS_PATH,
"static_url_path": "/static",
}
@@ -25,6 +25,7 @@ def __init__(self, *args, **kwargs):
def create_app():
from . import (
authentication,
+ extensions,
handlers,
limiter,
mail,
@@ -42,7 +43,7 @@ def create_app():
app = Redash()
# Check and update the cached version for use by the client
- reset_new_version_status()
+ app.before_first_request(reset_new_version_status)
security.init_app(app)
request_metrics.init_app(app)
@@ -53,6 +54,7 @@ def create_app():
limiter.init_app(app)
handlers.init_app(app)
configure_webpack(app)
+ extensions.init_app(app)
users.init_app(app)
tasks.init_app(app)
diff --git a/redash/authentication/__init__.py b/redash/authentication/__init__.py
index c7fa638085..7aeea8f332 100644
--- a/redash/authentication/__init__.py
+++ b/redash/authentication/__init__.py
@@ -2,29 +2,29 @@
import hmac
import logging
import time
-from datetime import timedelta
from urllib.parse import urlsplit, urlunsplit
-from flask import jsonify, redirect, request, session, url_for
+from flask import jsonify, redirect, request, url_for
from flask_login import LoginManager, login_user, logout_user, user_logged_in
-from sqlalchemy.orm.exc import NoResultFound
-from werkzeug.exceptions import Unauthorized
-
from redash import models, settings
from redash.authentication import jwt_auth
from redash.authentication.org_resolving import current_org
from redash.settings.organization import settings as org_settings
from redash.tasks import record_event
+from sqlalchemy.orm.exc import NoResultFound
+from werkzeug.exceptions import Unauthorized
login_manager = LoginManager()
logger = logging.getLogger("authentication")
def get_login_url(external=False, next="/"):
- if settings.MULTI_ORG and current_org == None: # noqa: E711
+ if settings.MULTI_ORG and current_org == None:
login_url = "/"
elif settings.MULTI_ORG:
- login_url = url_for("redash.login", org_slug=current_org.slug, next=next, _external=external)
+ login_url = url_for(
+ "redash.login", org_slug=current_org.slug, next=next, _external=external
+ )
else:
login_url = url_for("redash.login", next=next, _external=external)
@@ -67,7 +67,11 @@ def request_loader(request):
elif settings.AUTH_TYPE == "api_key":
user = api_key_load_user_from_request(request)
else:
- logger.warning("Unknown authentication type ({}). Using default (HMAC).".format(settings.AUTH_TYPE))
+ logger.warning(
+ "Unknown authentication type ({}). Using default (HMAC).".format(
+ settings.AUTH_TYPE
+ )
+ )
user = hmac_load_user_from_request(request)
if org_settings["auth_jwt_login_enabled"] and user is None:
@@ -187,10 +191,6 @@ def jwt_token_load_user_from_request(request):
if not payload:
return
- if "email" not in payload:
- logger.info("No email field in token, refusing to login")
- return
-
try:
user = models.User.get_by_email_and_org(payload["email"], org)
except models.NoResultFound:
@@ -215,9 +215,12 @@ def log_user_logged_in(app, user):
@login_manager.unauthorized_handler
def redirect_to_login():
- is_xhr = request.headers.get("X-Requested-With") == "XMLHttpRequest"
- if is_xhr or "/api/" in request.path:
- return {"message": "Couldn't find resource. Please login and try again."}, 404
+ if request.is_xhr or "/api/" in request.path:
+ response = jsonify(
+ {"message": "Couldn't find resource. Please login and try again."}
+ )
+ response.status_code = 404
+ return response
login_url = get_login_url(next=request.url, external=False)
@@ -227,7 +230,7 @@ def redirect_to_login():
def logout_and_redirect_to_index():
logout_user()
- if settings.MULTI_ORG and current_org == None: # noqa: E711
+ if settings.MULTI_ORG and current_org == None:
index_url = "/"
elif settings.MULTI_ORG:
index_url = url_for("redash.index", org_slug=current_org.slug, _external=False)
@@ -238,31 +241,20 @@ def logout_and_redirect_to_index():
def init_app(app):
- from redash.authentication import ldap_auth, remote_user_auth, saml_auth
- from redash.authentication.google_oauth import (
- create_google_oauth_blueprint,
+ from redash.authentication import (
+ google_oauth,
+ saml_auth,
+ remote_user_auth,
+ ldap_auth,
)
login_manager.init_app(app)
login_manager.anonymous_user = models.AnonymousUser
- login_manager.REMEMBER_COOKIE_DURATION = settings.REMEMBER_COOKIE_DURATION
-
- @app.before_request
- def extend_session():
- session.permanent = True
- app.permanent_session_lifetime = timedelta(seconds=settings.SESSION_EXPIRY_TIME)
-
- from redash.security import csrf
-
- # Authlib's flask oauth client requires a Flask app to initialize
- for blueprint in [
- create_google_oauth_blueprint(app),
- saml_auth.blueprint,
- remote_user_auth.blueprint,
- ldap_auth.blueprint,
- ]:
- csrf.exempt(blueprint)
- app.register_blueprint(blueprint)
+
+ app.register_blueprint(google_oauth.blueprint)
+ app.register_blueprint(saml_auth.blueprint)
+ app.register_blueprint(remote_user_auth.blueprint)
+ app.register_blueprint(ldap_auth.blueprint)
user_logged_in.connect(log_user_logged_in)
login_manager.request_loader(request_loader)
diff --git a/redash/authentication/account.py b/redash/authentication/account.py
index 30ec3a26b2..c826a71aa4 100644
--- a/redash/authentication/account.py
+++ b/redash/authentication/account.py
@@ -1,12 +1,13 @@
import logging
-
from flask import render_template
-from itsdangerous import URLSafeTimedSerializer
from redash import settings
from redash.tasks import send_mail
from redash.utils import base_url
+# noinspection PyUnresolvedReferences
+from itsdangerous import URLSafeTimedSerializer, SignatureExpired, BadSignature
+
logger = logging.getLogger(__name__)
serializer = URLSafeTimedSerializer(settings.SECRET_KEY)
diff --git a/redash/authentication/google_oauth.py b/redash/authentication/google_oauth.py
index 1107d4281e..59d49ef90e 100644
--- a/redash/authentication/google_oauth.py
+++ b/redash/authentication/google_oauth.py
@@ -1,17 +1,53 @@
import logging
-
import requests
-from authlib.integrations.flask_client import OAuth
-from flask import Blueprint, flash, redirect, request, session, url_for
+from flask import redirect, url_for, Blueprint, flash, request, session
+from flask_oauthlib.client import OAuth
-from redash import models
+from redash import models, settings
from redash.authentication import (
create_and_login_user,
- get_next_path,
logout_and_redirect_to_index,
+ get_next_path,
)
from redash.authentication.org_resolving import current_org
+logger = logging.getLogger("google_oauth")
+
+oauth = OAuth()
+blueprint = Blueprint("google_oauth", __name__)
+
+
+def google_remote_app():
+ if "google" not in oauth.remote_apps:
+ oauth.remote_app(
+ "google",
+ base_url="https://www.google.com/accounts/",
+ authorize_url="https://accounts.google.com/o/oauth2/auth?prompt=select_account+consent",
+ request_token_url=None,
+ request_token_params={
+ "scope": "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
+ },
+ access_token_url="https://accounts.google.com/o/oauth2/token",
+ access_token_method="POST",
+ consumer_key=settings.GOOGLE_CLIENT_ID,
+ consumer_secret=settings.GOOGLE_CLIENT_SECRET,
+ )
+
+ return oauth.google
+
+
+def get_user_profile(access_token):
+ headers = {"Authorization": "OAuth {}".format(access_token)}
+ response = requests.get(
+ "https://www.googleapis.com/oauth2/v1/userinfo", headers=headers
+ )
+
+ if response.status_code == 401:
+ logger.warning("Failed getting user profile (response code 401).")
+ return None
+
+ return response.json()
+
def verify_profile(org, profile):
if org.is_public:
@@ -29,90 +65,60 @@ def verify_profile(org, profile):
return False
-def create_google_oauth_blueprint(app):
- oauth = OAuth(app)
+@blueprint.route("//oauth/google", endpoint="authorize_org")
+def org_login(org_slug):
+ session["org_slug"] = current_org.slug
+ return redirect(url_for(".authorize", next=request.args.get("next", None)))
- logger = logging.getLogger("google_oauth")
- blueprint = Blueprint("google_oauth", __name__)
- CONF_URL = "https://accounts.google.com/.well-known/openid-configuration"
- oauth = OAuth(app)
- oauth.register(
- name="google",
- server_metadata_url=CONF_URL,
- client_kwargs={"scope": "openid email profile"},
+@blueprint.route("/oauth/google", endpoint="authorize")
+def login():
+ callback = url_for(".callback", _external=True)
+ next_path = request.args.get(
+ "next", url_for("redash.index", org_slug=session.get("org_slug"))
)
+ logger.debug("Callback url: %s", callback)
+ logger.debug("Next is: %s", next_path)
+ return google_remote_app().authorize(callback=callback, state=next_path)
+
+
+@blueprint.route("/oauth/google_callback", endpoint="callback")
+def authorized():
+ resp = google_remote_app().authorized_response()
+ access_token = resp["access_token"]
+
+ if access_token is None:
+ logger.warning("Access token missing in call back request.")
+ flash("Validation error. Please retry.")
+ return redirect(url_for("redash.login"))
+
+ profile = get_user_profile(access_token)
+ if profile is None:
+ flash("Validation error. Please retry.")
+ return redirect(url_for("redash.login"))
+
+ if "org_slug" in session:
+ org = models.Organization.get_by_slug(session.pop("org_slug"))
+ else:
+ org = current_org
+
+ if not verify_profile(org, profile):
+ logger.warning(
+ "User tried to login with unauthorized domain name: %s (org: %s)",
+ profile["email"],
+ org,
+ )
+ flash("Your Google Apps account ({}) isn't allowed.".format(profile["email"]))
+ return redirect(url_for("redash.login", org_slug=org.slug))
+
+ picture_url = "%s?sz=40" % profile["picture"]
+ user = create_and_login_user(org, profile["name"], profile["email"], picture_url)
+ if user is None:
+ return logout_and_redirect_to_index()
+
+ unsafe_next_path = request.args.get("state") or url_for(
+ "redash.index", org_slug=org.slug
+ )
+ next_path = get_next_path(unsafe_next_path)
- def get_user_profile(access_token):
- headers = {"Authorization": "OAuth {}".format(access_token)}
- response = requests.get("https://www.googleapis.com/oauth2/v1/userinfo", headers=headers)
-
- if response.status_code == 401:
- logger.warning("Failed getting user profile (response code 401).")
- return None
-
- return response.json()
-
- @blueprint.route("//oauth/google", endpoint="authorize_org")
- def org_login(org_slug):
- session["org_slug"] = current_org.slug
- return redirect(url_for(".authorize", next=request.args.get("next", None)))
-
- @blueprint.route("/oauth/google", endpoint="authorize")
- def login():
- redirect_uri = url_for(".callback", _external=True)
-
- next_path = request.args.get("next", url_for("redash.index", org_slug=session.get("org_slug")))
- logger.debug("Callback url: %s", redirect_uri)
- logger.debug("Next is: %s", next_path)
-
- session["next_url"] = next_path
-
- return oauth.google.authorize_redirect(redirect_uri)
-
- @blueprint.route("/oauth/google_callback", endpoint="callback")
- def authorized():
- logger.debug("Authorized user inbound")
-
- resp = oauth.google.authorize_access_token()
- user = resp.get("userinfo")
- if user:
- session["user"] = user
-
- access_token = resp["access_token"]
-
- if access_token is None:
- logger.warning("Access token missing in call back request.")
- flash("Validation error. Please retry.")
- return redirect(url_for("redash.login"))
-
- profile = get_user_profile(access_token)
- if profile is None:
- flash("Validation error. Please retry.")
- return redirect(url_for("redash.login"))
-
- if "org_slug" in session:
- org = models.Organization.get_by_slug(session.pop("org_slug"))
- else:
- org = current_org
-
- if not verify_profile(org, profile):
- logger.warning(
- "User tried to login with unauthorized domain name: %s (org: %s)",
- profile["email"],
- org,
- )
- flash("Your Google Apps account ({}) isn't allowed.".format(profile["email"]))
- return redirect(url_for("redash.login", org_slug=org.slug))
-
- picture_url = "%s?sz=40" % profile["picture"]
- user = create_and_login_user(org, profile["name"], profile["email"], picture_url)
- if user is None:
- return logout_and_redirect_to_index()
-
- unsafe_next_path = session.get("next_url") or url_for("redash.index", org_slug=org.slug)
- next_path = get_next_path(unsafe_next_path)
-
- return redirect(next_path)
-
- return blueprint
+ return redirect(next_path)
diff --git a/redash/authentication/jwt_auth.py b/redash/authentication/jwt_auth.py
index a59029b6d2..81904235ae 100644
--- a/redash/authentication/jwt_auth.py
+++ b/redash/authentication/jwt_auth.py
@@ -1,39 +1,10 @@
-import json
import logging
-
import jwt
import requests
+import simplejson
logger = logging.getLogger("jwt_auth")
-FILE_SCHEME_PREFIX = "file://"
-
-
-def get_public_key_from_file(url):
- file_path = url[len(FILE_SCHEME_PREFIX) :]
- with open(file_path) as key_file:
- key_str = key_file.read()
-
- get_public_keys.key_cache[url] = [key_str]
- return key_str
-
-
-def get_public_key_from_net(url):
- r = requests.get(url)
- r.raise_for_status()
- data = r.json()
- if "keys" in data:
- public_keys = []
- for key_dict in data["keys"]:
- public_key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key_dict))
- public_keys.append(public_key)
-
- get_public_keys.key_cache[url] = public_keys
- return public_keys
- else:
- get_public_keys.key_cache[url] = data
- return data
-
def get_public_keys(url):
"""
@@ -41,21 +12,33 @@ def get_public_keys(url):
List of RSA public keys usable by PyJWT.
"""
key_cache = get_public_keys.key_cache
- keys = {}
if url in key_cache:
- keys = key_cache[url]
+ return key_cache[url]
else:
- if url.startswith(FILE_SCHEME_PREFIX):
- keys = [get_public_key_from_file(url)]
+ r = requests.get(url)
+ r.raise_for_status()
+ data = r.json()
+ if "keys" in data:
+ public_keys = []
+ for key_dict in data["keys"]:
+ public_key = jwt.algorithms.RSAAlgorithm.from_jwk(
+ simplejson.dumps(key_dict)
+ )
+ public_keys.append(public_key)
+
+ get_public_keys.key_cache[url] = public_keys
+ return public_keys
else:
- keys = get_public_key_from_net(url)
- return keys
+ get_public_keys.key_cache[url] = data
+ return data
get_public_keys.key_cache = {}
-def verify_jwt_token(jwt_token, expected_issuer, expected_audience, algorithms, public_certs_url):
+def verify_jwt_token(
+ jwt_token, expected_issuer, expected_audience, algorithms, public_certs_url
+):
# https://developers.cloudflare.com/access/setting-up-access/validate-jwt-tokens/
# https://cloud.google.com/iap/docs/signed-headers-howto
# Loop through the keys since we can't pass the key set to the decoder
@@ -70,7 +53,9 @@ def verify_jwt_token(jwt_token, expected_issuer, expected_audience, algorithms,
for key in keys:
try:
# decode returns the claims which has the email if you need it
- payload = jwt.decode(jwt_token, key=key, audience=expected_audience, algorithms=algorithms)
+ payload = jwt.decode(
+ jwt_token, key=key, audience=expected_audience, algorithms=algorithms
+ )
issuer = payload["iss"]
if issuer != expected_issuer:
raise Exception("Wrong issuer: {}".format(issuer))
@@ -78,5 +63,4 @@ def verify_jwt_token(jwt_token, expected_issuer, expected_audience, algorithms,
break
except Exception as e:
logging.exception(e)
-
return payload, valid_token
diff --git a/redash/authentication/ldap_auth.py b/redash/authentication/ldap_auth.py
index 3bc5ff272f..e102b3f516 100644
--- a/redash/authentication/ldap_auth.py
+++ b/redash/authentication/ldap_auth.py
@@ -1,24 +1,23 @@
import logging
import sys
-from flask import Blueprint, flash, redirect, render_template, request, url_for
-from flask_login import current_user
-
from redash import settings
+from flask import flash, redirect, render_template, request, url_for, Blueprint
+from flask_login import current_user
+
try:
- from ldap3 import Connection, Server
- from ldap3.utils.conv import escape_filter_chars
+ from ldap3 import Server, Connection
except ImportError:
if settings.LDAP_LOGIN_ENABLED:
sys.exit(
- "The ldap3 library was not found. This is required to use LDAP authentication. Rebuild the Docker image installing the `ldap3` poetry dependency group."
+ "The ldap3 library was not found. This is required to use LDAP authentication (see requirements.txt)."
)
from redash.authentication import (
create_and_login_user,
- get_next_path,
logout_and_redirect_to_index,
+ get_next_path,
)
from redash.authentication.org_resolving import current_org
from redash.handlers.base import org_scoped_rule
@@ -70,7 +69,6 @@ def login(org_slug=None):
def auth_ldap_user(username, password):
- clean_username = escape_filter_chars(username)
server = Server(settings.LDAP_HOST_URL, use_ssl=settings.LDAP_SSL)
if settings.LDAP_BIND_DN is not None:
conn = Connection(
@@ -85,7 +83,7 @@ def auth_ldap_user(username, password):
conn.search(
settings.LDAP_SEARCH_DN,
- settings.LDAP_SEARCH_TEMPLATE % {"username": clean_username},
+ settings.LDAP_SEARCH_TEMPLATE % {"username": username},
attributes=[settings.LDAP_DISPLAY_NAME_KEY, settings.LDAP_EMAIL_KEY],
)
diff --git a/redash/authentication/remote_user_auth.py b/redash/authentication/remote_user_auth.py
index 59f25fb07c..7cba295ccd 100644
--- a/redash/authentication/remote_user_auth.py
+++ b/redash/authentication/remote_user_auth.py
@@ -1,15 +1,13 @@
import logging
-
-from flask import Blueprint, redirect, request, url_for
-
-from redash import settings
+from flask import redirect, url_for, Blueprint, request
from redash.authentication import (
create_and_login_user,
- get_next_path,
logout_and_redirect_to_index,
+ get_next_path,
)
from redash.authentication.org_resolving import current_org
from redash.handlers.base import org_scoped_rule
+from redash import settings
logger = logging.getLogger("remote_user_auth")
@@ -22,7 +20,9 @@ def login(org_slug=None):
next_path = get_next_path(unsafe_next_path)
if not settings.REMOTE_USER_LOGIN_ENABLED:
- logger.error("Cannot use remote user for login without being enabled in settings")
+ logger.error(
+ "Cannot use remote user for login without being enabled in settings"
+ )
return redirect(url_for("redash.index", next=next_path, org_slug=org_slug))
email = request.headers.get(settings.REMOTE_USER_HEADER)
diff --git a/redash/authentication/saml_auth.py b/redash/authentication/saml_auth.py
index 1c52bf1a3c..af96841cd0 100644
--- a/redash/authentication/saml_auth.py
+++ b/redash/authentication/saml_auth.py
@@ -1,24 +1,16 @@
import logging
-
-from flask import Blueprint, flash, redirect, request, url_for
+from flask import flash, redirect, url_for, Blueprint, request
+from redash import settings
+from redash.authentication import create_and_login_user, logout_and_redirect_to_index
+from redash.authentication.org_resolving import current_org
+from redash.handlers.base import org_scoped_rule
from saml2 import BINDING_HTTP_POST, BINDING_HTTP_REDIRECT, entity
from saml2.client import Saml2Client
from saml2.config import Config as Saml2Config
from saml2.saml import NAMEID_FORMAT_TRANSIENT
-from saml2.sigver import get_xmlsec_binary
-
-from redash import settings
-from redash.authentication import (
- create_and_login_user,
- logout_and_redirect_to_index,
-)
-from redash.authentication.org_resolving import current_org
-from redash.handlers.base import org_scoped_rule
-from redash.utils import mustache_render
logger = logging.getLogger("saml_auth")
blueprint = Blueprint("saml_auth", __name__)
-inline_metadata_template = """{{x509_cert}}"""
def get_saml_client(org):
@@ -27,21 +19,12 @@ def get_saml_client(org):
The configuration is a hash for use by saml2.config.Config
"""
-
- saml_type = org.get_setting("auth_saml_type")
- entity_id = org.get_setting("auth_saml_entity_id")
- sso_url = org.get_setting("auth_saml_sso_url")
- x509_cert = org.get_setting("auth_saml_x509_cert")
metadata_url = org.get_setting("auth_saml_metadata_url")
- sp_settings = org.get_setting("auth_saml_sp_settings")
+ entity_id = org.get_setting("auth_saml_entity_id")
if settings.SAML_SCHEME_OVERRIDE:
- acs_url = url_for(
- "saml_auth.idp_initiated",
- org_slug=org.slug,
- _external=True,
- _scheme=settings.SAML_SCHEME_OVERRIDE,
- )
+ acs_url = url_for("saml_auth.idp_initiated", org_slug=org.slug, _external=True,
+ _scheme=settings.SAML_SCHEME_OVERRIDE)
else:
acs_url = url_for("saml_auth.idp_initiated", org_slug=org.slug, _external=True)
@@ -68,36 +51,9 @@ def get_saml_client(org):
},
}
- if settings.SAML_ENCRYPTION_ENABLED:
- encryption_dict = {
- "xmlsec_binary": get_xmlsec_binary(),
- "encryption_keypairs": [
- {
- "key_file": settings.SAML_ENCRYPTION_PEM_PATH,
- "cert_file": settings.SAML_ENCRYPTION_CERT_PATH,
- }
- ],
- }
- saml_settings.update(encryption_dict)
-
- if saml_type is not None and saml_type == "static":
- metadata_inline = mustache_render(
- inline_metadata_template,
- entity_id=entity_id,
- x509_cert=x509_cert,
- sso_url=sso_url,
- )
-
- saml_settings["metadata"] = {"inline": [metadata_inline]}
-
if entity_id is not None and entity_id != "":
saml_settings["entityid"] = entity_id
- if sp_settings:
- import json
-
- saml_settings["service"]["sp"].update(json.loads(sp_settings))
-
sp_config = Saml2Config()
sp_config.load(saml_settings)
sp_config.allow_unknown_attributes = True
diff --git a/redash/cli/__init__.py b/redash/cli/__init__.py
index 5bf40d4a8e..c20031ff97 100644
--- a/redash/cli/__init__.py
+++ b/redash/cli/__init__.py
@@ -1,25 +1,17 @@
-import json
-
import click
+import simplejson
from flask import current_app
-from flask.cli import FlaskGroup, run_command, with_appcontext
+from flask.cli import FlaskGroup, run_command
from rq import Connection
-from redash import __version__, create_app, rq_redis_connection, settings
-from redash.cli import (
- data_sources,
- database,
- groups,
- organization,
- queries,
- rq,
- users,
-)
+from redash import __version__, create_app, settings, rq_redis_connection
+from redash.cli import data_sources, database, groups, organization, queries, users, rq
from redash.monitor import get_status
-def create():
+def create(group):
app = current_app or create_app()
+ group.app = app
@app.shell_context_processor
def shell_context():
@@ -54,7 +46,7 @@ def version():
@manager.command()
def status():
with Connection(rq_redis_connection):
- print(json.dumps(get_status(), indent=2))
+ print(simplejson.dumps(get_status(), indent=2))
@manager.command()
@@ -70,24 +62,36 @@ def send_test_mail(email=None):
"""
Send test message to EMAIL (default: the address you defined in MAIL_DEFAULT_SENDER)
"""
- from flask_mail import Message
-
from redash import mail
+ from flask_mail import Message
if email is None:
email = settings.MAIL_DEFAULT_SENDER
- mail.send(Message(subject="Test Message from Redash", recipients=[email], body="Test message."))
+ mail.send(
+ Message(
+ subject="Test Message from Redash", recipients=[email], body="Test message."
+ )
+ )
-@manager.command("shell")
-@with_appcontext
-def shell():
+@manager.command()
+def ipython():
+ """Starts IPython shell instead of the default Python shell."""
import sys
-
+ import IPython
from flask.globals import _app_ctx_stack
- from ptpython import repl
app = _app_ctx_stack.top.app
- repl.embed(globals=app.make_shell_context())
+ banner = "Python %s on %s\nIPython: %s\nRedash version: %s\n" % (
+ sys.version,
+ sys.platform,
+ IPython.__version__,
+ __version__,
+ )
+
+ ctx = {}
+ ctx.update(app.make_shell_context())
+
+ IPython.embed(banner1=banner, user_ns=ctx)
diff --git a/redash/cli/data_sources.py b/redash/cli/data_sources.py
index ccc7429a20..fe15075395 100644
--- a/redash/cli/data_sources.py
+++ b/redash/cli/data_sources.py
@@ -1,11 +1,10 @@
from sys import exit
import click
-from click.types import convert_type
from flask.cli import AppGroup
from sqlalchemy.orm.exc import NoResultFound
-from redash import models
+from redash import models, tasks
from redash.query_runner import (
get_configuration_schema_for_query_runner_type,
query_runners,
@@ -34,10 +33,14 @@ def list_command(organization=None):
if i > 0:
print("-" * 20)
- print("Id: {}\nName: {}\nType: {}\nOptions: {}".format(ds.id, ds.name, ds.type, ds.options.to_json()))
+ print(
+ "Id: {}\nName: {}\nType: {}\nOptions: {}".format(
+ ds.id, ds.name, ds.type, ds.options.to_json()
+ )
+ )
-@manager.command(name="list_types")
+@manager.command()
def list_types():
print("Enabled Query Runners:")
types = sorted(query_runners.keys())
@@ -72,7 +75,9 @@ def test(name, organization="default"):
data_source = models.DataSource.query.filter(
models.DataSource.name == name, models.DataSource.org == org
).one()
- print("Testing connection to data source: {} (id={})".format(name, data_source.id))
+ print(
+ "Testing connection to data source: {} (id={})".format(name, data_source.id)
+ )
try:
data_source.query_runner.test_connection()
except Exception as e:
@@ -134,19 +139,11 @@ def new(name=None, type=None, options=None, organization="default"):
else:
prompt = "{} (optional)".format(prompt)
- _type = types[prop["type"]]
-
- def value_proc(value):
- if value == default_value:
- return default_value
- return convert_type(_type, default_value)(value)
-
value = click.prompt(
prompt,
default=default_value,
- type=_type,
+ type=types[prop["type"]],
show_default=False,
- value_proc=value_proc,
)
if value != default_value:
options_obj[k] = value
@@ -157,9 +154,13 @@ def value_proc(value):
if not options.is_valid():
print("Error: invalid configuration.")
- exit(1)
+ exit()
- print("Creating {} data source ({}) with options:\n{}".format(type, name, options.to_json()))
+ print(
+ "Creating {} data source ({}) with options:\n{}".format(
+ type, name, options.to_json()
+ )
+ )
data_source = models.DataSource.create_with_group(
name=name,
@@ -201,6 +202,38 @@ def update_attr(obj, attr, new_value):
setattr(obj, attr, new_value)
+@manager.command()
+@click.argument("name")
+@click.option(
+ "--org",
+ "organization",
+ default="default",
+ help="The organization the user belongs to (leave blank for " "'default').",
+)
+@click.option(
+ "--count",
+ "num_tables",
+ default=50,
+ help="number of tables to process data samples for",
+)
+def refresh_samples(name, num_tables=50, organization="default"):
+ """Refresh table samples by data source name."""
+ try:
+ org = models.Organization.get_by_slug(organization)
+ data_source = models.DataSource.query.filter(
+ models.DataSource.name == name, models.DataSource.org == org
+ ).one()
+ print(
+ "Refreshing samples for data source: {} (id={})".format(
+ name, data_source.id
+ )
+ )
+ tasks.refresh_samples(data_source.id, num_tables)
+ except NoResultFound:
+ print("Couldn't find data source named: {}".format(name))
+ exit(1)
+
+
@manager.command()
@click.argument("name")
@click.option("--name", "new_name", default=None, help="new name for the data source")
diff --git a/redash/cli/database.py b/redash/cli/database.py
index ef1d4adbe9..ce55b73c6f 100644
--- a/redash/cli/database.py
+++ b/redash/cli/database.py
@@ -1,17 +1,14 @@
-import logging
import time
-import sqlalchemy
from click import argument, option
-from cryptography.fernet import InvalidToken
from flask.cli import AppGroup
from flask_migrate import stamp
+import sqlalchemy
from sqlalchemy.exc import DatabaseError
from sqlalchemy.sql import select
from sqlalchemy_utils.types.encrypted.encrypted_type import FernetEngine
-from redash import settings
-from redash.models.base import Column, key_type
+from redash.models.base import Column
from redash.models.types import EncryptedConfiguration
from redash.utils.configuration import ConfigurationContainer
@@ -30,40 +27,21 @@ def _wait_for_db_connection(db):
retried = True
-def is_db_empty():
- from redash.models import db
-
- table_names = sqlalchemy.inspect(db.get_engine()).get_table_names()
- return len(table_names) == 0
-
-
-def load_extensions(db):
- with db.engine.connect() as connection:
- for extension in settings.dynamic_settings.database_extensions:
- connection.execute(f'CREATE EXTENSION IF NOT EXISTS "{extension}";')
-
-
-@manager.command(name="create_tables")
+@manager.command()
def create_tables():
"""Create the database tables."""
from redash.models import db
_wait_for_db_connection(db)
+ # To create triggers for searchable models, we need to call configure_mappers().
+ sqlalchemy.orm.configure_mappers()
+ db.create_all()
- # We need to make sure we run this only if the DB is empty, because otherwise calling
- # stamp() will stamp it with the latest migration value and migrations won't run.
- if is_db_empty():
- load_extensions(db)
-
- # To create triggers for searchable models, we need to call configure_mappers().
- sqlalchemy.orm.configure_mappers()
- db.create_all()
-
- # Need to mark current DB as up to date
- stamp()
+ # Need to mark current DB as up to date
+ stamp()
-@manager.command(name="drop_tables")
+@manager.command()
def drop_tables():
"""Drop the database tables."""
from redash.models import db
@@ -83,43 +61,41 @@ def reencrypt(old_secret, new_secret, show_sql):
_wait_for_db_connection(db)
if show_sql:
+ import logging
+
logging.basicConfig()
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
- def _reencrypt_for_table(table_name, orm_name):
- table_for_select = sqlalchemy.Table(
- table_name,
- sqlalchemy.MetaData(),
- Column("id", key_type(orm_name), primary_key=True),
- Column(
- "encrypted_options",
- ConfigurationContainer.as_mutable(EncryptedConfiguration(db.Text, old_secret, FernetEngine)),
+ table_for_select = sqlalchemy.Table(
+ "data_sources",
+ sqlalchemy.MetaData(),
+ Column("id", db.Integer, primary_key=True),
+ Column(
+ "encrypted_options",
+ ConfigurationContainer.as_mutable(
+ EncryptedConfiguration(db.Text, old_secret, FernetEngine)
),
- )
- table_for_update = sqlalchemy.Table(
- table_name,
- sqlalchemy.MetaData(),
- Column("id", key_type(orm_name), primary_key=True),
- Column(
- "encrypted_options",
- ConfigurationContainer.as_mutable(EncryptedConfiguration(db.Text, new_secret, FernetEngine)),
+ ),
+ )
+ table_for_update = sqlalchemy.Table(
+ "data_sources",
+ sqlalchemy.MetaData(),
+ Column("id", db.Integer, primary_key=True),
+ Column(
+ "encrypted_options",
+ ConfigurationContainer.as_mutable(
+ EncryptedConfiguration(db.Text, new_secret, FernetEngine)
),
+ ),
+ )
+
+ update = table_for_update.update()
+ data_sources = db.session.execute(select([table_for_select]))
+ for ds in data_sources:
+ stmt = update.where(table_for_update.c.id == ds["id"]).values(
+ encrypted_options=ds["encrypted_options"]
)
+ db.session.execute(stmt)
- update = table_for_update.update()
- selected_items = db.session.execute(select([table_for_select]))
- for item in selected_items:
- try:
- stmt = update.where(table_for_update.c.id == item["id"]).values(
- encrypted_options=item["encrypted_options"]
- )
- except InvalidToken:
- logging.error(f'Invalid Decryption Key for id {item["id"]} in table {table_for_select}')
- else:
- db.session.execute(stmt)
-
- selected_items.close()
- db.session.commit()
-
- _reencrypt_for_table("data_sources", "DataSource")
- _reencrypt_for_table("notification_destinations", "NotificationDestination")
+ data_sources.close()
+ db.session.commit()
diff --git a/redash/cli/groups.py b/redash/cli/groups.py
index 2da7dcd776..1770057feb 100644
--- a/redash/cli/groups.py
+++ b/redash/cli/groups.py
@@ -1,8 +1,8 @@
from sys import exit
-from click import argument, option
-from flask.cli import AppGroup
from sqlalchemy.orm.exc import NoResultFound
+from flask.cli import AppGroup
+from click import argument, option
from redash import models
@@ -43,7 +43,7 @@ def create(name, permissions=None, organization="default"):
exit(1)
-@manager.command(name="change_permissions")
+@manager.command()
@argument("group_id")
@option(
"--permissions",
@@ -60,11 +60,14 @@ def change_permissions(group_id, permissions=None):
try:
group = models.Group.query.get(group_id)
except NoResultFound:
- print("Group [%s] not found." % group_id)
+ print("User [%s] not found." % group_id)
exit(1)
permissions = extract_permissions_string(permissions)
- print("current permissions [%s] will be modify to [%s]" % (",".join(group.permissions), ",".join(permissions)))
+ print(
+ "current permissions [%s] will be modify to [%s]"
+ % (",".join(group.permissions), ",".join(permissions))
+ )
group.permissions = permissions
@@ -116,7 +119,4 @@ def list_command(organization=None):
members = models.Group.members(group.id)
user_names = [m.name for m in members]
- if user_names:
- print("Users: {}".format(", ".join(user_names)))
- else:
- print("Users:")
+ print("Users: {}".format(", ".join(user_names)))
diff --git a/redash/cli/organization.py b/redash/cli/organization.py
index d941e06adb..45c73551fc 100644
--- a/redash/cli/organization.py
+++ b/redash/cli/organization.py
@@ -1,4 +1,4 @@
-from click import argument, option
+from click import argument
from flask.cli import AppGroup
from redash import models
@@ -6,7 +6,7 @@
manager = AppGroup(help="Organization management commands.")
-@manager.command(name="set_google_apps_domains")
+@manager.command()
@argument("domains")
def set_google_apps_domains(domains):
"""
@@ -17,32 +17,21 @@ def set_google_apps_domains(domains):
organization.settings[k] = domains.split(",")
models.db.session.add(organization)
models.db.session.commit()
- print("Updated list of allowed domains to: {}".format(organization.google_apps_domains))
+ print(
+ "Updated list of allowed domains to: {}".format(
+ organization.google_apps_domains
+ )
+ )
-@manager.command(name="show_google_apps_domains")
+@manager.command()
def show_google_apps_domains():
organization = models.Organization.query.first()
- print("Current list of Google Apps domains: {}".format(", ".join(organization.google_apps_domains)))
-
-
-@manager.command(name="create")
-@argument("name")
-@option(
- "--slug",
- "slug",
- default="default",
- help="The slug the organization belongs to (leave blank for " "'default').",
-)
-def create(name, slug="default"):
- print("Creating organization (%s)..." % (name))
-
- try:
- models.db.session.add(models.Organization(name=name, slug=slug, settings={}))
- models.db.session.commit()
- except Exception as e:
- print("Failed create organization: %s" % e)
- exit(1)
+ print(
+ "Current list of Google Apps domains: {}".format(
+ ", ".join(organization.google_apps_domains)
+ )
+ )
@manager.command(name="list")
diff --git a/redash/cli/queries.py b/redash/cli/queries.py
index dddb35724e..f71bdbabe6 100644
--- a/redash/cli/queries.py
+++ b/redash/cli/queries.py
@@ -5,23 +5,7 @@
manager = AppGroup(help="Queries management commands.")
-@manager.command(name="rehash")
-def rehash():
- from redash import models
-
- for q in models.Query.query.all():
- old_hash = q.query_hash
- q.update_query_hash()
- new_hash = q.query_hash
-
- if old_hash != new_hash:
- print(f"Query {q.id} has changed hash from {old_hash} to {new_hash}")
- models.db.session.add(q)
-
- models.db.session.commit()
-
-
-@manager.command(name="add_tag")
+@manager.command()
@argument("query_id")
@argument("tag")
def add_tag(query_id, tag):
@@ -47,7 +31,7 @@ def add_tag(query_id, tag):
print("Tag added.")
-@manager.command(name="remove_tag")
+@manager.command()
@argument("query_id")
@argument("tag")
def remove_tag(query_id, tag):
diff --git a/redash/cli/rq.py b/redash/cli/rq.py
index c2c1ed6f7a..80ec7eb7ee 100644
--- a/redash/cli/rq.py
+++ b/redash/cli/rq.py
@@ -1,5 +1,7 @@
-import datetime
+from __future__ import absolute_import
import socket
+import sys
+import datetime
from itertools import chain
from click import argument
@@ -12,11 +14,11 @@
from redash import rq_redis_connection
from redash.tasks import (
- periodic_job_definitions,
+ Worker,
rq_scheduler,
schedule_periodic_jobs,
+ periodic_job_definitions,
)
-from redash.tasks.worker import Worker
from redash.worker import default_queues
manager = AppGroup(help="RQ management commands.")
@@ -48,18 +50,30 @@ def worker(queues):
class WorkerHealthcheck(base.BaseCheck):
- NAME = "RQ Worker Healthcheck"
+ NAME = 'RQ Worker Healthcheck'
+ INTERVAL = datetime.timedelta(minutes=5)
+ _last_check_time = {}
- def __call__(self, process_spec):
- pid = process_spec["pid"]
- all_workers = Worker.all(connection=rq_redis_connection)
- workers = [w for w in all_workers if w.hostname == socket.gethostname() and w.pid == pid]
+ def time_to_check(self, pid):
+ now = datetime.datetime.utcnow()
- if not workers:
- self._log(f"Cannot find worker for hostname {socket.gethostname()} and pid {pid}. ==> Is healthy? False")
- return False
+ if pid not in self._last_check_time:
+ self._last_check_time[pid] = now
- worker = workers.pop()
+ if now - self._last_check_time[pid] >= self.INTERVAL:
+ self._last_check_time[pid] = now
+ return True
+
+ return False
+
+ def __call__(self, process_spec):
+ pid = process_spec['pid']
+ if not self.time_to_check(pid):
+ return True
+
+ all_workers = Worker.all(connection=rq_redis_connection)
+ worker = [w for w in all_workers if w.hostname == socket.gethostname().encode() and
+ w.pid == pid].pop()
is_busy = worker.get_state() == WorkerStatus.BUSY
@@ -71,23 +85,17 @@ def __call__(self, process_spec):
is_healthy = is_busy or seen_lately or has_nothing_to_do
- self._log(
- "Worker %s healthcheck: Is busy? %s. "
- "Seen lately? %s (%d seconds ago). "
- "Has nothing to do? %s (%d jobs in watched queues). "
- "==> Is healthy? %s",
- worker.key,
- is_busy,
- seen_lately,
- time_since_seen.seconds,
- has_nothing_to_do,
- total_jobs_in_watched_queues,
- is_healthy,
- )
+ self._log("Worker %s healthcheck: Is busy? %s. "
+ "Seen lately? %s (%d seconds ago). "
+ "Has nothing to do? %s (%d jobs in watched queues). "
+ "==> Is healthy? %s",
+ worker.key, is_busy, seen_lately, time_since_seen.seconds,
+ has_nothing_to_do, total_jobs_in_watched_queues, is_healthy)
return is_healthy
@manager.command()
def healthcheck():
- return check_runner.CheckRunner("worker_healthcheck", "worker", None, [(WorkerHealthcheck, {})]).run()
+ return check_runner.CheckRunner(
+ 'worker_healthcheck', 'worker', None, [(WorkerHealthcheck, {})]).run()
diff --git a/redash/cli/users.py b/redash/cli/users.py
index 03e22dfa63..fc6a4420ee 100644
--- a/redash/cli/users.py
+++ b/redash/cli/users.py
@@ -2,8 +2,8 @@
from click import BOOL, argument, option, prompt
from flask.cli import AppGroup
-from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm.exc import NoResultFound
+from sqlalchemy.exc import IntegrityError
from redash import models
from redash.handlers.users import invite_user
@@ -26,7 +26,7 @@ def build_groups(org, groups, is_admin):
return groups
-@manager.command(name="grant_admin")
+@manager.command()
@argument("email")
@option(
"--org",
@@ -116,7 +116,7 @@ def create(
exit(1)
-@manager.command(name="create_root")
+@manager.command()
@argument("email")
@argument("name")
@option(
@@ -136,13 +136,17 @@ def create(
"--password",
"password",
default=None,
- help="Password for root user who don't use Google Auth (leave blank for prompt).",
+ help="Password for root user who don't use Google Auth "
+ "(leave blank for prompt).",
)
def create_root(email, name, google_auth=False, password=None, organization="default"):
"""
Create root user.
"""
- print("Creating root user (%s, %s) in organization %s..." % (email, name, organization))
+ print(
+ "Creating root user (%s, %s) in organization %s..."
+ % (email, name, organization)
+ )
print("Login with Google Auth: %r\n" % google_auth)
user = models.User.query.filter(models.User.email == email).first()
@@ -151,13 +155,15 @@ def create_root(email, name, google_auth=False, password=None, organization="def
exit(1)
org_slug = organization
- org = models.Organization.query.filter(models.Organization.slug == org_slug).first()
+ org = models.Organization.query.filter(
+ models.Organization.slug == org_slug
+ ).first()
if org is None:
org = models.Organization(name=org_slug, slug=org_slug, settings={})
admin_group = models.Group(
name="admin",
- permissions=models.Group.ADMIN_PERMISSIONS,
+ permissions=["admin", "super_admin"],
org=org,
type=models.Group.BUILTIN_GROUP,
)
@@ -202,9 +208,13 @@ def delete(email, organization=None):
"""
if organization:
org = models.Organization.get_by_slug(organization)
- deleted_count = models.User.query.filter(models.User.email == email, models.User.org == org.id).delete()
+ deleted_count = models.User.query.filter(
+ models.User.email == email, models.User.org == org.id
+ ).delete()
else:
- deleted_count = models.User.query.filter(models.User.email == email).delete(synchronize_session=False)
+ deleted_count = models.User.query.filter(models.User.email == email).delete(
+ synchronize_session=False
+ )
models.db.session.commit()
print("Deleted %d users." % deleted_count)
@@ -224,7 +234,9 @@ def password(email, password, organization=None):
"""
if organization:
org = models.Organization.get_by_slug(organization)
- user = models.User.query.filter(models.User.email == email, models.User.org == org).first()
+ user = models.User.query.filter(
+ models.User.email == email, models.User.org == org
+ ).first()
else:
user = models.User.query.filter(models.User.email == email).first()
@@ -253,7 +265,7 @@ def password(email, password, organization=None):
"--groups",
"groups",
default=None,
- help="Comma separated list of groups (leave blank for default).",
+ help="Comma seperated list of groups (leave blank for default).",
)
def invite(email, name, inviter_email, groups, is_admin=False, organization="default"):
"""
diff --git a/redash/destinations/__init__.py b/redash/destinations/__init__.py
index 4a24e99103..bbd5fef9cb 100644
--- a/redash/destinations/__init__.py
+++ b/redash/destinations/__init__.py
@@ -5,7 +5,7 @@
__all__ = ["BaseDestination", "register", "get_destination", "import_destinations"]
-class BaseDestination:
+class BaseDestination(object):
deprecated = False
def __init__(self, configuration):
@@ -31,7 +31,7 @@ def enabled(cls):
def configuration_schema(cls):
return {}
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
+ def notify(self, alert, query, user, new_state, app, host, options):
raise NotImplementedError()
@classmethod
@@ -41,7 +41,7 @@ def to_dict(cls):
"type": cls.type(),
"icon": cls.icon(),
"configuration_schema": cls.configuration_schema(),
- **({"deprecated": True} if cls.deprecated else {}),
+ **({ "deprecated": True } if cls.deprecated else {})
}
diff --git a/redash/destinations/asana.py b/redash/destinations/asana.py
deleted file mode 100644
index 394633a265..0000000000
--- a/redash/destinations/asana.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import logging
-import textwrap
-
-import requests
-
-from redash.destinations import BaseDestination, register
-from redash.models import Alert
-
-
-class Asana(BaseDestination):
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "pat": {"type": "string", "title": "Asana Personal Access Token"},
- "project_id": {"type": "string", "title": "Asana Project ID"},
- },
- "secret": ["pat"],
- "required": ["pat", "project_id"],
- }
-
- @classmethod
- def icon(cls):
- return "fa-asana"
-
- @property
- def api_base_url(self):
- return "https://app.asana.com/api/1.0/tasks"
-
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
- # Documentation: https://developers.asana.com/docs/tasks
- state = "TRIGGERED" if new_state == Alert.TRIGGERED_STATE else "RECOVERED"
-
- notes = textwrap.dedent(
- f"""
- {alert.name} has {state}.
-
- Query: {host}/queries/{query.id}
- Alert: {host}/alerts/{alert.id}
- """
- ).strip()
-
- data = {
- "name": f"[Redash Alert] {state}: {alert.name}",
- "notes": notes,
- "projects": [options["project_id"]],
- }
-
- try:
- resp = requests.post(
- self.api_base_url,
- data=data,
- timeout=5.0,
- headers={"Authorization": f"Bearer {options['pat']}"},
- )
- logging.warning(resp.text)
- if resp.status_code != 201:
- logging.error("Asana send ERROR. status_code => {status}".format(status=resp.status_code))
- except Exception as e:
- logging.exception("Asana send ERROR. {exception}".format(exception=e))
-
-
-register(Asana)
diff --git a/redash/destinations/chatwork.py b/redash/destinations/chatwork.py
index 46904750c1..d3dda8288a 100644
--- a/redash/destinations/chatwork.py
+++ b/redash/destinations/chatwork.py
@@ -1,12 +1,13 @@
import logging
-
import requests
-from redash.destinations import BaseDestination, register
+from redash.destinations import *
class ChatWork(BaseDestination):
- ALERTS_DEFAULT_MESSAGE_TEMPLATE = "{alert_name} changed state to {new_state}.\\n{alert_url}\\n{query_url}"
+ ALERTS_DEFAULT_MESSAGE_TEMPLATE = (
+ "{alert_name} changed state to {new_state}.\\n{alert_url}\\n{query_url}"
+ )
@classmethod
def configuration_schema(cls):
@@ -21,7 +22,6 @@ def configuration_schema(cls):
"title": "Message Template",
},
},
- "secret": ["api_token"],
"required": ["message_template", "api_token", "room_id"],
}
@@ -29,10 +29,12 @@ def configuration_schema(cls):
def icon(cls):
return "fa-comment"
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
+ def notify(self, alert, query, user, new_state, app, host, options):
try:
# Documentation: http://developer.chatwork.com/ja/endpoint_rooms.html#POST-rooms-room_id-messages
- url = "https://api.chatwork.com/v2/rooms/{room_id}/messages".format(room_id=options.get("room_id"))
+ url = "https://api.chatwork.com/v2/rooms/{room_id}/messages".format(
+ room_id=options.get("room_id")
+ )
message = ""
if alert.custom_subject:
@@ -40,9 +42,15 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
if alert.custom_body:
message += alert.custom_body
else:
- alert_url = "{host}/alerts/{alert_id}".format(host=host, alert_id=alert.id)
- query_url = "{host}/queries/{query_id}".format(host=host, query_id=query.id)
- message_template = options.get("message_template", ChatWork.ALERTS_DEFAULT_MESSAGE_TEMPLATE)
+ alert_url = "{host}/alerts/{alert_id}".format(
+ host=host, alert_id=alert.id
+ )
+ query_url = "{host}/queries/{query_id}".format(
+ host=host, query_id=query.id
+ )
+ message_template = options.get(
+ "message_template", ChatWork.ALERTS_DEFAULT_MESSAGE_TEMPLATE
+ )
message += message_template.replace("\\n", "\n").format(
alert_name=alert.name,
new_state=new_state.upper(),
@@ -56,7 +64,11 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
resp = requests.post(url, headers=headers, data=payload, timeout=5.0)
logging.warning(resp.text)
if resp.status_code != 200:
- logging.error("ChatWork send ERROR. status_code => {status}".format(status=resp.status_code))
+ logging.error(
+ "ChatWork send ERROR. status_code => {status}".format(
+ status=resp.status_code
+ )
+ )
except Exception:
logging.exception("ChatWork send ERROR.")
diff --git a/redash/destinations/datadog.py b/redash/destinations/datadog.py
deleted file mode 100644
index 61a4e0ddc0..0000000000
--- a/redash/destinations/datadog.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import logging
-import os
-
-import requests
-
-from redash.destinations import BaseDestination, register
-from redash.utils import json_dumps
-
-
-class Datadog(BaseDestination):
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "api_key": {"type": "string", "title": "API Key"},
- "tags": {"type": "string", "title": "Tags"},
- "priority": {"type": "string", "default": "normal", "title": "Priority"},
- # https://docs.datadoghq.com/integrations/faq/list-of-api-source-attribute-value/
- "source_type_name": {"type": "string", "default": "my_apps", "title": "Source Type Name"},
- },
- "secret": ["api_key"],
- "required": ["api_key"],
- }
-
- @classmethod
- def icon(cls):
- return "fa-datadog"
-
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
- # Documentation: https://docs.datadoghq.com/api/latest/events/#post-an-event
- if new_state == "triggered":
- alert_type = "error"
- if alert.custom_subject:
- title = alert.custom_subject
- else:
- title = f"{alert.name} just triggered"
- else:
- alert_type = "success"
- if alert.custom_subject:
- title = alert.custom_subject
- else:
- title = f"{alert.name} went back to normal"
-
- if alert.custom_body:
- text = alert.custom_body
- else:
- text = f"{alert.name} changed state to {new_state}."
-
- query_url = f"{host}/queries/{query.id}"
- alert_url = f"{host}/alerts/{alert.id}"
- text += f"\nQuery: {query_url}\nAlert: {alert_url}"
-
- headers = {
- "Accept": "application/json",
- "Content-Type": "application/json",
- "DD-API-KEY": options.get("api_key"),
- }
-
- body = {
- "title": title,
- "text": text,
- "alert_type": alert_type,
- "priority": options.get("priority"),
- "source_type_name": options.get("source_type_name"),
- "aggregation_key": f"redash:{alert_url}",
- "tags": [],
- }
-
- tags = options.get("tags")
- if tags:
- body["tags"] = tags.split(",")
- body["tags"].extend(
- [
- "redash",
- f"query_id:{query.id}",
- f"alert_id:{alert.id}",
- ]
- )
-
- dd_host = os.getenv("DATADOG_HOST", "api.datadoghq.com")
- url = f"https://{dd_host}/api/v1/events"
-
- try:
- resp = requests.post(url, headers=headers, data=json_dumps(body), timeout=5.0)
- logging.warning(resp.text)
- if resp.status_code != 202:
- logging.error(f"Datadog send ERROR. status_code => {resp.status_code}")
- except Exception as e:
- logging.exception("Datadog send ERROR: %s", e)
-
-
-register(Datadog)
diff --git a/redash/destinations/discord.py b/redash/destinations/discord.py
deleted file mode 100644
index c6deca20f6..0000000000
--- a/redash/destinations/discord.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import logging
-
-import requests
-
-from redash.destinations import BaseDestination, register
-from redash.models import Alert
-from redash.utils import json_dumps
-
-colors = {
- # Colors are in a Decimal format as Discord requires them to be Decimals for embeds
- Alert.OK_STATE: "2600544", # Green Decimal Code
- Alert.TRIGGERED_STATE: "12597547", # Red Decimal Code
- Alert.UNKNOWN_STATE: "16776960", # Yellow Decimal Code
-}
-
-
-class Discord(BaseDestination):
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {"url": {"type": "string", "title": "Discord Webhook URL"}},
- "secret": ["url"],
- "required": ["url"],
- }
-
- @classmethod
- def icon(cls):
- return "fa-discord"
-
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
- # Documentation: https://birdie0.github.io/discord-webhooks-guide/discord_webhook.html
- fields = [
- {
- "name": "Query",
- "value": f"{host}/queries/{query.id}",
- "inline": True,
- },
- {
- "name": "Alert",
- "value": f"{host}/alerts/{alert.id}",
- "inline": True,
- },
- ]
- if alert.custom_body:
- fields.append({"name": "Description", "value": alert.custom_body})
- if new_state == Alert.TRIGGERED_STATE:
- if alert.options.get("custom_subject"):
- text = alert.options["custom_subject"]
- else:
- text = f"{alert.name} just triggered"
- else:
- text = f"{alert.name} went back to normal"
- color = colors.get(new_state)
- payload = {"content": text, "embeds": [{"color": color, "fields": fields}]}
- headers = {"Content-Type": "application/json"}
- try:
- resp = requests.post(
- options.get("url"),
- data=json_dumps(payload),
- headers=headers,
- timeout=5.0,
- )
- if resp.status_code != 200 and resp.status_code != 204:
- logging.error(f"Discord send ERROR. status_code => {resp.status_code}")
- except Exception as e:
- logging.exception("Discord send ERROR: %s", e)
-
-
-register(Discord)
diff --git a/redash/destinations/email.py b/redash/destinations/email.py
index cb835212c7..11923aa2ed 100644
--- a/redash/destinations/email.py
+++ b/redash/destinations/email.py
@@ -1,9 +1,8 @@
import logging
from flask_mail import Message
-
from redash import mail, settings
-from redash.destinations import BaseDestination, register
+from redash.destinations import *
class Email(BaseDestination):
@@ -27,8 +26,10 @@ def configuration_schema(cls):
def icon(cls):
return "fa-envelope"
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
- recipients = [email for email in options.get("addresses", "").split(",") if email]
+ def notify(self, alert, query, user, new_state, app, host, options):
+ recipients = [
+ email for email in options.get("addresses", "").split(",") if email
+ ]
if not recipients:
logging.warning("No emails given. Skipping send.")
@@ -36,8 +37,12 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
if alert.custom_body:
html = alert.custom_body
else:
- with open(settings.REDASH_ALERTS_DEFAULT_MAIL_BODY_TEMPLATE_FILE, "r") as f:
- html = alert.render_template(f.read())
+ html = """
+ Check alert / check
+ query .
+ """.format(
+ host=host, alert_id=alert.id, query_id=query.id
+ )
logging.debug("Notifying: %s", recipients)
try:
@@ -45,7 +50,9 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
if alert.custom_subject:
subject = alert.custom_subject
else:
- subject_template = options.get("subject_template", settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE)
+ subject_template = options.get(
+ "subject_template", settings.ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE
+ )
subject = subject_template.format(alert_name=alert.name, state=state)
message = Message(recipients=recipients, subject=subject, html=html)
diff --git a/redash/destinations/hangoutschat.py b/redash/destinations/hangoutschat.py
index f090662d50..bc52f3de69 100644
--- a/redash/destinations/hangoutschat.py
+++ b/redash/destinations/hangoutschat.py
@@ -1,8 +1,7 @@
import logging
-
import requests
-from redash.destinations import BaseDestination, register
+from redash.destinations import *
from redash.utils import json_dumps
@@ -29,7 +28,6 @@ def configuration_schema(cls):
"title": "Icon URL (32x32 or multiple, png format)",
},
},
- "secret": ["url"],
"required": ["url"],
}
@@ -37,14 +35,16 @@ def configuration_schema(cls):
def icon(cls):
return "fa-bolt"
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
+ def notify(self, alert, query, user, new_state, app, host, options):
try:
if new_state == "triggered":
message = 'Triggered'
elif new_state == "ok":
message = 'Went back to normal'
else:
- message = "Unable to determine status. Check Query and Alert configuration."
+ message = (
+ "Unable to determine status. Check Query and Alert configuration."
+ )
if alert.custom_subject:
title = alert.custom_subject
@@ -55,13 +55,17 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
"cards": [
{
"header": {"title": title},
- "sections": [{"widgets": [{"textParagraph": {"text": message}}]}],
+ "sections": [
+ {"widgets": [{"textParagraph": {"text": message}}]}
+ ],
}
]
}
if alert.custom_body:
- data["cards"][0]["sections"].append({"widgets": [{"textParagraph": {"text": alert.custom_body}}]})
+ data["cards"][0]["sections"].append(
+ {"widgets": [{"textParagraph": {"text": alert.custom_body}}]}
+ )
if options.get("icon_url"):
data["cards"][0]["header"]["imageUrl"] = options.get("icon_url")
@@ -76,7 +80,9 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
"text": "OPEN QUERY",
"onClick": {
"openLink": {
- "url": "{host}/queries/{query_id}".format(host=host, query_id=query.id)
+ "url": "{host}/queries/{query_id}".format(
+ host=host, query_id=query.id
+ )
}
},
}
@@ -86,9 +92,15 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
)
headers = {"Content-Type": "application/json; charset=UTF-8"}
- resp = requests.post(options.get("url"), data=json_dumps(data), headers=headers, timeout=5.0)
+ resp = requests.post(
+ options.get("url"), data=json_dumps(data), headers=headers, timeout=5.0
+ )
if resp.status_code != 200:
- logging.error("webhook send ERROR. status_code => {status}".format(status=resp.status_code))
+ logging.error(
+ "webhook send ERROR. status_code => {status}".format(
+ status=resp.status_code
+ )
+ )
except Exception:
logging.exception("webhook send ERROR.")
diff --git a/redash/destinations/hipchat.py b/redash/destinations/hipchat.py
new file mode 100644
index 0000000000..add7ee1a60
--- /dev/null
+++ b/redash/destinations/hipchat.py
@@ -0,0 +1,61 @@
+import logging
+import requests
+
+from redash.destinations import *
+from redash.models import Alert
+from redash.utils import json_dumps, deprecated
+
+
+colors = {
+ Alert.OK_STATE: "green",
+ Alert.TRIGGERED_STATE: "red",
+ Alert.UNKNOWN_STATE: "yellow",
+}
+
+
+@deprecated()
+class HipChat(BaseDestination):
+ @classmethod
+ def configuration_schema(cls):
+ return {
+ "type": "object",
+ "properties": {
+ "url": {
+ "type": "string",
+ "title": "HipChat Notification URL (get it from the Integrations page)",
+ }
+ },
+ "required": ["url"],
+ }
+
+ @classmethod
+ def icon(cls):
+ return "fa-comment-o"
+
+ def notify(self, alert, query, user, new_state, app, host, options):
+ try:
+ alert_url = "{host}/alerts/{alert_id}".format(host=host, alert_id=alert.id)
+ query_url = "{host}/queries/{query_id}".format(host=host, query_id=query.id)
+
+ message = '{alert_name} changed state to {new_state} (based on this query).'.format(
+ alert_name=alert.name,
+ new_state=new_state.upper(),
+ alert_url=alert_url,
+ query_url=query_url,
+ )
+
+ data = {"message": message, "color": colors.get(new_state, "green")}
+ headers = {"Content-Type": "application/json"}
+ response = requests.post(
+ options["url"], data=json_dumps(data), headers=headers, timeout=5.0
+ )
+
+ if response.status_code != 204:
+ logging.error(
+ "Bad status code received from HipChat: %d", response.status_code
+ )
+ except Exception:
+ logging.exception("HipChat Send ERROR.")
+
+
+register(HipChat)
diff --git a/redash/destinations/mattermost.py b/redash/destinations/mattermost.py
index 6254ecf71e..5d601ff6ff 100644
--- a/redash/destinations/mattermost.py
+++ b/redash/destinations/mattermost.py
@@ -1,8 +1,7 @@
import logging
-
import requests
-from redash.destinations import BaseDestination, register
+from redash.destinations import *
from redash.utils import json_dumps
@@ -17,14 +16,13 @@ def configuration_schema(cls):
"icon_url": {"type": "string", "title": "Icon (URL)"},
"channel": {"type": "string", "title": "Channel"},
},
- "secret": "url",
}
@classmethod
def icon(cls):
return "fa-bolt"
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
+ def notify(self, alert, query, user, new_state, app, host, options):
if alert.custom_subject:
text = alert.custom_subject
elif new_state == "triggered":
@@ -34,7 +32,9 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
payload = {"text": text}
if alert.custom_body:
- payload["attachments"] = [{"fields": [{"title": "Description", "value": alert.custom_body}]}]
+ payload["attachments"] = [
+ {"fields": [{"title": "Description", "value": alert.custom_body}]}
+ ]
if options.get("username"):
payload["username"] = options.get("username")
@@ -44,11 +44,17 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
payload["channel"] = options.get("channel")
try:
- resp = requests.post(options.get("url"), data=json_dumps(payload), timeout=5.0)
+ resp = requests.post(
+ options.get("url"), data=json_dumps(payload), timeout=5.0
+ )
logging.warning(resp.text)
if resp.status_code != 200:
- logging.error("Mattermost webhook send ERROR. status_code => {status}".format(status=resp.status_code))
+ logging.error(
+ "Mattermost webhook send ERROR. status_code => {status}".format(
+ status=resp.status_code
+ )
+ )
except Exception:
logging.exception("Mattermost webhook send ERROR.")
diff --git a/redash/destinations/microsoft_teams_webhook.py b/redash/destinations/microsoft_teams_webhook.py
deleted file mode 100644
index 8c64d93534..0000000000
--- a/redash/destinations/microsoft_teams_webhook.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import logging
-from string import Template
-
-import requests
-
-from redash.destinations import BaseDestination, register
-from redash.utils import json_dumps
-
-
-def json_string_substitute(j, substitutions):
- """
- Alternative to string.format when the string has braces.
- :param j: json string that will have substitutions
- :type j: str
- :param substitutions: dictionary of values to be replaced
- :type substitutions: dict
- """
- if substitutions:
- substitution_candidate = j.replace("{", "${")
- string_template = Template(substitution_candidate)
- substituted = string_template.safe_substitute(substitutions)
- out_str = substituted.replace("${", "{")
- return out_str
- else:
- return j
-
-
-class MicrosoftTeamsWebhook(BaseDestination):
- ALERTS_DEFAULT_MESSAGE_TEMPLATE = json_dumps(
- {
- "@type": "MessageCard",
- "@context": "http://schema.org/extensions",
- "themeColor": "0076D7",
- "summary": "A Redash Alert was Triggered",
- "sections": [
- {
- "activityTitle": "A Redash Alert was Triggered",
- "facts": [
- {"name": "Alert Name", "value": "{alert_name}"},
- {"name": "Alert URL", "value": "{alert_url}"},
- {"name": "Query", "value": "{query_text}"},
- {"name": "Query URL", "value": "{query_url}"},
- ],
- "markdown": True,
- }
- ],
- }
- )
-
- @classmethod
- def name(cls):
- return "Microsoft Teams Webhook"
-
- @classmethod
- def type(cls):
- return "microsoft_teams_webhook"
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "url": {"type": "string", "title": "Microsoft Teams Webhook URL"},
- "message_template": {
- "type": "string",
- "default": MicrosoftTeamsWebhook.ALERTS_DEFAULT_MESSAGE_TEMPLATE,
- "title": "Message Template",
- },
- },
- "required": ["url"],
- }
-
- @classmethod
- def icon(cls):
- return "fa-bolt"
-
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
- """
- :type app: redash.Redash
- """
- try:
- alert_url = "{host}/alerts/{alert_id}".format(host=host, alert_id=alert.id)
-
- query_url = "{host}/queries/{query_id}".format(host=host, query_id=query.id)
-
- message_template = options.get("message_template", MicrosoftTeamsWebhook.ALERTS_DEFAULT_MESSAGE_TEMPLATE)
-
- # Doing a string Template substitution here because the template contains braces, which
- # result in keyerrors when attempting string.format
- payload = json_string_substitute(
- message_template,
- {
- "alert_name": alert.name,
- "alert_url": alert_url,
- "query_text": query.query_text,
- "query_url": query_url,
- },
- )
-
- headers = {"Content-Type": "application/json"}
-
- resp = requests.post(
- options.get("url"),
- data=payload,
- headers=headers,
- timeout=5.0,
- )
- if resp.status_code != 200:
- logging.error("MS Teams Webhook send ERROR. status_code => {status}".format(status=resp.status_code))
- except Exception:
- logging.exception("MS Teams Webhook send ERROR.")
-
-
-register(MicrosoftTeamsWebhook)
diff --git a/redash/destinations/pagerduty.py b/redash/destinations/pagerduty.py
index 570c876da3..3a844fa10d 100644
--- a/redash/destinations/pagerduty.py
+++ b/redash/destinations/pagerduty.py
@@ -1,6 +1,5 @@
import logging
-
-from redash.destinations import BaseDestination, register
+from redash.destinations import *
enabled = True
@@ -11,6 +10,7 @@
class PagerDuty(BaseDestination):
+
KEY_STRING = "{alert_id}_{query_id}"
DESCRIPTION_STR = "Alert: {alert_name}"
@@ -32,7 +32,6 @@ def configuration_schema(cls):
"title": "Description for the event, defaults to alert name",
},
},
- "secret": ["integration_key"],
"required": ["integration_key"],
}
@@ -40,7 +39,8 @@ def configuration_schema(cls):
def icon(cls):
return "creative-commons-pd-alt"
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
+ def notify(self, alert, query, user, new_state, app, host, options):
+
if alert.custom_subject:
default_desc = alert.custom_subject
elif options.get("description"):
@@ -72,6 +72,7 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
data["event_action"] = "resolve"
try:
+
ev = pypd.EventV2.create(data=data)
logging.warning(ev)
diff --git a/redash/destinations/slack.py b/redash/destinations/slack.py
index a7e44b6a7c..edbd6f2c2f 100644
--- a/redash/destinations/slack.py
+++ b/redash/destinations/slack.py
@@ -1,8 +1,7 @@
import logging
-
import requests
-from redash.destinations import BaseDestination, register
+from redash.destinations import *
from redash.utils import json_dumps
@@ -13,26 +12,33 @@ def configuration_schema(cls):
"type": "object",
"properties": {
"url": {"type": "string", "title": "Slack Webhook URL"},
+ "username": {"type": "string", "title": "Username"},
+ "icon_emoji": {"type": "string", "title": "Icon (Emoji)"},
+ "icon_url": {"type": "string", "title": "Icon (URL)"},
+ "channel": {"type": "string", "title": "Channel"},
},
- "secret": ["url"],
}
@classmethod
def icon(cls):
return "fa-slack"
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
+ def notify(self, alert, query, user, new_state, app, host, options):
# Documentation: https://api.slack.com/docs/attachments
fields = [
{
"title": "Query",
- "type": "mrkdwn",
- "value": "{host}/queries/{query_id}".format(host=host, query_id=query.id),
+ "value": "{host}/queries/{query_id}".format(
+ host=host, query_id=query.id
+ ),
+ "short": True,
},
{
"title": "Alert",
- "type": "mrkdwn",
- "value": "{host}/alerts/{alert_id}".format(host=host, alert_id=alert.id),
+ "value": "{host}/alerts/{alert_id}".format(
+ host=host, alert_id=alert.id
+ ),
+ "short": True,
},
]
if alert.custom_body:
@@ -49,11 +55,26 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
payload = {"attachments": [{"text": text, "color": color, "fields": fields}]}
+ if options.get("username"):
+ payload["username"] = options.get("username")
+ if options.get("icon_emoji"):
+ payload["icon_emoji"] = options.get("icon_emoji")
+ if options.get("icon_url"):
+ payload["icon_url"] = options.get("icon_url")
+ if options.get("channel"):
+ payload["channel"] = options.get("channel")
+
try:
- resp = requests.post(options.get("url"), data=json_dumps(payload).encode("utf-8"), timeout=5.0)
+ resp = requests.post(
+ options.get("url"), data=json_dumps(payload), timeout=5.0
+ )
logging.warning(resp.text)
if resp.status_code != 200:
- logging.error("Slack send ERROR. status_code => {status}".format(status=resp.status_code))
+ logging.error(
+ "Slack send ERROR. status_code => {status}".format(
+ status=resp.status_code
+ )
+ )
except Exception:
logging.exception("Slack send ERROR.")
diff --git a/redash/destinations/webex.py b/redash/destinations/webex.py
deleted file mode 100644
index 599c485e36..0000000000
--- a/redash/destinations/webex.py
+++ /dev/null
@@ -1,230 +0,0 @@
-import html
-import json
-import logging
-from copy import deepcopy
-
-import requests
-
-from redash.destinations import BaseDestination, register
-from redash.models import Alert
-
-
-class Webex(BaseDestination):
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "webex_bot_token": {"type": "string", "title": "Webex Bot Token"},
- "to_person_emails": {
- "type": "string",
- "title": "People (comma-separated)",
- },
- "to_room_ids": {
- "type": "string",
- "title": "Rooms (comma-separated)",
- },
- },
- "secret": ["webex_bot_token"],
- "required": ["webex_bot_token"],
- }
-
- @classmethod
- def icon(cls):
- return "fa-webex"
-
- @property
- def api_base_url(self):
- return "https://webexapis.com/v1/messages"
-
- @staticmethod
- def formatted_attachments_template(subject, description, query_link, alert_link):
- # Attempt to parse the description to find a 2D array
- try:
- # Extract the part of the description that looks like a JSON array
- start_index = description.find("[")
- end_index = description.rfind("]") + 1
- json_array_str = description[start_index:end_index]
-
- # Decode HTML entities
- json_array_str = html.unescape(json_array_str)
-
- # Replace single quotes with double quotes for valid JSON
- json_array_str = json_array_str.replace("'", '"')
-
- # Load the JSON array
- data_array = json.loads(json_array_str)
-
- # Check if it's a 2D array
- if isinstance(data_array, list) and all(isinstance(i, list) for i in data_array):
- # Create a table for the Adaptive Card
- table_rows = []
- for row in data_array:
- table_rows.append(
- {
- "type": "ColumnSet",
- "columns": [
- {"type": "Column", "items": [{"type": "TextBlock", "text": str(item), "wrap": True}]}
- for item in row
- ],
- }
- )
-
- # Create the body of the card with the table
- body = (
- [
- {
- "type": "TextBlock",
- "text": f"{subject}",
- "weight": "bolder",
- "size": "medium",
- "wrap": True,
- },
- {
- "type": "TextBlock",
- "text": f"{description[:start_index]}",
- "isSubtle": True,
- "wrap": True,
- },
- ]
- + table_rows
- + [
- {
- "type": "TextBlock",
- "text": f"Click [here]({query_link}) to check your query!",
- "wrap": True,
- "isSubtle": True,
- },
- {
- "type": "TextBlock",
- "text": f"Click [here]({alert_link}) to check your alert!",
- "wrap": True,
- "isSubtle": True,
- },
- ]
- )
- else:
- # Fallback to the original description if no valid 2D array is found
- body = [
- {
- "type": "TextBlock",
- "text": f"{subject}",
- "weight": "bolder",
- "size": "medium",
- "wrap": True,
- },
- {
- "type": "TextBlock",
- "text": f"{description}",
- "isSubtle": True,
- "wrap": True,
- },
- {
- "type": "TextBlock",
- "text": f"Click [here]({query_link}) to check your query!",
- "wrap": True,
- "isSubtle": True,
- },
- {
- "type": "TextBlock",
- "text": f"Click [here]({alert_link}) to check your alert!",
- "wrap": True,
- "isSubtle": True,
- },
- ]
- except json.JSONDecodeError:
- # If parsing fails, fallback to the original description
- body = [
- {
- "type": "TextBlock",
- "text": f"{subject}",
- "weight": "bolder",
- "size": "medium",
- "wrap": True,
- },
- {
- "type": "TextBlock",
- "text": f"{description}",
- "isSubtle": True,
- "wrap": True,
- },
- {
- "type": "TextBlock",
- "text": f"Click [here]({query_link}) to check your query!",
- "wrap": True,
- "isSubtle": True,
- },
- {
- "type": "TextBlock",
- "text": f"Click [here]({alert_link}) to check your alert!",
- "wrap": True,
- "isSubtle": True,
- },
- ]
-
- return [
- {
- "contentType": "application/vnd.microsoft.card.adaptive",
- "content": {
- "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
- "type": "AdaptiveCard",
- "version": "1.0",
- "body": body,
- },
- }
- ]
-
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
- # Documentation: https://developer.webex.com/docs/api/guides/cards
-
- query_link = f"{host}/queries/{query.id}"
- alert_link = f"{host}/alerts/{alert.id}"
-
- if new_state == Alert.TRIGGERED_STATE:
- subject = alert.custom_subject or f"{alert.name} just triggered"
- else:
- subject = f"{alert.name} went back to normal"
-
- attachments = self.formatted_attachments_template(
- subject=subject, description=alert.custom_body, query_link=query_link, alert_link=alert_link
- )
-
- template_payload = {"markdown": subject + "\n" + alert.custom_body, "attachments": attachments}
-
- headers = {"Authorization": f"Bearer {options['webex_bot_token']}"}
-
- api_destinations = {
- "toPersonEmail": options.get("to_person_emails"),
- "roomId": options.get("to_room_ids"),
- }
-
- for payload_tag, destinations in api_destinations.items():
- if destinations is None:
- continue
-
- # destinations is guaranteed to be a comma-separated string
- for destination_id in destinations.split(","):
- destination_id = destination_id.strip() # Remove any leading or trailing whitespace
- if not destination_id: # Check if the destination_id is empty or blank
- continue # Skip to the next iteration if it's empty or blank
-
- payload = deepcopy(template_payload)
- payload[payload_tag] = destination_id
- self.post_message(payload, headers)
-
- def post_message(self, payload, headers):
- try:
- resp = requests.post(
- self.api_base_url,
- json=payload,
- headers=headers,
- timeout=5.0,
- )
- logging.warning(resp.text)
- if resp.status_code != 200:
- logging.error("Webex send ERROR. status_code => {status}".format(status=resp.status_code))
- except Exception as e:
- logging.exception(f"Webex send ERROR: {e}")
-
-
-register(Webex)
diff --git a/redash/destinations/webhook.py b/redash/destinations/webhook.py
index b98b6f18a3..83581e9e37 100644
--- a/redash/destinations/webhook.py
+++ b/redash/destinations/webhook.py
@@ -1,11 +1,10 @@
import logging
-
import requests
from requests.auth import HTTPBasicAuth
-from redash.destinations import BaseDestination, register
-from redash.serializers import serialize_alert
+from redash.destinations import *
from redash.utils import json_dumps
+from redash.serializers import serialize_alert
class Webhook(BaseDestination):
@@ -19,27 +18,30 @@ def configuration_schema(cls):
"password": {"type": "string"},
},
"required": ["url"],
- "secret": ["password", "url"],
+ "secret": ["password"],
}
@classmethod
def icon(cls):
return "fa-bolt"
- def notify(self, alert, query, user, new_state, app, host, metadata, options):
+ def notify(self, alert, query, user, new_state, app, host, options):
try:
data = {
"event": "alert_state_change",
"alert": serialize_alert(alert, full=False),
"url_base": host,
- "metadata": metadata,
}
data["alert"]["description"] = alert.custom_body
data["alert"]["title"] = alert.custom_subject
headers = {"Content-Type": "application/json"}
- auth = HTTPBasicAuth(options.get("username"), options.get("password")) if options.get("username") else None
+ auth = (
+ HTTPBasicAuth(options.get("username"), options.get("password"))
+ if options.get("username")
+ else None
+ )
resp = requests.post(
options.get("url"),
data=json_dumps(data),
@@ -48,7 +50,11 @@ def notify(self, alert, query, user, new_state, app, host, metadata, options):
timeout=5.0,
)
if resp.status_code != 200:
- logging.error("webhook send ERROR. status_code => {status}".format(status=resp.status_code))
+ logging.error(
+ "webhook send ERROR. status_code => {status}".format(
+ status=resp.status_code
+ )
+ )
except Exception:
logging.exception("webhook send ERROR.")
diff --git a/redash/extensions.py b/redash/extensions.py
new file mode 100644
index 0000000000..56452d1d13
--- /dev/null
+++ b/redash/extensions.py
@@ -0,0 +1,107 @@
+import logging
+from collections import OrderedDict as odict
+
+from importlib_metadata import entry_points
+
+# The global Redash extension registry
+extensions = odict()
+
+# The periodic RQ jobs as provided by Redash extensions.
+# This is separate from the internal periodic RQ jobs
+# since the extension job discovery phase is
+# after the configuration has already happened.
+periodic_jobs = odict()
+
+extension_logger = logging.getLogger(__name__)
+
+
+def entry_point_loader(group_name, mapping, logger=None, *args, **kwargs):
+ """
+ Loads the list Python entry points with the given entry point group name
+ (e.g. "redash.extensions"), calls each with the provided *args/**kwargs
+ arguments and stores the results in the provided mapping under the name
+ of the entry point.
+
+ If provided, the logger is used for error and debugging statements.
+ """
+ if logger is None:
+ logger = extension_logger
+
+ for entry_point in entry_points().get(group_name, []):
+ logger.info('Loading entry point "%s".', entry_point.name)
+ try:
+ # Then try to load the entry point (import and getattr)
+ obj = entry_point.load()
+ except (ImportError, AttributeError):
+ # or move on
+ logger.error(
+ 'Entry point "%s" could not be found.', entry_point.name, exc_info=True
+ )
+ continue
+
+ if not callable(obj):
+ logger.error('Entry point "%s" is not a callable.', entry_point.name)
+ continue
+
+ try:
+ # then simply call the loaded entry point.
+ mapping[entry_point.name] = obj(*args, **kwargs)
+ except AssertionError:
+ logger.error(
+ 'Entry point "%s" cound not be loaded.', entry_point.name, exc_info=True
+ )
+ continue
+
+
+def load_extensions(app):
+ """Load the Redash extensions for the given Redash Flask app.
+
+ The extension entry point can return any type of value but
+ must take a Flask application object.
+
+ E.g.::
+
+ def extension(app):
+ app.logger.info("Loading the Foobar extenions")
+ Foobar(app)
+
+ """
+ entry_point_loader("redash.extensions", extensions, logger=app.logger, app=app)
+
+
+def load_periodic_jobs(logger=None):
+ """Load the periodic jobs as defined in Redash extensions.
+
+ The periodic task entry point needs to return a set of parameters
+ that can be passed to RQ Scheduler API:
+
+ https://github.com/rq/rq-scheduler#periodic--repeated-jobs
+
+ E.g.::
+
+ def add_two_and_two():
+ return {
+ "func": add,
+ "args": [2, 2]
+ "interval": 10, # in seconds or as a timedelta
+ }
+
+ and then registered with an entry point under the "redash.periodic_jobs"
+ group, e.g. in your setup.py::
+
+ setup(
+ # ...
+ entry_points={
+ "redash.periodic_jobs": [
+ "add_two_and_two = calculus.addition:add_two_and_two",
+ ]
+ # ...
+ },
+ # ...
+ )
+ """
+ entry_point_loader("redash.periodic_jobs", periodic_jobs, logger=logger)
+
+
+def init_app(app):
+ load_extensions(app)
diff --git a/redash/handlers/__init__.py b/redash/handlers/__init__.py
index 8c6e61d8fe..a7a05ed0f7 100644
--- a/redash/handlers/__init__.py
+++ b/redash/handlers/__init__.py
@@ -24,13 +24,13 @@ def status_api():
def init_app(app):
from redash.handlers import (
- admin,
- authentication,
embed,
- organization,
queries,
- setup,
static,
+ authentication,
+ admin,
+ setup,
+ organization,
)
app.register_blueprint(routes)
diff --git a/redash/handlers/admin.py b/redash/handlers/admin.py
index b376beec25..753494d341 100644
--- a/redash/handlers/admin.py
+++ b/redash/handlers/admin.py
@@ -1,13 +1,14 @@
-from flask_login import current_user, login_required
+from flask import request
+from flask_login import login_required, current_user
from redash import models, redis_connection
from redash.authentication import current_org
from redash.handlers import routes
from redash.handlers.base import json_response, record_event
-from redash.monitor import rq_status
from redash.permissions import require_super_admin
from redash.serializers import QuerySerializer
from redash.utils import json_loads
+from redash.monitor import rq_status
@routes.route("/api/admin/queries/outdated", methods=["GET"])
@@ -28,14 +29,13 @@ def outdated_queries():
record_event(
current_org,
current_user._get_current_object(),
- {
- "action": "list",
- "object_type": "outdated_queries",
- },
+ {"action": "list", "object_type": "outdated_queries",},
)
response = {
- "queries": QuerySerializer(outdated_queries, with_stats=True, with_last_modified_by=False).serialize(),
+ "queries": QuerySerializer(
+ outdated_queries, with_stats=True, with_last_modified_by=False
+ ).serialize(),
"updated_at": manager_status["last_refresh_at"],
}
return json_response(response)
diff --git a/redash/handlers/alerts.py b/redash/handlers/alerts.py
index 5e107ebb41..7929df9c05 100644
--- a/redash/handlers/alerts.py
+++ b/redash/handlers/alerts.py
@@ -1,85 +1,83 @@
+import time
+
from flask import request
from funcy import project
-from redash import models, utils
-from redash.handlers.base import (
- BaseResource,
- get_object_or_404,
- require_fields,
-)
+from redash import models
+from redash.serializers import serialize_alert
+from redash.handlers.base import BaseResource, get_object_or_404, require_fields
from redash.permissions import (
require_access,
require_admin_or_owner,
require_permission,
view_only,
)
-from redash.serializers import serialize_alert
-from redash.tasks.alerts import (
- notify_subscriptions,
- should_notify,
-)
+from redash.utils import json_dumps
class AlertResource(BaseResource):
def get(self, alert_id):
- alert = get_object_or_404(models.Alert.get_by_id_and_org, alert_id, self.current_org)
+ alert = get_object_or_404(
+ models.Alert.get_by_id_and_org, alert_id, self.current_org
+ )
require_access(alert, self.current_user, view_only)
- self.record_event({"action": "view", "object_id": alert.id, "object_type": "alert"})
+ self.record_event(
+ {"action": "view", "object_id": alert.id, "object_type": "alert"}
+ )
return serialize_alert(alert)
def post(self, alert_id):
req = request.get_json(True)
params = project(req, ("options", "name", "query_id", "rearm"))
- alert = get_object_or_404(models.Alert.get_by_id_and_org, alert_id, self.current_org)
+ alert = get_object_or_404(
+ models.Alert.get_by_id_and_org, alert_id, self.current_org
+ )
require_admin_or_owner(alert.user.id)
self.update_model(alert, params)
models.db.session.commit()
- self.record_event({"action": "edit", "object_id": alert.id, "object_type": "alert"})
+ self.record_event(
+ {"action": "edit", "object_id": alert.id, "object_type": "alert"}
+ )
return serialize_alert(alert)
def delete(self, alert_id):
- alert = get_object_or_404(models.Alert.get_by_id_and_org, alert_id, self.current_org)
+ alert = get_object_or_404(
+ models.Alert.get_by_id_and_org, alert_id, self.current_org
+ )
require_admin_or_owner(alert.user_id)
models.db.session.delete(alert)
models.db.session.commit()
-class AlertEvaluateResource(BaseResource):
- def post(self, alert_id):
- alert = get_object_or_404(models.Alert.get_by_id_and_org, alert_id, self.current_org)
- require_admin_or_owner(alert.user.id)
-
- new_state = alert.evaluate()
- if should_notify(alert, new_state):
- alert.state = new_state
- alert.last_triggered_at = utils.utcnow()
- models.db.session.commit()
-
- notify_subscriptions(alert, new_state, {})
- self.record_event({"action": "evaluate", "object_id": alert.id, "object_type": "alert"})
-
-
class AlertMuteResource(BaseResource):
def post(self, alert_id):
- alert = get_object_or_404(models.Alert.get_by_id_and_org, alert_id, self.current_org)
+ alert = get_object_or_404(
+ models.Alert.get_by_id_and_org, alert_id, self.current_org
+ )
require_admin_or_owner(alert.user.id)
alert.options["muted"] = True
models.db.session.commit()
- self.record_event({"action": "mute", "object_id": alert.id, "object_type": "alert"})
+ self.record_event(
+ {"action": "mute", "object_id": alert.id, "object_type": "alert"}
+ )
def delete(self, alert_id):
- alert = get_object_or_404(models.Alert.get_by_id_and_org, alert_id, self.current_org)
+ alert = get_object_or_404(
+ models.Alert.get_by_id_and_org, alert_id, self.current_org
+ )
require_admin_or_owner(alert.user.id)
alert.options["muted"] = False
models.db.session.commit()
- self.record_event({"action": "unmute", "object_id": alert.id, "object_type": "alert"})
+ self.record_event(
+ {"action": "unmute", "object_id": alert.id, "object_type": "alert"}
+ )
class AlertListResource(BaseResource):
@@ -102,14 +100,19 @@ def post(self):
models.db.session.flush()
models.db.session.commit()
- self.record_event({"action": "create", "object_id": alert.id, "object_type": "alert"})
+ self.record_event(
+ {"action": "create", "object_id": alert.id, "object_type": "alert"}
+ )
return serialize_alert(alert)
@require_permission("list_alerts")
def get(self):
self.record_event({"action": "list", "object_type": "alert"})
- return [serialize_alert(alert) for alert in models.Alert.all(group_ids=self.current_user.group_ids)]
+ return [
+ serialize_alert(alert)
+ for alert in models.Alert.all(group_ids=self.current_user.group_ids)
+ ]
class AlertSubscriptionListResource(BaseResource):
@@ -121,7 +124,9 @@ def post(self, alert_id):
kwargs = {"alert": alert, "user": self.current_user}
if "destination_id" in req:
- destination = models.NotificationDestination.get_by_id_and_org(req["destination_id"], self.current_org)
+ destination = models.NotificationDestination.get_by_id_and_org(
+ req["destination_id"], self.current_org
+ )
kwargs["destination"] = destination
subscription = models.AlertSubscription(**kwargs)
@@ -141,6 +146,7 @@ def post(self, alert_id):
return d
def get(self, alert_id):
+ alert_id = int(alert_id)
alert = models.Alert.get_by_id_and_org(alert_id, self.current_org)
require_access(alert, self.current_user, view_only)
@@ -155,4 +161,6 @@ def delete(self, alert_id, subscriber_id):
models.db.session.delete(subscription)
models.db.session.commit()
- self.record_event({"action": "unsubscribe", "object_id": alert_id, "object_type": "alert"})
+ self.record_event(
+ {"action": "unsubscribe", "object_id": alert_id, "object_type": "alert"}
+ )
diff --git a/redash/handlers/api.py b/redash/handlers/api.py
index 643bee371f..0a09d32935 100644
--- a/redash/handlers/api.py
+++ b/redash/handlers/api.py
@@ -3,22 +3,19 @@
from werkzeug.wrappers import Response
from redash.handlers.alerts import (
- AlertEvaluateResource,
AlertListResource,
- AlertMuteResource,
AlertResource,
+ AlertMuteResource,
AlertSubscriptionListResource,
AlertSubscriptionResource,
)
from redash.handlers.base import org_scoped_rule
from redash.handlers.dashboards import (
DashboardFavoriteListResource,
- DashboardForkResource,
DashboardListResource,
DashboardResource,
DashboardShareResource,
DashboardTagsResource,
- MyDashboardsResource,
PublicDashboardResource,
)
from redash.handlers.data_sources import (
@@ -28,11 +25,11 @@
DataSourceSchemaResource,
DataSourceTestResource,
DataSourceTypeListResource,
+ DataSourceToggleStringResource,
)
from redash.handlers.databricks import (
DatabricksDatabaseListResource,
DatabricksSchemaResource,
- DatabricksTableColumnListResource,
)
from redash.handlers.destinations import (
DestinationListResource,
@@ -40,10 +37,7 @@
DestinationTypeListResource,
)
from redash.handlers.events import EventsResource
-from redash.handlers.favorites import (
- DashboardFavoriteResource,
- QueryFavoriteResource,
-)
+from redash.handlers.favorites import DashboardFavoriteResource, QueryFavoriteResource
from redash.handlers.groups import (
GroupDataSourceListResource,
GroupDataSourceResource,
@@ -64,15 +58,15 @@
QueryListResource,
QueryRecentResource,
QueryRefreshResource,
- QueryRegenerateApiKeyResource,
QueryResource,
QuerySearchResource,
QueryTagsResource,
+ QueryRegenerateApiKeyResource,
)
from redash.handlers.query_results import (
JobResource,
- QueryDropdownsResource,
QueryResultDropdownResource,
+ QueryDropdownsResource,
QueryResultListResource,
QueryResultResource,
)
@@ -117,8 +111,9 @@ def json_representation(data, code, headers=None):
api.add_org_resource(AlertResource, "/api/alerts/", endpoint="alert")
-api.add_org_resource(AlertMuteResource, "/api/alerts//mute", endpoint="alert_mute")
-api.add_org_resource(AlertEvaluateResource, "/api/alerts//eval", endpoint="alert_eval")
+api.add_org_resource(
+ AlertMuteResource, "/api/alerts//mute", endpoint="alert_mute"
+)
api.add_org_resource(
AlertSubscriptionListResource,
"/api/alerts//subscriptions",
@@ -132,7 +127,9 @@ def json_representation(data, code, headers=None):
api.add_org_resource(AlertListResource, "/api/alerts", endpoint="alerts")
api.add_org_resource(DashboardListResource, "/api/dashboards", endpoint="dashboards")
-api.add_org_resource(DashboardResource, "/api/dashboards/", endpoint="dashboard")
+api.add_org_resource(
+ DashboardResource, "/api/dashboards/", endpoint="dashboard"
+)
api.add_org_resource(
PublicDashboardResource,
"/api/dashboards/public/",
@@ -144,25 +141,39 @@ def json_representation(data, code, headers=None):
endpoint="dashboard_share",
)
-api.add_org_resource(DataSourceTypeListResource, "/api/data_sources/types", endpoint="data_source_types")
-api.add_org_resource(DataSourceListResource, "/api/data_sources", endpoint="data_sources")
-api.add_org_resource(DataSourceSchemaResource, "/api/data_sources//schema")
-api.add_org_resource(DatabricksDatabaseListResource, "/api/databricks/databases/")
+api.add_org_resource(
+ DataSourceTypeListResource, "/api/data_sources/types", endpoint="data_source_types"
+)
+api.add_org_resource(
+ DataSourceListResource, "/api/data_sources", endpoint="data_sources"
+)
+api.add_org_resource(
+ DataSourceSchemaResource, "/api/data_sources//schema"
+)
+api.add_org_resource(
+ DatabricksDatabaseListResource, "/api/databricks/databases/"
+)
api.add_org_resource(
DatabricksSchemaResource,
"/api/databricks/databases///tables",
)
api.add_org_resource(
- DatabricksTableColumnListResource,
- "/api/databricks/databases///columns/",
+ DataSourcePauseResource, "/api/data_sources//pause"
)
-api.add_org_resource(DataSourcePauseResource, "/api/data_sources//pause")
api.add_org_resource(DataSourceTestResource, "/api/data_sources//test")
-api.add_org_resource(DataSourceResource, "/api/data_sources/", endpoint="data_source")
+api.add_org_resource(
+ DataSourceResource, "/api/data_sources/", endpoint="data_source"
+)
+api.add_resource(
+ DataSourceToggleStringResource, "/api/data_sources//toggle_string"
+)
+
api.add_org_resource(GroupListResource, "/api/groups", endpoint="groups")
api.add_org_resource(GroupResource, "/api/groups/", endpoint="group")
-api.add_org_resource(GroupMemberListResource, "/api/groups//members", endpoint="group_members")
+api.add_org_resource(
+ GroupMemberListResource, "/api/groups//members", endpoint="group_members"
+)
api.add_org_resource(
GroupMemberResource,
"/api/groups//members/",
@@ -181,8 +192,12 @@ def json_representation(data, code, headers=None):
api.add_org_resource(EventsResource, "/api/events", endpoint="events")
-api.add_org_resource(QueryFavoriteListResource, "/api/queries/favorites", endpoint="query_favorites")
-api.add_org_resource(QueryFavoriteResource, "/api/queries//favorite", endpoint="query_favorite")
+api.add_org_resource(
+ QueryFavoriteListResource, "/api/queries/favorites", endpoint="query_favorites"
+)
+api.add_org_resource(
+ QueryFavoriteResource, "/api/queries//favorite", endpoint="query_favorite"
+)
api.add_org_resource(
DashboardFavoriteListResource,
"/api/dashboards/favorites",
@@ -193,21 +208,30 @@ def json_representation(data, code, headers=None):
"/api/dashboards//favorite",
endpoint="dashboard_favorite",
)
-api.add_org_resource(DashboardForkResource, "/api/dashboards//fork", endpoint="dashboard_fork")
-
-api.add_org_resource(MyDashboardsResource, "/api/dashboards/my", endpoint="my_dashboards")
api.add_org_resource(QueryTagsResource, "/api/queries/tags", endpoint="query_tags")
-api.add_org_resource(DashboardTagsResource, "/api/dashboards/tags", endpoint="dashboard_tags")
+api.add_org_resource(
+ DashboardTagsResource, "/api/dashboards/tags", endpoint="dashboard_tags"
+)
-api.add_org_resource(QuerySearchResource, "/api/queries/search", endpoint="queries_search")
-api.add_org_resource(QueryRecentResource, "/api/queries/recent", endpoint="recent_queries")
-api.add_org_resource(QueryArchiveResource, "/api/queries/archive", endpoint="queries_archive")
+api.add_org_resource(
+ QuerySearchResource, "/api/queries/search", endpoint="queries_search"
+)
+api.add_org_resource(
+ QueryRecentResource, "/api/queries/recent", endpoint="recent_queries"
+)
+api.add_org_resource(
+ QueryArchiveResource, "/api/queries/archive", endpoint="queries_archive"
+)
api.add_org_resource(QueryListResource, "/api/queries", endpoint="queries")
api.add_org_resource(MyQueriesResource, "/api/queries/my", endpoint="my_queries")
-api.add_org_resource(QueryRefreshResource, "/api/queries//refresh", endpoint="query_refresh")
+api.add_org_resource(
+ QueryRefreshResource, "/api/queries//refresh", endpoint="query_refresh"
+)
api.add_org_resource(QueryResource, "/api/queries/", endpoint="query")
-api.add_org_resource(QueryForkResource, "/api/queries//fork", endpoint="query_fork")
+api.add_org_resource(
+ QueryForkResource, "/api/queries//fork", endpoint="query_fork"
+)
api.add_org_resource(
QueryRegenerateApiKeyResource,
"/api/queries//regenerate_api_key",
@@ -225,7 +249,9 @@ def json_representation(data, code, headers=None):
endpoint="check_permissions",
)
-api.add_org_resource(QueryResultListResource, "/api/query_results", endpoint="query_results")
+api.add_org_resource(
+ QueryResultListResource, "/api/query_results", endpoint="query_results"
+)
api.add_org_resource(
QueryResultDropdownResource,
"/api/queries//dropdown",
@@ -254,7 +280,9 @@ def json_representation(data, code, headers=None):
api.add_org_resource(UserListResource, "/api/users", endpoint="users")
api.add_org_resource(UserResource, "/api/users/", endpoint="user")
-api.add_org_resource(UserInviteResource, "/api/users//invite", endpoint="user_invite")
+api.add_org_resource(
+ UserInviteResource, "/api/users//invite", endpoint="user_invite"
+)
api.add_org_resource(
UserResetPasswordResource,
"/api/users//reset_password",
@@ -265,9 +293,13 @@ def json_representation(data, code, headers=None):
"/api/users//regenerate_api_key",
endpoint="user_regenerate_api_key",
)
-api.add_org_resource(UserDisableResource, "/api/users//disable", endpoint="user_disable")
+api.add_org_resource(
+ UserDisableResource, "/api/users//disable", endpoint="user_disable"
+)
-api.add_org_resource(VisualizationListResource, "/api/visualizations", endpoint="visualizations")
+api.add_org_resource(
+ VisualizationListResource, "/api/visualizations", endpoint="visualizations"
+)
api.add_org_resource(
VisualizationResource,
"/api/visualizations/",
@@ -277,11 +309,23 @@ def json_representation(data, code, headers=None):
api.add_org_resource(WidgetListResource, "/api/widgets", endpoint="widgets")
api.add_org_resource(WidgetResource, "/api/widgets/", endpoint="widget")
-api.add_org_resource(DestinationTypeListResource, "/api/destinations/types", endpoint="destination_types")
-api.add_org_resource(DestinationResource, "/api/destinations/", endpoint="destination")
-api.add_org_resource(DestinationListResource, "/api/destinations", endpoint="destinations")
+api.add_org_resource(
+ DestinationTypeListResource, "/api/destinations/types", endpoint="destination_types"
+)
+api.add_org_resource(
+ DestinationResource, "/api/destinations/", endpoint="destination"
+)
+api.add_org_resource(
+ DestinationListResource, "/api/destinations", endpoint="destinations"
+)
-api.add_org_resource(QuerySnippetResource, "/api/query_snippets/", endpoint="query_snippet")
-api.add_org_resource(QuerySnippetListResource, "/api/query_snippets", endpoint="query_snippets")
+api.add_org_resource(
+ QuerySnippetResource, "/api/query_snippets/", endpoint="query_snippet"
+)
+api.add_org_resource(
+ QuerySnippetListResource, "/api/query_snippets", endpoint="query_snippets"
+)
-api.add_org_resource(OrganizationSettings, "/api/settings/organization", endpoint="organization_settings")
+api.add_org_resource(
+ OrganizationSettings, "/api/settings/organization", endpoint="organization_settings"
+)
diff --git a/redash/handlers/authentication.py b/redash/handlers/authentication.py
index 62b20531c3..a4f1d2f87d 100644
--- a/redash/handlers/authentication.py
+++ b/redash/handlers/authentication.py
@@ -1,13 +1,13 @@
import logging
from flask import abort, flash, redirect, render_template, request, url_for
-from flask_login import current_user, login_required, login_user, logout_user
-from itsdangerous import BadSignature, SignatureExpired
-from sqlalchemy.orm.exc import NoResultFound
+from flask_login import current_user, login_required, login_user, logout_user
from redash import __version__, limiter, models, settings
from redash.authentication import current_org, get_login_url, get_next_path
from redash.authentication.account import (
+ BadSignature,
+ SignatureExpired,
send_password_reset_email,
send_user_disabled_email,
send_verify_email,
@@ -16,44 +16,46 @@
from redash.handlers import routes
from redash.handlers.base import json_response, org_scoped_rule
from redash.version_check import get_latest_version
+from sqlalchemy.orm.exc import NoResultFound
logger = logging.getLogger(__name__)
def get_google_auth_url(next_path):
if settings.MULTI_ORG:
- google_auth_url = url_for("google_oauth.authorize_org", next=next_path, org_slug=current_org.slug)
+ google_auth_url = url_for(
+ "google_oauth.authorize_org", next=next_path, org_slug=current_org.slug
+ )
else:
google_auth_url = url_for("google_oauth.authorize", next=next_path)
return google_auth_url
def render_token_login_page(template, org_slug, token, invite):
- error_message = None
try:
user_id = validate_token(token)
org = current_org._get_current_object()
user = models.User.get_by_id_and_org(user_id, org)
except NoResultFound:
logger.exception(
- "Bad user id in token. Token=%s , User id= %s, Org=%s",
- token,
+ "Bad user id in token. Token= , User id= %s, Org=%s",
user_id,
+ token,
org_slug,
)
- error_message = "Your invite link is invalid. Bad user id in token. Please ask for a new one."
- except SignatureExpired:
- logger.exception("Token signature has expired. Token: %s, org=%s", token, org_slug)
- error_message = "Your invite link has expired. Please ask for a new one."
- except BadSignature:
- logger.exception("Bad signature for the token: %s, org=%s", token, org_slug)
- error_message = "Your invite link is invalid. Bad signature. Please double-check the token."
-
- if error_message:
return (
render_template(
"error.html",
- error_message=error_message,
+ error_message="Invalid invite link. Please ask for a new one.",
+ ),
+ 400,
+ )
+ except (SignatureExpired, BadSignature):
+ logger.exception("Failed to verify invite token: %s, org=%s", token, org_slug)
+ return (
+ render_template(
+ "error.html",
+ error_message="Your invite link has expired. Please ask for a new one.",
),
400,
)
@@ -63,7 +65,8 @@ def render_token_login_page(template, org_slug, token, invite):
render_template(
"error.html",
error_message=(
- "This invitation has already been accepted. Please try resetting your password instead."
+ "This invitation has already been accepted. "
+ "Please try resetting your password instead."
),
),
400,
@@ -123,7 +126,9 @@ def verify(token, org_slug=None):
org = current_org._get_current_object()
user = models.User.get_by_id_and_org(user_id, org)
except (BadSignature, NoResultFound):
- logger.exception("Failed to verify email verification token: %s, org=%s", token, org_slug)
+ logger.exception(
+ "Failed to verify email verification token: %s, org=%s", token, org_slug
+ )
return (
render_template(
"error.html",
@@ -143,7 +148,6 @@ def verify(token, org_slug=None):
@routes.route(org_scoped_rule("/forgot"), methods=["GET", "POST"])
-@limiter.limit(settings.THROTTLE_PASS_RESET_PATTERN)
def forgot_password(org_slug=None):
if not current_org.get_setting("auth_password_login_enabled"):
abort(404)
@@ -170,7 +174,11 @@ def verification_email(org_slug=None):
if not current_user.is_email_verified:
send_verify_email(current_user, current_org)
- return json_response({"message": "Please check your email inbox in order to verify your email address."})
+ return json_response(
+ {
+ "message": "Please check your email inbox in order to verify your email address."
+ }
+ )
@routes.route(org_scoped_rule("/login"), methods=["GET", "POST"])
@@ -178,9 +186,9 @@ def verification_email(org_slug=None):
def login(org_slug=None):
# We intentionally use == as otherwise it won't actually use the proxy. So weird :O
# noinspection PyComparisonWithNone
- if current_org == None and not settings.MULTI_ORG: # noqa: E711
+ if current_org == None and not settings.MULTI_ORG:
return redirect("/setup")
- elif current_org == None: # noqa: E711
+ elif current_org == None:
return redirect("/")
index_url = url_for("redash.index", org_slug=org_slug)
@@ -189,11 +197,15 @@ def login(org_slug=None):
if current_user.is_authenticated:
return redirect(next_path)
- if request.method == "POST" and current_org.get_setting("auth_password_login_enabled"):
+ if request.method == "POST":
try:
org = current_org._get_current_object()
user = models.User.get_by_email_and_org(request.form["email"], org)
- if user and not user.is_disabled and user.verify_password(request.form["password"]):
+ if (
+ user
+ and not user.is_disabled
+ and user.verify_password(request.form["password"])
+ ):
remember = "remember" in request.form
login_user(user, remember=remember)
return redirect(next_path)
@@ -201,8 +213,6 @@ def login(org_slug=None):
flash("Wrong email or password.")
except NoResultFound:
flash("Wrong email or password.")
- elif request.method == "POST" and not current_org.get_setting("auth_password_login_enabled"):
- flash("Password login is not enabled for your organization.")
google_auth_url = get_google_auth_url(next_path)
@@ -264,14 +274,20 @@ def client_config():
else:
client_config = {}
- if current_user.has_permission("admin") and current_org.get_setting("beacon_consent") is None:
+ if (
+ current_user.has_permission("admin")
+ and current_org.get_setting("beacon_consent") is None
+ ):
client_config["showBeaconConsentMessage"] = True
defaults = {
"allowScriptsInUserInput": settings.ALLOW_SCRIPTS_IN_USER_INPUT,
- "showPermissionsControl": current_org.get_setting("feature_show_permissions_control"),
- "hidePlotlyModeBar": current_org.get_setting("hide_plotly_mode_bar"),
- "disablePublicUrls": current_org.get_setting("disable_public_urls"),
+ "showPermissionsControl": current_org.get_setting(
+ "feature_show_permissions_control"
+ ),
+ "hidePlotlyModeBar": current_org.get_setting(
+ "hide_plotly_mode_bar"
+ ),
"allowCustomJSVisualizations": settings.FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS,
"autoPublishNamedQueries": settings.FEATURE_AUTO_PUBLISH_NAMED_QUERIES,
"extendedAlertOptions": settings.FEATURE_EXTENDED_ALERT_OPTIONS,
@@ -307,7 +323,9 @@ def messages():
@routes.route("/api/config", methods=["GET"])
def config(org_slug=None):
- return json_response({"org_slug": current_org.slug, "client_config": client_config()})
+ return json_response(
+ {"org_slug": current_org.slug, "client_config": client_config()}
+ )
@routes.route(org_scoped_rule("/api/session"), methods=["GET"])
diff --git a/redash/handlers/base.py b/redash/handlers/base.py
index 2a817c8b92..26db713003 100644
--- a/redash/handlers/base.py
+++ b/redash/handlers/base.py
@@ -1,21 +1,23 @@
import time
-from inspect import isclass
+from inspect import isclass
from flask import Blueprint, current_app, request
+
from flask_login import current_user, login_required
from flask_restful import Resource, abort
-from sqlalchemy import cast
-from sqlalchemy.dialects.postgresql import ARRAY
-from sqlalchemy.orm.exc import NoResultFound
-
from redash import settings
from redash.authentication import current_org
from redash.models import db
from redash.tasks import record_event as record_event_task
from redash.utils import json_dumps
-from redash.utils.query_order import sort_query
+from sqlalchemy.orm.exc import NoResultFound
+from sqlalchemy import cast
+from sqlalchemy.dialects import postgresql
+from sqlalchemy_utils import sort_query
-routes = Blueprint("redash", __name__, template_folder=settings.fix_assets_path("templates"))
+routes = Blueprint(
+ "redash", __name__, template_folder=settings.fix_assets_path("templates")
+)
class BaseResource(Resource):
@@ -114,7 +116,9 @@ def json_response(response):
def filter_by_tags(result_set, column):
if request.args.getlist("tags"):
tags = request.args.getlist("tags")
- result_set = result_set.filter(cast(column, ARRAY(db.Text)).contains(tags))
+ result_set = result_set.filter(
+ cast(column, postgresql.ARRAY(db.Text)).contains(tags)
+ )
return result_set
diff --git a/redash/handlers/dashboards.py b/redash/handlers/dashboards.py
index 2eee9b8bc5..8cc9f4a0ab 100644
--- a/redash/handlers/dashboards.py
+++ b/redash/handlers/dashboards.py
@@ -1,16 +1,15 @@
from flask import request, url_for
-from flask_restful import abort
-from funcy import partial, project
-from sqlalchemy.orm.exc import StaleDataError
+from funcy import project, partial
+from flask_restful import abort
from redash import models
from redash.handlers.base import (
BaseResource,
- filter_by_tags,
get_object_or_404,
paginate,
+ filter_by_tags,
+ order_results as _order_results,
)
-from redash.handlers.base import order_results as _order_results
from redash.permissions import (
can_modify,
require_admin_or_owner,
@@ -18,7 +17,13 @@
require_permission,
)
from redash.security import csp_allows_embeding
-from redash.serializers import DashboardSerializer, public_dashboard
+from redash.serializers import (
+ DashboardSerializer,
+ public_dashboard,
+)
+from sqlalchemy.exc import IntegrityError
+from sqlalchemy.orm.exc import StaleDataError
+
# Ordering map for relationships
order_map = {
@@ -28,7 +33,9 @@
"-created_at": "-created_at",
}
-order_results = partial(_order_results, default_order="-created_at", allowed_orders=order_map)
+order_results = partial(
+ _order_results, default_order="-created_at", allowed_orders=order_map
+)
class DashboardListResource(BaseResource):
@@ -55,7 +62,9 @@ def get(self):
search_term,
)
else:
- results = models.Dashboard.all(self.current_org, self.current_user.group_ids, self.current_user.id)
+ results = models.Dashboard.all(
+ self.current_org, self.current_user.group_ids, self.current_user.id
+ )
results = filter_by_tags(results, models.Dashboard.tags)
@@ -75,7 +84,9 @@ def get(self):
)
if search_term:
- self.record_event({"action": "search", "object_type": "dashboard", "term": search_term})
+ self.record_event(
+ {"action": "search", "object_type": "dashboard", "term": search_term}
+ )
else:
self.record_event({"action": "list", "object_type": "dashboard"})
@@ -96,52 +107,20 @@ def post(self):
org=self.current_org,
user=self.current_user,
is_draft=True,
- layout=[],
+ layout="[]",
)
models.db.session.add(dashboard)
models.db.session.commit()
return DashboardSerializer(dashboard).serialize()
-class MyDashboardsResource(BaseResource):
- @require_permission("list_dashboards")
- def get(self):
- """
- Retrieve a list of dashboards created by the current user.
-
- :qparam number page_size: Number of dashboards to return per page
- :qparam number page: Page number to retrieve
- :qparam number order: Name of column to order by
- :qparam number search: Full text search term
-
- Responds with an array of :ref:`dashboard `
- objects.
- """
- search_term = request.args.get("q", "")
- if search_term:
- results = models.Dashboard.search_by_user(search_term, self.current_user)
- else:
- results = models.Dashboard.by_user(self.current_user)
-
- results = filter_by_tags(results, models.Dashboard.tags)
-
- # order results according to passed order parameter,
- # special-casing search queries where the database
- # provides an order by search rank
- ordered_results = order_results(results, fallback=not bool(search_term))
-
- page = request.args.get("page", 1, type=int)
- page_size = request.args.get("page_size", 25, type=int)
- return paginate(ordered_results, page, page_size, DashboardSerializer)
-
-
class DashboardResource(BaseResource):
@require_permission("list_dashboards")
- def get(self, dashboard_id=None):
+ def get(self, dashboard_slug=None):
"""
Retrieves a dashboard.
- :qparam number id: Id of dashboard to retrieve.
+ :qparam string slug: Slug of dashboard to retrieve.
.. _dashboard-response-label:
@@ -157,7 +136,6 @@ def get(self, dashboard_id=None):
:>json boolean is_draft: Whether this dashboard is a draft or not.
:>json array layout: Array of arrays containing widget IDs, corresponding to the rows and columns the widgets are displayed in
:>json array widgets: Array of arrays containing :ref:`widget ` data
- :>json object options: Dashboard options
.. _widget-response-label:
@@ -172,13 +150,12 @@ def get(self, dashboard_id=None):
:>json string widget.created_at: ISO format timestamp for widget creation
:>json string widget.updated_at: ISO format timestamp for last widget modification
"""
- if request.args.get("legacy") is not None:
- fn = models.Dashboard.get_by_slug_and_org
- else:
- fn = models.Dashboard.get_by_id_and_org
-
- dashboard = get_object_or_404(fn, dashboard_id, self.current_org)
- response = DashboardSerializer(dashboard, with_widgets=True, user=self.current_user).serialize()
+ dashboard = get_object_or_404(
+ models.Dashboard.get_by_slug_and_org, dashboard_slug, self.current_org
+ )
+ response = DashboardSerializer(
+ dashboard, with_widgets=True, user=self.current_user
+ ).serialize()
api_key = models.ApiKey.get_by_object(dashboard)
if api_key:
@@ -192,16 +169,18 @@ def get(self, dashboard_id=None):
response["can_edit"] = can_modify(dashboard, self.current_user)
- self.record_event({"action": "view", "object_id": dashboard.id, "object_type": "dashboard"})
+ self.record_event(
+ {"action": "view", "object_id": dashboard.id, "object_type": "dashboard"}
+ )
return response
@require_permission("edit_dashboard")
- def post(self, dashboard_id):
+ def post(self, dashboard_slug):
"""
Modifies a dashboard.
- :qparam number id: Id of dashboard to retrieve.
+ :qparam string slug: Slug of dashboard to retrieve.
Responds with the updated :ref:`dashboard `.
@@ -210,7 +189,7 @@ def post(self, dashboard_id):
"""
dashboard_properties = request.get_json(force=True)
# TODO: either convert all requests to use slugs or ids
- dashboard = models.Dashboard.get_by_id_and_org(dashboard_id, self.current_org)
+ dashboard = models.Dashboard.get_by_id_and_org(dashboard_slug, self.current_org)
require_object_modify_permission(dashboard, self.current_user)
@@ -224,7 +203,6 @@ def post(self, dashboard_id):
"is_draft",
"is_archived",
"dashboard_filters_enabled",
- "options",
),
)
@@ -241,31 +219,45 @@ def post(self, dashboard_id):
try:
models.db.session.commit()
except StaleDataError:
+ models.db.session.rollback()
abort(409)
+ except IntegrityError:
+ models.db.session.rollback()
+ abort(400)
- result = DashboardSerializer(dashboard, with_widgets=True, user=self.current_user).serialize()
+ result = DashboardSerializer(
+ dashboard, with_widgets=True, user=self.current_user
+ ).serialize()
- self.record_event({"action": "edit", "object_id": dashboard.id, "object_type": "dashboard"})
+ self.record_event(
+ {"action": "edit", "object_id": dashboard.id, "object_type": "dashboard"}
+ )
return result
@require_permission("edit_dashboard")
- def delete(self, dashboard_id):
+ def delete(self, dashboard_slug):
"""
Archives a dashboard.
- :qparam number id: Id of dashboard to retrieve.
+ :qparam string slug: Slug of dashboard to retrieve.
Responds with the archived :ref:`dashboard `.
"""
- dashboard = models.Dashboard.get_by_id_and_org(dashboard_id, self.current_org)
+ dashboard = models.Dashboard.get_by_slug_and_org(
+ dashboard_slug, self.current_org
+ )
dashboard.is_archived = True
dashboard.record_changes(changed_by=self.current_user)
models.db.session.add(dashboard)
- d = DashboardSerializer(dashboard, with_widgets=True, user=self.current_user).serialize()
+ d = DashboardSerializer(
+ dashboard, with_widgets=True, user=self.current_user
+ ).serialize()
models.db.session.commit()
- self.record_event({"action": "archive", "object_id": dashboard.id, "object_type": "dashboard"})
+ self.record_event(
+ {"action": "archive", "object_id": dashboard.id, "object_type": "dashboard"}
+ )
return d
@@ -280,9 +272,6 @@ def get(self, token):
:param token: An API key for a public dashboard.
:>json array widgets: An array of arrays of :ref:`public widgets `, corresponding to the rows and columns the widgets are displayed in
"""
- if self.current_org.get_setting("disable_public_urls"):
- abort(400, message="Public URLs are disabled.")
-
if not isinstance(self.current_user, models.ApiUser):
api_key = get_object_or_404(models.ApiKey.get_by_api_key, token)
dashboard = api_key.object
@@ -369,7 +358,9 @@ def get(self):
self.current_user.id,
search_term,
)
- favorites = models.Dashboard.favorites(self.current_user, base_query=base_query)
+ favorites = models.Dashboard.favorites(
+ self.current_user, base_query=base_query
+ )
else:
favorites = models.Dashboard.favorites(self.current_user)
@@ -398,16 +389,3 @@ def get(self):
)
return response
-
-
-class DashboardForkResource(BaseResource):
- @require_permission("edit_dashboard")
- def post(self, dashboard_id):
- dashboard = models.Dashboard.get_by_id_and_org(dashboard_id, self.current_org)
-
- fork_dashboard = dashboard.fork(self.current_user)
- models.db.session.commit()
-
- self.record_event({"action": "fork", "object_id": dashboard_id, "object_type": "dashboard"})
-
- return DashboardSerializer(fork_dashboard, with_widgets=True).serialize()
diff --git a/redash/handlers/data_sources.py b/redash/handlers/data_sources.py
index 8ee728bade..a18b1b2244 100644
--- a/redash/handlers/data_sources.py
+++ b/redash/handlers/data_sources.py
@@ -7,11 +7,7 @@
from sqlalchemy.exc import IntegrityError
from redash import models
-from redash.handlers.base import (
- BaseResource,
- get_object_or_404,
- require_fields,
-)
+from redash.handlers.base import BaseResource, get_object_or_404, require_fields
from redash.permissions import (
require_access,
require_admin,
@@ -21,22 +17,27 @@
from redash.query_runner import (
get_configuration_schema_for_query_runner_type,
query_runners,
+ NotSupported,
)
-from redash.serializers import serialize_job
-from redash.tasks.general import get_schema, test_connection
+from redash.tasks.queries import refresh_schema
from redash.utils import filter_none
from redash.utils.configuration import ConfigurationContainer, ValidationError
+from redash.tasks.general import test_connection
class DataSourceTypeListResource(BaseResource):
@require_admin
def get(self):
- return [q.to_dict() for q in sorted(query_runners.values(), key=lambda q: q.name().lower())]
+ return [
+ q.to_dict() for q in sorted(query_runners.values(), key=lambda q: q.name())
+ ]
class DataSourceResource(BaseResource):
def get(self, data_source_id):
- data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, self.current_org)
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
require_access(data_source, self.current_user, view_only)
ds = {}
@@ -45,13 +46,19 @@ def get(self, data_source_id):
ds = data_source.to_dict(all=self.current_user.has_permission("admin"))
# add view_only info, required for frontend permissions
- ds["view_only"] = all(project(data_source.groups, self.current_user.group_ids).values())
- self.record_event({"action": "view", "object_id": data_source_id, "object_type": "datasource"})
+ ds["view_only"] = all(
+ project(data_source.groups, self.current_user.group_ids).values()
+ )
+ self.record_event(
+ {"action": "view", "object_id": data_source_id, "object_type": "datasource"}
+ )
return ds
@require_admin
def post(self, data_source_id):
- data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org)
+ data_source = models.DataSource.get_by_id_and_org(
+ data_source_id, self.current_org
+ )
req = request.get_json(True)
schema = get_configuration_schema_for_query_runner_type(req["type"])
@@ -65,26 +72,37 @@ def post(self, data_source_id):
data_source.type = req["type"]
data_source.name = req["name"]
+ data_source.description = req["description"] if "description" in req else ""
models.db.session.add(data_source)
+ # Refresh the stored schemas when a data source is updated
+ refresh_schema.delay(data_source.id)
+
try:
models.db.session.commit()
except IntegrityError as e:
+ models.db.session.rollback()
if req["name"] in str(e):
abort(
400,
- message="Data source with the name {} already exists.".format(req["name"]),
+ message="Data source with the name {} already exists.".format(
+ req["name"]
+ ),
)
abort(400)
- self.record_event({"action": "edit", "object_id": data_source.id, "object_type": "datasource"})
+ self.record_event(
+ {"action": "edit", "object_id": data_source.id, "object_type": "datasource"}
+ )
return data_source.to_dict(all=True)
@require_admin
def delete(self, data_source_id):
- data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org)
+ data_source = models.DataSource.get_by_id_and_org(
+ data_source_id, self.current_org
+ )
data_source.delete()
self.record_event(
@@ -104,7 +122,9 @@ def get(self):
if self.current_user.has_permission("admin"):
data_sources = models.DataSource.all(self.current_org)
else:
- data_sources = models.DataSource.all(self.current_org, group_ids=self.current_user.group_ids)
+ data_sources = models.DataSource.all(
+ self.current_org, group_ids=self.current_user.group_ids
+ )
response = {}
for ds in data_sources:
@@ -112,11 +132,15 @@ def get(self):
continue
try:
- d = ds.to_dict()
- d["view_only"] = all(project(ds.groups, self.current_user.group_ids).values())
+ d = ds.to_dict(all=True)
+ d["view_only"] = all(
+ project(ds.groups, self.current_user.group_ids).values()
+ )
response[ds.id] = d
except AttributeError:
- logging.exception("Error with DataSource#to_dict (data source id: %d)", ds.id)
+ logging.exception(
+ "Error with DataSource#to_dict (data source id: %d)", ds.id
+ )
self.record_event(
{
@@ -143,15 +167,26 @@ def post(self):
try:
datasource = models.DataSource.create_with_group(
- org=self.current_org, name=req["name"], type=req["type"], options=config
+ org=self.current_org,
+ name=req["name"],
+ type=req["type"],
+ description=req["description"] if "description" in req else "",
+ options=config,
)
models.db.session.commit()
+
+ # Refresh the stored schemas when a new data source is added to the list
+ refresh_schema.delay(datasource.id)
+
except IntegrityError as e:
+ models.db.session.rollback()
if req["name"] in str(e):
abort(
400,
- message="Data source with the name {} already exists.".format(req["name"]),
+ message="Data source with the name {} already exists.".format(
+ req["name"]
+ ),
)
abort(400)
@@ -168,26 +203,44 @@ def post(self):
class DataSourceSchemaResource(BaseResource):
+ @require_admin
+ def post(self, data_source_id):
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
+ new_schema_data = request.get_json(force=True)
+ models.DataSource.save_schema(new_schema_data)
+ # Force update the schema cache to have all changes available right away
+ data_source.schema_cache.populate(forced=True)
+
def get(self, data_source_id):
- data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, self.current_org)
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
require_access(data_source, self.current_user, view_only)
refresh = request.args.get("refresh") is not None
- if not refresh:
- cached_schema = data_source.get_cached_schema()
-
- if cached_schema is not None:
- return {"schema": cached_schema}
+ response = {}
- job = get_schema.delay(data_source.id, refresh)
+ try:
+ response["schema"] = data_source.get_schema(refresh)
+ except NotSupported:
+ response["error"] = {
+ "code": 1,
+ "message": "Data source type does not support retrieving schema",
+ }
+ except Exception:
+ response["error"] = {"code": 2, "message": "Error retrieving schema."}
- return serialize_job(job)
+ return response
class DataSourcePauseResource(BaseResource):
@require_admin
def post(self, data_source_id):
- data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, self.current_org)
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
data = request.get_json(force=True, silent=True)
if data:
reason = data.get("reason")
@@ -207,7 +260,9 @@ def post(self, data_source_id):
@require_admin
def delete(self, data_source_id):
- data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, self.current_org)
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
data_source.resume()
self.record_event(
@@ -223,7 +278,9 @@ def delete(self, data_source_id):
class DataSourceTestResource(BaseResource):
@require_admin
def post(self, data_source_id):
- data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, self.current_org)
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
response = {}
@@ -246,3 +303,15 @@ def post(self, data_source_id):
}
)
return response
+
+
+class DataSourceToggleStringResource(BaseResource):
+ def get(self, data_source_id):
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
+ require_access(data_source.groups, self.current_user, view_only)
+ try:
+ return {"toggle_string": data_source.options.get("toggle_table_string", "")}
+ except Exception:
+ abort(400)
diff --git a/redash/handlers/databricks.py b/redash/handlers/databricks.py
index 3b8b43305f..e5743115c2 100644
--- a/redash/handlers/databricks.py
+++ b/redash/handlers/databricks.py
@@ -1,85 +1,41 @@
-from flask import request
from flask_restful import abort
-
-from redash import models, redis_connection
+from redash import models
from redash.handlers.base import BaseResource, get_object_or_404
-from redash.permissions import require_access, view_only
-from redash.serializers import serialize_job
-from redash.tasks.databricks import (
- get_database_tables_with_columns,
- get_databricks_databases,
- get_databricks_table_columns,
- get_databricks_tables,
+from redash.permissions import (
+ require_access,
+ view_only,
)
-from redash.utils import json_loads
-
-
-def _get_databricks_data_source(data_source_id, user, org):
- data_source = get_object_or_404(models.DataSource.get_by_id_and_org, data_source_id, org)
- require_access(data_source, user, view_only)
-
- if not data_source.type == "databricks":
- abort(400, message="Resource only available for the Databricks query runner.")
-
- return data_source
-
-
-def _databases_key(data_source_id):
- return "databricks:databases:{}".format(data_source_id)
-
-
-def _tables_key(data_source_id, database_name):
- return "databricks:database_tables:{}:{}".format(data_source_id, database_name)
-
-
-def _get_databases_from_cache(data_source_id):
- cache = redis_connection.get(_databases_key(data_source_id))
- return json_loads(cache) if cache else None
-
-
-def _get_tables_from_cache(data_source_id, database_name):
- cache = redis_connection.get(_tables_key(data_source_id, database_name))
- return json_loads(cache) if cache else None
+from redash.tasks.databricks import get_databricks_databases, get_databricks_schema
+from redash.serializers import serialize_job
class DatabricksDatabaseListResource(BaseResource):
def get(self, data_source_id):
- data_source = _get_databricks_data_source(data_source_id, user=self.current_user, org=self.current_org)
-
- refresh = request.args.get("refresh") is not None
- if not refresh:
- cached_databases = _get_databases_from_cache(data_source_id)
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
+ )
+ require_access(data_source, self.current_user, view_only)
- if cached_databases is not None:
- return cached_databases
+ if not data_source.type == "databricks":
+ abort(
+ 400, message="Resource only available for the Databricks query runner."
+ )
- job = get_databricks_databases.delay(data_source.id, redis_key=_databases_key(data_source_id))
+ job = get_databricks_databases.delay(data_source.id)
return serialize_job(job)
class DatabricksSchemaResource(BaseResource):
def get(self, data_source_id, database_name):
- data_source = _get_databricks_data_source(data_source_id, user=self.current_user, org=self.current_org)
-
- refresh = request.args.get("refresh") is not None
- if not refresh:
- cached_tables = _get_tables_from_cache(data_source_id, database_name)
-
- if cached_tables is not None:
- return {"schema": cached_tables, "has_columns": True}
-
- job = get_databricks_tables.delay(data_source.id, database_name)
- return serialize_job(job)
-
- job = get_database_tables_with_columns.delay(
- data_source.id, database_name, redis_key=_tables_key(data_source_id, database_name)
+ data_source = get_object_or_404(
+ models.DataSource.get_by_id_and_org, data_source_id, self.current_org
)
- return serialize_job(job)
-
+ require_access(data_source, self.current_user, view_only)
-class DatabricksTableColumnListResource(BaseResource):
- def get(self, data_source_id, database_name, table_name):
- data_source = _get_databricks_data_source(data_source_id, user=self.current_user, org=self.current_org)
+ if not data_source.type == "databricks":
+ abort(
+ 400, message="Resource only available for the Databricks query runner."
+ )
- job = get_databricks_table_columns.delay(data_source.id, database_name, table_name)
+ job = get_databricks_schema.delay(data_source.id, database_name)
return serialize_job(job)
diff --git a/redash/handlers/destinations.py b/redash/handlers/destinations.py
index 15a80865d1..e935ed84bb 100644
--- a/redash/handlers/destinations.py
+++ b/redash/handlers/destinations.py
@@ -21,7 +21,9 @@ def get(self):
class DestinationResource(BaseResource):
@require_admin
def get(self, destination_id):
- destination = models.NotificationDestination.get_by_id_and_org(destination_id, self.current_org)
+ destination = models.NotificationDestination.get_by_id_and_org(
+ destination_id, self.current_org
+ )
d = destination.to_dict(all=True)
self.record_event(
{
@@ -34,7 +36,9 @@ def get(self, destination_id):
@require_admin
def post(self, destination_id):
- destination = models.NotificationDestination.get_by_id_and_org(destination_id, self.current_org)
+ destination = models.NotificationDestination.get_by_id_and_org(
+ destination_id, self.current_org
+ )
req = request.get_json(True)
schema = get_configuration_schema_for_destination_type(req["type"])
@@ -54,7 +58,9 @@ def post(self, destination_id):
if "name" in str(e):
abort(
400,
- message="Alert Destination with the name {} already exists.".format(req["name"]),
+ message="Alert Destination with the name {} already exists.".format(
+ req["name"]
+ ),
)
abort(500)
@@ -62,7 +68,9 @@ def post(self, destination_id):
@require_admin
def delete(self, destination_id):
- destination = models.NotificationDestination.get_by_id_and_org(destination_id, self.current_org)
+ destination = models.NotificationDestination.get_by_id_and_org(
+ destination_id, self.current_org
+ )
models.db.session.delete(destination)
models.db.session.commit()
@@ -127,7 +135,9 @@ def post(self):
if "name" in str(e):
abort(
400,
- message="Alert Destination with the name {} already exists.".format(req["name"]),
+ message="Alert Destination with the name {} already exists.".format(
+ req["name"]
+ ),
)
abort(500)
diff --git a/redash/handlers/embed.py b/redash/handlers/embed.py
index 9ba3570823..e7a5cfb576 100644
--- a/redash/handlers/embed.py
+++ b/redash/handlers/embed.py
@@ -1,18 +1,13 @@
from flask import request
-from flask_login import current_user, login_required
+from .authentication import current_org
+from flask_login import current_user, login_required
from redash import models
from redash.handlers import routes
-from redash.handlers.base import (
- get_object_or_404,
- org_scoped_rule,
- record_event,
-)
+from redash.handlers.base import get_object_or_404, org_scoped_rule, record_event
from redash.handlers.static import render_index
from redash.security import csp_allows_embeding
-from .authentication import current_org
-
@routes.route(
org_scoped_rule("/embed/query//visualization/"),
diff --git a/redash/handlers/events.py b/redash/handlers/events.py
index f77a72e830..6ecbe84758 100644
--- a/redash/handlers/events.py
+++ b/redash/handlers/events.py
@@ -1,6 +1,6 @@
+from flask import request
import geolite2
import maxminddb
-from flask import request
from user_agents import parse as parse_ua
from redash.handlers.base import BaseResource, paginate
@@ -44,7 +44,9 @@ def serialize_event(event):
}
if event.user_id:
- d["user_name"] = event.additional_properties.get("user_name", "User {}".format(event.user_id))
+ d["user_name"] = event.additional_properties.get(
+ "user_name", "User {}".format(event.user_id)
+ )
if not event.user_id:
d["user_name"] = event.additional_properties.get("api_key", "Unknown")
diff --git a/redash/handlers/favorites.py b/redash/handlers/favorites.py
index 796dfb6ab1..71ac3a20b8 100644
--- a/redash/handlers/favorites.py
+++ b/redash/handlers/favorites.py
@@ -1,16 +1,21 @@
+from flask import request
from sqlalchemy.exc import IntegrityError
from redash import models
-from redash.handlers.base import BaseResource, get_object_or_404
+from redash.handlers.base import BaseResource, get_object_or_404, paginate
from redash.permissions import require_access, view_only
class QueryFavoriteResource(BaseResource):
def post(self, query_id):
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_access(query, self.current_user, view_only)
- fav = models.Favorite(org_id=self.current_org.id, object=query, user=self.current_user)
+ fav = models.Favorite(
+ org_id=self.current_org.id, object=query, user=self.current_user
+ )
models.db.session.add(fav)
try:
@@ -21,10 +26,14 @@ def post(self, query_id):
else:
raise e
- self.record_event({"action": "favorite", "object_id": query.id, "object_type": "query"})
+ self.record_event(
+ {"action": "favorite", "object_id": query.id, "object_type": "query"}
+ )
def delete(self, query_id):
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_access(query, self.current_user, view_only)
models.Favorite.query.filter(
@@ -34,13 +43,19 @@ def delete(self, query_id):
).delete()
models.db.session.commit()
- self.record_event({"action": "favorite", "object_id": query.id, "object_type": "query"})
+ self.record_event(
+ {"action": "favorite", "object_id": query.id, "object_type": "query"}
+ )
class DashboardFavoriteResource(BaseResource):
def post(self, object_id):
- dashboard = get_object_or_404(models.Dashboard.get_by_id_and_org, object_id, self.current_org)
- fav = models.Favorite(org_id=self.current_org.id, object=dashboard, user=self.current_user)
+ dashboard = get_object_or_404(
+ models.Dashboard.get_by_slug_and_org, object_id, self.current_org
+ )
+ fav = models.Favorite(
+ org_id=self.current_org.id, object=dashboard, user=self.current_user
+ )
models.db.session.add(fav)
try:
@@ -60,7 +75,9 @@ def post(self, object_id):
)
def delete(self, object_id):
- dashboard = get_object_or_404(models.Dashboard.get_by_id_and_org, object_id, self.current_org)
+ dashboard = get_object_or_404(
+ models.Dashboard.get_by_slug_and_org, object_id, self.current_org
+ )
models.Favorite.query.filter(
models.Favorite.object == dashboard,
models.Favorite.user == self.current_user,
diff --git a/redash/handlers/groups.py b/redash/handlers/groups.py
index 200c31507f..40839e0345 100644
--- a/redash/handlers/groups.py
+++ b/redash/handlers/groups.py
@@ -1,9 +1,9 @@
+import time
from flask import request
from flask_restful import abort
-
from redash import models
-from redash.handlers.base import BaseResource, get_object_or_404
from redash.permissions import require_admin, require_permission
+from redash.handlers.base import BaseResource, get_object_or_404
class GroupListResource(BaseResource):
@@ -14,7 +14,9 @@ def post(self):
models.db.session.add(group)
models.db.session.commit()
- self.record_event({"action": "create", "object_id": group.id, "object_type": "group"})
+ self.record_event(
+ {"action": "create", "object_id": group.id, "object_type": "group"}
+ )
return group.to_dict()
@@ -22,9 +24,13 @@ def get(self):
if self.current_user.has_permission("admin"):
groups = models.Group.all(self.current_org)
else:
- groups = models.Group.query.filter(models.Group.id.in_(self.current_user.group_ids))
+ groups = models.Group.query.filter(
+ models.Group.id.in_(self.current_user.group_ids)
+ )
- self.record_event({"action": "list", "object_id": "groups", "object_type": "group"})
+ self.record_event(
+ {"action": "list", "object_id": "groups", "object_type": "group"}
+ )
return [g.to_dict() for g in groups]
@@ -40,17 +46,24 @@ def post(self, group_id):
group.name = request.json["name"]
models.db.session.commit()
- self.record_event({"action": "edit", "object_id": group.id, "object_type": "group"})
+ self.record_event(
+ {"action": "edit", "object_id": group.id, "object_type": "group"}
+ )
return group.to_dict()
def get(self, group_id):
- if not (self.current_user.has_permission("admin") or int(group_id) in self.current_user.group_ids):
+ if not (
+ self.current_user.has_permission("admin")
+ or int(group_id) in self.current_user.group_ids
+ ):
abort(403)
group = models.Group.get_by_id_and_org(group_id, self.current_org)
- self.record_event({"action": "view", "object_id": group_id, "object_type": "group"})
+ self.record_event(
+ {"action": "view", "object_id": group_id, "object_type": "group"}
+ )
return group.to_dict()
@@ -90,7 +103,10 @@ def post(self, group_id):
@require_permission("list_users")
def get(self, group_id):
- if not (self.current_user.has_permission("admin") or int(group_id) in self.current_user.group_ids):
+ if not (
+ self.current_user.has_permission("admin")
+ or int(group_id) in self.current_user.group_ids
+ ):
abort(403)
members = models.Group.members(group_id)
@@ -124,7 +140,9 @@ class GroupDataSourceListResource(BaseResource):
@require_admin
def post(self, group_id):
data_source_id = request.json["data_source_id"]
- data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org)
+ data_source = models.DataSource.get_by_id_and_org(
+ data_source_id, self.current_org
+ )
group = models.Group.get_by_id_and_org(group_id, self.current_org)
data_source_group = data_source.add_group(group)
@@ -143,14 +161,18 @@ def post(self, group_id):
@require_admin
def get(self, group_id):
- group = get_object_or_404(models.Group.get_by_id_and_org, group_id, self.current_org)
+ group = get_object_or_404(
+ models.Group.get_by_id_and_org, group_id, self.current_org
+ )
# TOOD: move to models
data_sources = models.DataSource.query.join(models.DataSourceGroup).filter(
models.DataSourceGroup.group == group
)
- self.record_event({"action": "list", "object_id": group_id, "object_type": "group"})
+ self.record_event(
+ {"action": "list", "object_id": group_id, "object_type": "group"}
+ )
return [ds.to_dict(with_permissions_for=group) for ds in data_sources]
@@ -158,7 +180,9 @@ def get(self, group_id):
class GroupDataSourceResource(BaseResource):
@require_admin
def post(self, group_id, data_source_id):
- data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org)
+ data_source = models.DataSource.get_by_id_and_org(
+ data_source_id, self.current_org
+ )
group = models.Group.get_by_id_and_org(group_id, self.current_org)
view_only = request.json["view_only"]
@@ -179,7 +203,9 @@ def post(self, group_id, data_source_id):
@require_admin
def delete(self, group_id, data_source_id):
- data_source = models.DataSource.get_by_id_and_org(data_source_id, self.current_org)
+ data_source = models.DataSource.get_by_id_and_org(
+ data_source_id, self.current_org
+ )
group = models.Group.get_by_id_and_org(group_id, self.current_org)
data_source.remove_group(group)
diff --git a/redash/handlers/organization.py b/redash/handlers/organization.py
index 4fa004f5f6..f39548f8ce 100644
--- a/redash/handlers/organization.py
+++ b/redash/handlers/organization.py
@@ -1,9 +1,9 @@
from flask_login import current_user, login_required
from redash import models
-from redash.authentication import current_org
from redash.handlers import routes
from redash.handlers.base import json_response, org_scoped_rule
+from redash.authentication import current_org
@routes.route(org_scoped_rule("/api/organization/status"), methods=["GET"])
@@ -12,10 +12,14 @@ def organization_status(org_slug=None):
counters = {
"users": models.User.all(current_org).count(),
"alerts": models.Alert.all(group_ids=current_user.group_ids).count(),
- "data_sources": models.DataSource.all(current_org, group_ids=current_user.group_ids).count(),
- "queries": models.Query.all_queries(current_user.group_ids, current_user.id, include_drafts=True).count(),
+ "data_sources": models.DataSource.all(
+ current_org, group_ids=current_user.group_ids
+ ).count(),
+ "queries": models.Query.all_queries(
+ current_user.group_ids, current_user.id, include_drafts=True
+ ).count(),
"dashboards": models.Dashboard.query.filter(
- models.Dashboard.org == current_org, models.Dashboard.is_archived.is_(False)
+ models.Dashboard.org == current_org, models.Dashboard.is_archived == False
).count(),
}
diff --git a/redash/handlers/permissions.py b/redash/handlers/permissions.py
index 731ddcc3f5..94bf111eb6 100644
--- a/redash/handlers/permissions.py
+++ b/redash/handlers/permissions.py
@@ -1,12 +1,12 @@
from collections import defaultdict
+from redash.handlers.base import BaseResource, get_object_or_404
+from redash.models import AccessPermission, Query, Dashboard, User, db
+from redash.permissions import require_admin_or_owner, ACCESS_TYPES
from flask import request
from flask_restful import abort
from sqlalchemy.orm.exc import NoResultFound
-from redash.handlers.base import BaseResource, get_object_or_404
-from redash.models import AccessPermission, Dashboard, Query, User, db
-from redash.permissions import ACCESS_TYPES, require_admin_or_owner
model_to_types = {"queries": Query, "dashboards": Dashboard}
@@ -51,7 +51,9 @@ def post(self, object_type, object_id):
except NoResultFound:
abort(400, message="User not found.")
- permission = AccessPermission.grant(obj, access_type, grantee, self.current_user)
+ permission = AccessPermission.grant(
+ obj, access_type, grantee, self.current_user
+ )
db.session.commit()
self.record_event(
diff --git a/redash/handlers/queries.py b/redash/handlers/queries.py
index 71ae418da8..5511d43a3e 100644
--- a/redash/handlers/queries.py
+++ b/redash/handlers/queries.py
@@ -2,8 +2,8 @@
from flask import jsonify, request, url_for
from flask_login import login_required
from flask_restful import abort
-from funcy import partial
from sqlalchemy.orm.exc import StaleDataError
+from funcy import partial
from redash import models, settings
from redash.authentication.org_resolving import current_org
@@ -14,10 +14,9 @@
org_scoped_rule,
paginate,
routes,
+ order_results as _order_results,
)
-from redash.handlers.base import order_results as _order_results
from redash.handlers.query_results import run_query
-from redash.models.parameterized_query import ParameterizedQuery
from redash.permissions import (
can_modify,
not_view_only,
@@ -27,8 +26,10 @@
require_permission,
view_only,
)
-from redash.serializers import QuerySerializer
from redash.utils import collect_parameters_from_request
+from redash.serializers import QuerySerializer
+from redash.models.parameterized_query import ParameterizedQuery
+
# Ordering map for relationships
order_map = {
@@ -46,7 +47,9 @@
"-created_by": "-users-name",
}
-order_results = partial(_order_results, default_order="-created_at", allowed_orders=order_map)
+order_results = partial(
+ _order_results, default_order="-created_at", allowed_orders=order_map
+)
@routes.route(org_scoped_rule("/api/queries/format"), methods=["POST"])
@@ -61,7 +64,9 @@ def format_sql_query(org_slug=None):
arguments = request.get_json(force=True)
query = arguments.get("query", "")
- return jsonify({"query": sqlparse.format(query, **settings.SQLPARSE_FORMAT_OPTIONS)})
+ return jsonify(
+ {"query": sqlparse.format(query, **settings.SQLPARSE_FORMAT_OPTIONS)}
+ )
class QuerySearchResource(BaseResource):
@@ -102,8 +107,14 @@ def get(self):
Responds with a list of :ref:`query ` objects.
"""
- results = models.Query.by_user(self.current_user).order_by(models.Query.updated_at.desc()).limit(10)
- return QuerySerializer(results, with_last_modified_by=False, with_user=False).serialize()
+ results = (
+ models.Query.by_user(self.current_user)
+ .order_by(models.Query.updated_at.desc())
+ .limit(10)
+ )
+ return QuerySerializer(
+ results, with_last_modified_by=False, with_user=False
+ ).serialize()
class BaseQueryListResource(BaseResource):
@@ -117,7 +128,9 @@ def get_queries(self, search_term):
multi_byte_search=current_org.get_setting("multi_byte_search_enabled"),
)
else:
- results = models.Query.all_queries(self.current_user.group_ids, self.current_user.id, include_drafts=True)
+ results = models.Query.all_queries(
+ self.current_user.group_ids, self.current_user.id, include_drafts=True
+ )
return filter_by_tags(results, models.Query.tags)
@require_permission("view_query")
@@ -157,7 +170,9 @@ def get(self):
)
if search_term:
- self.record_event({"action": "search", "object_type": "query", "term": search_term})
+ self.record_event(
+ {"action": "search", "object_type": "query", "term": search_term}
+ )
else:
self.record_event({"action": "list", "object_type": "query"})
@@ -166,7 +181,9 @@ def get(self):
def require_access_to_dropdown_queries(user, query_def):
parameters = query_def.get("options", {}).get("parameters", [])
- dropdown_query_ids = set([str(p["queryId"]) for p in parameters if p["type"] == "query"])
+ dropdown_query_ids = set(
+ [str(p["queryId"]) for p in parameters if p["type"] == "query"]
+ )
if dropdown_query_ids:
groups = models.Query.all_groups_for_query_ids(dropdown_query_ids)
@@ -217,7 +234,9 @@ def post(self):
:>json number runtime: Runtime of last query execution, in seconds (may be null)
"""
query_def = request.get_json(force=True)
- data_source = models.DataSource.get_by_id_and_org(query_def.pop("data_source_id"), self.current_org)
+ data_source = models.DataSource.get_by_id_and_org(
+ query_def.pop("data_source_id"), self.current_org
+ )
require_access(data_source, self.current_user, not_view_only)
require_access_to_dropdown_queries(self.current_user, query_def)
@@ -240,7 +259,9 @@ def post(self):
models.db.session.add(query)
models.db.session.commit()
- self.record_event({"action": "create", "object_id": query.id, "object_type": "query"})
+ self.record_event(
+ {"action": "create", "object_id": query.id, "object_type": "query"}
+ )
return QuerySerializer(query, with_visualizations=True).serialize()
@@ -280,11 +301,7 @@ def get(self):
"""
search_term = request.args.get("q", "")
if search_term:
- results = models.Query.search_by_user(
- search_term,
- self.current_user,
- multi_byte_search=current_org.get_setting("multi_byte_search_enabled"),
- )
+ results = models.Query.search_by_user(search_term, self.current_user)
else:
results = models.Query.by_user(self.current_user)
@@ -323,7 +340,9 @@ def post(self, query_id):
Responds with the updated :ref:`query ` object.
"""
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
query_def = request.get_json(force=True)
require_object_modify_permission(query, self.current_user)
@@ -347,10 +366,6 @@ def post(self, query_id):
if "tags" in query_def:
query_def["tags"] = [tag for tag in query_def["tags"] if tag]
- if "data_source_id" in query_def:
- data_source = models.DataSource.get_by_id_and_org(query_def["data_source_id"], self.current_org)
- require_access(data_source, self.current_user, not_view_only)
-
query_def["last_modified_by"] = self.current_user
query_def["changed_by"] = self.current_user
# SQLAlchemy handles the case where a concurrent transaction beats us
@@ -376,13 +391,17 @@ def get(self, query_id):
Responds with the :ref:`query ` contents.
"""
- q = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ q = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_access(q, self.current_user, view_only)
result = QuerySerializer(q, with_visualizations=True).serialize()
result["can_edit"] = can_modify(q, self.current_user)
- self.record_event({"action": "view", "object_id": query_id, "object_type": "query"})
+ self.record_event(
+ {"action": "view", "object_id": query_id, "object_type": "query"}
+ )
return result
@@ -393,7 +412,9 @@ def delete(self, query_id):
:param query_id: ID of query to archive
"""
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_admin_or_owner(query.user_id)
query.archive(self.current_user)
models.db.session.commit()
@@ -402,7 +423,9 @@ def delete(self, query_id):
class QueryRegenerateApiKeyResource(BaseResource):
@require_permission("edit_query")
def post(self, query_id):
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_admin_or_owner(query.user_id)
query.regenerate_api_key()
models.db.session.commit()
@@ -429,12 +452,16 @@ def post(self, query_id):
Responds with created :ref:`query ` object.
"""
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_access(query.data_source, self.current_user, not_view_only)
forked_query = query.fork(self.current_user)
models.db.session.commit()
- self.record_event({"action": "fork", "object_id": query_id, "object_type": "query"})
+ self.record_event(
+ {"action": "fork", "object_id": query_id, "object_type": "query"}
+ )
return QuerySerializer(forked_query, with_visualizations=True).serialize()
@@ -454,13 +481,17 @@ def post(self, query_id):
if self.current_user.is_api_user():
abort(403, message="Please use a user API key.")
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_access(query, self.current_user, not_view_only)
parameter_values = collect_parameters_from_request(request.args)
parameterized_query = ParameterizedQuery(query.query_text, org=self.current_org)
- should_apply_auto_limit = query.options.get("apply_auto_limit", False)
- return run_query(parameterized_query, parameter_values, query.data_source, query.id, should_apply_auto_limit)
+
+ return run_query(
+ parameterized_query, parameter_values, query.data_source, query.id
+ )
class QueryTagsResource(BaseResource):
@@ -482,7 +513,6 @@ def get(self):
self.current_user.group_ids,
include_drafts=True,
limit=None,
- multi_byte_search=current_org.get_setting("multi_byte_search_enabled"),
)
favorites = models.Query.favorites(self.current_user, base_query=base_query)
else:
diff --git a/redash/handlers/query_results.py b/redash/handlers/query_results.py
index bfc4371d08..4b9e189cce 100644
--- a/redash/handlers/query_results.py
+++ b/redash/handlers/query_results.py
@@ -1,40 +1,42 @@
-import unicodedata
-from urllib.parse import quote
+import logging
+import time
-import regex
+import unicodedata
from flask import make_response, request
from flask_login import current_user
from flask_restful import abort
-
+from werkzeug.urls import url_quote
from redash import models, settings
from redash.handlers.base import BaseResource, get_object_or_404, record_event
-from redash.models.parameterized_query import (
- InvalidParameterError,
- ParameterizedQuery,
- QueryDetachedFromDataSourceError,
- dropdown_values,
-)
from redash.permissions import (
has_access,
not_view_only,
require_access,
- require_any_of_permission,
require_permission,
+ require_any_of_permission,
view_only,
)
-from redash.serializers import (
- serialize_job,
- serialize_query_result,
- serialize_query_result_to_dsv,
- serialize_query_result_to_xlsx,
-)
from redash.tasks import Job
from redash.tasks.queries import enqueue_query
from redash.utils import (
collect_parameters_from_request,
+ gen_query_hash,
json_dumps,
+ utcnow,
to_filename,
)
+from redash.models.parameterized_query import (
+ ParameterizedQuery,
+ InvalidParameterError,
+ QueryDetachedFromDataSourceError,
+ dropdown_values,
+)
+from redash.serializers import (
+ serialize_query_result,
+ serialize_query_result_to_dsv,
+ serialize_query_result_to_xlsx,
+ serialize_job,
+)
def error_response(message, http_status=400):
@@ -50,19 +52,21 @@ def error_response(message, http_status=400):
"This query contains potentially unsafe parameters and cannot be executed with read-only access to this data source.",
403,
),
- "no_permission": error_response("You do not have permission to run queries with this data source.", 403),
- "select_data_source": error_response("Please select data source to run this query.", 401),
- "no_data_source": error_response("Target data source not available.", 401),
+ "no_permission": error_response(
+ "You do not have permission to run queries with this data source.", 403
+ ),
+ "select_data_source": error_response(
+ "Please select data source to run this query.", 401
+ ),
}
-def run_query(query, parameters, data_source, query_id, should_apply_auto_limit, max_age=0):
- if not data_source:
- return error_messages["no_data_source"]
-
+def run_query(query, parameters, data_source, query_id, max_age=0):
if data_source.paused:
if data_source.pause_reason:
- message = "{} is paused ({}). Please try later.".format(data_source.name, data_source.pause_reason)
+ message = "{} is paused ({}). Please try later.".format(
+ data_source.name, data_source.pause_reason
+ )
else:
message = "{} is paused. Please try later.".format(data_source.name)
@@ -73,15 +77,15 @@ def run_query(query, parameters, data_source, query_id, should_apply_auto_limit,
except (InvalidParameterError, QueryDetachedFromDataSourceError) as e:
abort(400, message=str(e))
- query_text = data_source.query_runner.apply_auto_limit(query.text, should_apply_auto_limit)
-
if query.missing_params:
- return error_response("Missing parameter value for: {}".format(", ".join(query.missing_params)))
+ return error_response(
+ "Missing parameter value for: {}".format(", ".join(query.missing_params))
+ )
if max_age == 0:
query_result = None
else:
- query_result = models.QueryResult.get_latest(data_source, query_text, max_age)
+ query_result = models.QueryResult.get_latest(data_source, query.text, max_age)
record_event(
current_user.org,
@@ -91,23 +95,29 @@ def run_query(query, parameters, data_source, query_id, should_apply_auto_limit,
"cache": "hit" if query_result else "miss",
"object_id": data_source.id,
"object_type": "data_source",
- "query": query_text,
+ "query": query.text,
"query_id": query_id,
"parameters": parameters,
},
)
if query_result:
- return {"query_result": serialize_query_result(query_result, current_user.is_api_user())}
+ return {
+ "query_result": serialize_query_result(
+ query_result, current_user.is_api_user()
+ )
+ }
else:
job = enqueue_query(
- query_text,
+ query.text,
data_source,
current_user.id,
current_user.is_api_user(),
metadata={
- "Username": current_user.get_actual_user(),
- "query_id": query_id,
+ "Username": repr(current_user)
+ if current_user.is_api_user()
+ else current_user.email,
+ "Query ID": query_id,
},
)
return serialize_job(job)
@@ -116,8 +126,7 @@ def run_query(query, parameters, data_source, query_id, should_apply_auto_limit,
def get_download_filename(query_result, query, filetype):
retrieved_at = query_result.retrieved_at.strftime("%Y_%m_%d")
if query:
- query_name = regex.sub(r"\p{C}", "", query.name)
- filename = to_filename(query_name) if query_name != "" else str(query.id)
+ filename = to_filename(query.name) if query.name != "" else str(query.id)
else:
filename = str(query_result.id)
return "{}_{}.{}".format(filename, retrieved_at, filetype)
@@ -131,8 +140,10 @@ def content_disposition_filenames(attachment_filename):
attachment_filename = attachment_filename.encode("ascii")
except UnicodeEncodeError:
filenames = {
- "filename": unicodedata.normalize("NFKD", attachment_filename).encode("ascii", "ignore"),
- "filename*": "UTF-8''%s" % quote(attachment_filename, safe=b""),
+ "filename": unicodedata.normalize("NFKD", attachment_filename).encode(
+ "ascii", "ignore"
+ ),
+ "filename*": "UTF-8''%s" % url_quote(attachment_filename, safe=b""),
}
else:
filenames = {"filename": attachment_filename}
@@ -164,14 +175,17 @@ def post(self):
max_age = -1
max_age = int(max_age)
query_id = params.get("query_id", "adhoc")
- parameters = params.get("parameters", collect_parameters_from_request(request.args))
+ parameters = params.get(
+ "parameters", collect_parameters_from_request(request.args)
+ )
parameterized_query = ParameterizedQuery(query, org=self.current_org)
- should_apply_auto_limit = params.get("apply_auto_limit", False)
data_source_id = params.get("data_source_id")
if data_source_id:
- data_source = models.DataSource.get_by_id_and_org(params.get("data_source_id"), self.current_org)
+ data_source = models.DataSource.get_by_id_and_org(
+ params.get("data_source_id"), self.current_org
+ )
else:
return error_messages["select_data_source"]
@@ -179,12 +193,7 @@ def post(self):
return error_messages["no_permission"]
return run_query(
- parameterized_query,
- parameters,
- data_source,
- query_id,
- should_apply_auto_limit,
- max_age,
+ parameterized_query, parameters, data_source, query_id, max_age
)
@@ -193,7 +202,9 @@ def post(self):
class QueryResultDropdownResource(BaseResource):
def get(self, query_id):
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_access(query.data_source, current_user, view_only)
try:
return dropdown_values(query_id, self.current_org)
@@ -203,12 +214,18 @@ def get(self, query_id):
class QueryDropdownsResource(BaseResource):
def get(self, query_id, dropdown_query_id):
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
require_access(query, current_user, view_only)
- related_queries_ids = [p["queryId"] for p in query.parameters if p["type"] == "query"]
+ related_queries_ids = [
+ p["queryId"] for p in query.parameters if p["type"] == "query"
+ ]
if int(dropdown_query_id) not in related_queries_ids:
- dropdown_query = get_object_or_404(models.Query.get_by_id_and_org, dropdown_query_id, self.current_org)
+ dropdown_query = get_object_or_404(
+ models.Query.get_by_id_and_org, dropdown_query_id, self.current_org
+ )
require_access(dropdown_query.data_source, current_user, view_only)
return dropdown_values(dropdown_query_id, self.current_org)
@@ -222,7 +239,9 @@ def add_cors_headers(headers):
if set(["*", origin]) & settings.ACCESS_CONTROL_ALLOW_ORIGIN:
headers["Access-Control-Allow-Origin"] = origin
- headers["Access-Control-Allow-Credentials"] = str(settings.ACCESS_CONTROL_ALLOW_CREDENTIALS).lower()
+ headers["Access-Control-Allow-Credentials"] = str(
+ settings.ACCESS_CONTROL_ALLOW_CREDENTIALS
+ ).lower()
@require_any_of_permission(("view_query", "execute_query"))
def options(self, query_id=None, query_result_id=None, filetype="json"):
@@ -230,10 +249,14 @@ def options(self, query_id=None, query_result_id=None, filetype="json"):
self.add_cors_headers(headers)
if settings.ACCESS_CONTROL_REQUEST_METHOD:
- headers["Access-Control-Request-Method"] = settings.ACCESS_CONTROL_REQUEST_METHOD
+ headers[
+ "Access-Control-Request-Method"
+ ] = settings.ACCESS_CONTROL_REQUEST_METHOD
if settings.ACCESS_CONTROL_ALLOW_HEADERS:
- headers["Access-Control-Allow-Headers"] = settings.ACCESS_CONTROL_ALLOW_HEADERS
+ headers[
+ "Access-Control-Allow-Headers"
+ ] = settings.ACCESS_CONTROL_ALLOW_HEADERS
return make_response("", 200, headers)
@@ -258,21 +281,20 @@ def post(self, query_id):
max_age = -1
max_age = int(max_age)
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
allow_executing_with_view_only_permissions = query.parameterized.is_safe
- if "apply_auto_limit" in params:
- should_apply_auto_limit = params.get("apply_auto_limit", False)
- else:
- should_apply_auto_limit = query.options.get("apply_auto_limit", False)
- if has_access(query, self.current_user, allow_executing_with_view_only_permissions):
+ if has_access(
+ query, self.current_user, allow_executing_with_view_only_permissions
+ ):
return run_query(
query.parameterized,
parameter_values,
query.data_source,
query_id,
- should_apply_auto_limit,
max_age,
)
else:
@@ -307,23 +329,38 @@ def get(self, query_id=None, query_result_id=None, filetype="json"):
# should check for query parameters and shouldn't cache the result).
should_cache = query_result_id is not None
+ parameter_values = collect_parameters_from_request(request.args)
+ max_age = int(request.args.get("maxAge", 0))
+
query_result = None
query = None
if query_result_id:
- query_result = get_object_or_404(models.QueryResult.get_by_id_and_org, query_result_id, self.current_org)
+ query_result = get_object_or_404(
+ models.QueryResult.get_by_id_and_org, query_result_id, self.current_org
+ )
if query_id is not None:
- query = get_object_or_404(models.Query.get_by_id_and_org, query_id, self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, query_id, self.current_org
+ )
- if query_result is None and query is not None and query.latest_query_data_id is not None:
+ if (
+ query_result is None
+ and query is not None
+ and query.latest_query_data_id is not None
+ ):
query_result = get_object_or_404(
models.QueryResult.get_by_id_and_org,
query.latest_query_data_id,
self.current_org,
)
- if query is not None and query_result is not None and self.current_user.is_api_user():
+ if (
+ query is not None
+ and query_result is not None
+ and self.current_user.is_api_user()
+ ):
if query.query_hash != query_result.query_hash:
abort(404, message="No cached result found for this query.")
@@ -351,10 +388,10 @@ def get(self, query_id=None, query_result_id=None, filetype="json"):
self.record_event(event)
response_builders = {
- "json": self.make_json_response,
- "xlsx": self.make_excel_response,
- "csv": self.make_csv_response,
- "tsv": self.make_tsv_response,
+ 'json': self.make_json_response,
+ 'xlsx': self.make_excel_response,
+ 'csv': self.make_csv_response,
+ 'tsv': self.make_tsv_response
}
response = response_builders[filetype](query_result)
@@ -362,7 +399,9 @@ def get(self, query_id=None, query_result_id=None, filetype="json"):
self.add_cors_headers(response.headers)
if should_cache:
- response.headers.add_header("Cache-Control", "private,max-age=%d" % ONE_YEAR)
+ response.headers.add_header(
+ "Cache-Control", "private,max-age=%d" % ONE_YEAR
+ )
filename = get_download_filename(query_result, query, filetype)
@@ -392,7 +431,9 @@ def make_tsv_response(query_result):
@staticmethod
def make_excel_response(query_result):
- headers = {"Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}
+ headers = {
+ "Content-Type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+ }
return make_response(serialize_query_result_to_xlsx(query_result), 200, headers)
diff --git a/redash/handlers/query_snippets.py b/redash/handlers/query_snippets.py
index b02eb74425..64808de522 100644
--- a/redash/handlers/query_snippets.py
+++ b/redash/handlers/query_snippets.py
@@ -2,36 +2,42 @@
from funcy import project
from redash import models
-from redash.handlers.base import (
- BaseResource,
- get_object_or_404,
- require_fields,
-)
from redash.permissions import require_admin_or_owner
+from redash.handlers.base import BaseResource, require_fields, get_object_or_404
class QuerySnippetResource(BaseResource):
def get(self, snippet_id):
- snippet = get_object_or_404(models.QuerySnippet.get_by_id_and_org, snippet_id, self.current_org)
+ snippet = get_object_or_404(
+ models.QuerySnippet.get_by_id_and_org, snippet_id, self.current_org
+ )
- self.record_event({"action": "view", "object_id": snippet_id, "object_type": "query_snippet"})
+ self.record_event(
+ {"action": "view", "object_id": snippet_id, "object_type": "query_snippet"}
+ )
return snippet.to_dict()
def post(self, snippet_id):
req = request.get_json(True)
params = project(req, ("trigger", "description", "snippet"))
- snippet = get_object_or_404(models.QuerySnippet.get_by_id_and_org, snippet_id, self.current_org)
+ snippet = get_object_or_404(
+ models.QuerySnippet.get_by_id_and_org, snippet_id, self.current_org
+ )
require_admin_or_owner(snippet.user.id)
self.update_model(snippet, params)
models.db.session.commit()
- self.record_event({"action": "edit", "object_id": snippet.id, "object_type": "query_snippet"})
+ self.record_event(
+ {"action": "edit", "object_id": snippet.id, "object_type": "query_snippet"}
+ )
return snippet.to_dict()
def delete(self, snippet_id):
- snippet = get_object_or_404(models.QuerySnippet.get_by_id_and_org, snippet_id, self.current_org)
+ snippet = get_object_or_404(
+ models.QuerySnippet.get_by_id_and_org, snippet_id, self.current_org
+ )
require_admin_or_owner(snippet.user.id)
models.db.session.delete(snippet)
models.db.session.commit()
@@ -73,4 +79,7 @@ def post(self):
def get(self):
self.record_event({"action": "list", "object_type": "query_snippet"})
- return [snippet.to_dict() for snippet in models.QuerySnippet.all(org=self.current_org)]
+ return [
+ snippet.to_dict()
+ for snippet in models.QuerySnippet.all(org=self.current_org)
+ ]
diff --git a/redash/handlers/settings.py b/redash/handlers/settings.py
index ecf2796e57..d684f42c35 100644
--- a/redash/handlers/settings.py
+++ b/redash/handlers/settings.py
@@ -1,7 +1,7 @@
from flask import request
-from redash.handlers.base import BaseResource
-from redash.models import Organization, db
+from redash.models import db, Organization
+from redash.handlers.base import BaseResource, record_event
from redash.permissions import require_admin
from redash.settings.organization import settings as org_settings
@@ -45,7 +45,9 @@ def post(self):
previous_values[k] = self.current_org.google_apps_domains
self.current_org.settings[Organization.SETTING_GOOGLE_APPS_DOMAINS] = v
else:
- previous_values[k] = self.current_org.get_setting(k, raise_on_missing=False)
+ previous_values[k] = self.current_org.get_setting(
+ k, raise_on_missing=False
+ )
self.current_org.set_setting(k, v)
db.session.add(self.current_org)
diff --git a/redash/handlers/setup.py b/redash/handlers/setup.py
index f7e8fab553..caa9be8641 100644
--- a/redash/handlers/setup.py
+++ b/redash/handlers/setup.py
@@ -1,13 +1,13 @@
from flask import g, redirect, render_template, request, url_for
-from flask_login import login_user
-from wtforms import BooleanField, Form, PasswordField, StringField, validators
-from wtforms.fields.html5 import EmailField
+from flask_login import login_user
from redash import settings
from redash.authentication.org_resolving import current_org
from redash.handlers.base import routes
from redash.models import Group, Organization, User, db
from redash.tasks.general import subscribe
+from wtforms import BooleanField, Form, PasswordField, StringField, validators
+from wtforms.fields.html5 import EmailField
class SetupForm(Form):
@@ -23,7 +23,7 @@ def create_org(org_name, user_name, email, password):
default_org = Organization(name=org_name, slug="default", settings={})
admin_group = Group(
name="admin",
- permissions=Group.ADMIN_PERMISSIONS,
+ permissions=["admin", "super_admin"],
org=default_org,
type=Group.BUILTIN_GROUP,
)
@@ -53,7 +53,7 @@ def create_org(org_name, user_name, email, password):
@routes.route("/setup", methods=["GET", "POST"])
def setup():
- if current_org != None or settings.MULTI_ORG: # noqa: E711
+ if current_org != None or settings.MULTI_ORG:
return redirect("/")
form = SetupForm(request.form)
@@ -61,7 +61,9 @@ def setup():
form.security_notifications.data = True
if request.method == "POST" and form.validate():
- default_org, user = create_org(form.org_name.data, form.name.data, form.email.data, form.password.data)
+ default_org, user = create_org(
+ form.org_name.data, form.name.data, form.email.data, form.password.data
+ )
g.org = default_org
login_user(user)
diff --git a/redash/handlers/static.py b/redash/handlers/static.py
index 71f10fedb4..1a02b66379 100644
--- a/redash/handlers/static.py
+++ b/redash/handlers/static.py
@@ -1,7 +1,6 @@
-from flask import render_template, send_file
-from flask_login import login_required
-from werkzeug.utils import safe_join
+from flask import render_template, safe_join, send_file
+from flask_login import login_required
from redash import settings
from redash.handlers import routes
from redash.handlers.authentication import base_href
@@ -14,7 +13,7 @@ def render_index():
response = render_template("multi_org.html", base_href=base_href())
else:
full_path = safe_join(settings.STATIC_ASSETS_PATH, "index.html")
- response = send_file(full_path, **dict(max_age=0, conditional=True))
+ response = send_file(full_path, **dict(cache_timeout=0, conditional=True))
return response
diff --git a/redash/handlers/users.py b/redash/handlers/users.py
index 0c5b305c82..446a8c5f66 100644
--- a/redash/handlers/users.py
+++ b/redash/handlers/users.py
@@ -1,33 +1,39 @@
-from disposable_email_domains import blacklist
+import re
+import time
from flask import request
-from flask_login import current_user, login_user
from flask_restful import abort
-from funcy import partial, project
-from sqlalchemy.exc import IntegrityError
+from flask_login import current_user, login_user
+from funcy import project
from sqlalchemy.orm.exc import NoResultFound
+from sqlalchemy.exc import IntegrityError
+from disposable_email_domains import blacklist
+from funcy import partial
-from redash import limiter, models, settings
-from redash.authentication.account import (
- invite_link_for_user,
- send_invite_email,
- send_password_reset_email,
- send_verify_email,
+from redash import models, limiter
+from redash.permissions import (
+ require_permission,
+ require_admin_or_owner,
+ is_admin_or_owner,
+ require_permission_or_owner,
+ require_admin,
)
from redash.handlers.base import (
BaseResource,
+ require_fields,
get_object_or_404,
paginate,
- require_fields,
+ order_results as _order_results,
)
-from redash.handlers.base import order_results as _order_results
-from redash.permissions import (
- is_admin_or_owner,
- require_admin,
- require_admin_or_owner,
- require_permission,
- require_permission_or_owner,
+
+from redash.authentication.account import (
+ invite_link_for_user,
+ send_invite_email,
+ send_password_reset_email,
+ send_verify_email,
)
from redash.settings import parse_boolean
+from redash import settings
+
# Ordering map for relationships
order_map = {
@@ -41,7 +47,9 @@
"-groups": "-group_ids",
}
-order_results = partial(_order_results, default_order="-created_at", allowed_orders=order_map)
+order_results = partial(
+ _order_results, default_order="-created_at", allowed_orders=order_map
+)
def invite_user(org, inviter, user, send_email=True):
@@ -56,16 +64,10 @@ def invite_user(org, inviter, user, send_email=True):
return d
-def require_allowed_email(email):
- # `example.com` and `example.com.` are equal - last dot stands for DNS root but usually is omitted
- _, domain = email.lower().rstrip(".").split("@", 1)
-
- if domain in blacklist or domain in settings.BLOCKED_DOMAINS:
- abort(400, message="Bad email address.")
-
-
class UserListResource(BaseResource):
- decorators = BaseResource.decorators + [limiter.limit("200/day;50/hour", methods=["POST"])]
+ decorators = BaseResource.decorators + [
+ limiter.limit("200/day;50/hour", methods=["POST"])
+ ]
def get_users(self, disabled, pending, search_term):
if disabled:
@@ -87,7 +89,9 @@ def get_users(self, disabled, pending, search_term):
}
)
else:
- self.record_event({"action": "list", "object_type": "user", "pending": pending})
+ self.record_event(
+ {"action": "list", "object_type": "user", "pending": pending}
+ )
# order results according to passed order parameter,
# special-casing search queries where the database
@@ -119,7 +123,9 @@ def serialize_user(user):
disabled = request.args.get("disabled", "false") # get enabled users by default
disabled = parse_boolean(disabled)
- pending = request.args.get("pending", None) # get both active and pending by default
+ pending = request.args.get(
+ "pending", None
+ ) # get both active and pending by default
if pending is not None:
pending = parse_boolean(pending)
@@ -134,7 +140,10 @@ def post(self):
if "@" not in req["email"]:
abort(400, message="Bad email address.")
- require_allowed_email(req["email"])
+ name, domain = req["email"].split("@", 1)
+
+ if domain.lower() in blacklist or domain.lower() == "qq.com":
+ abort(400, message="Bad email address.")
user = models.User(
org=self.current_org,
@@ -148,14 +157,19 @@ def post(self):
models.db.session.add(user)
models.db.session.commit()
except IntegrityError as e:
+ models.db.session.rollback()
if "email" in str(e):
abort(400, message="Email already taken.")
abort(500)
- self.record_event({"action": "create", "object_id": user.id, "object_type": "user"})
+ self.record_event(
+ {"action": "create", "object_id": user.id, "object_type": "user"}
+ )
should_send_invitation = "no_invite" not in request.args
- return invite_user(self.current_org, self.current_user, user, send_email=should_send_invitation)
+ return invite_user(
+ self.current_org, self.current_user, user, send_email=should_send_invitation
+ )
class UserInviteResource(BaseResource):
@@ -187,7 +201,9 @@ def post(self, user_id):
user.regenerate_api_key()
models.db.session.commit()
- self.record_event({"action": "regnerate_api_key", "object_id": user.id, "object_type": "user"})
+ self.record_event(
+ {"action": "regnerate_api_key", "object_id": user.id, "object_type": "user"}
+ )
return user.to_dict(with_api_key=True)
@@ -197,24 +213,32 @@ class UserResource(BaseResource):
def get(self, user_id):
require_permission_or_owner("list_users", user_id)
- user = get_object_or_404(models.User.get_by_id_and_org, user_id, self.current_org)
+ user = get_object_or_404(
+ models.User.get_by_id_and_org, user_id, self.current_org
+ )
- self.record_event({"action": "view", "object_id": user_id, "object_type": "user"})
+ self.record_event(
+ {"action": "view", "object_id": user_id, "object_type": "user"}
+ )
return user.to_dict(with_api_key=is_admin_or_owner(user_id))
- def post(self, user_id): # noqa: C901
+ def post(self, user_id):
require_admin_or_owner(user_id)
user = models.User.get_by_id_and_org(user_id, self.current_org)
req = request.get_json(True)
- params = project(req, ("email", "name", "password", "old_password", "group_ids"))
+ params = project(
+ req, ("email", "name", "password", "old_password", "group_ids")
+ )
if "password" in params and "old_password" not in params:
abort(403, message="Must provide current password to update password.")
- if "old_password" in params and not user.verify_password(params["old_password"]):
+ if "old_password" in params and not user.verify_password(
+ params["old_password"]
+ ):
abort(403, message="Incorrect current password.")
if "password" in params:
@@ -235,10 +259,15 @@ def post(self, user_id): # noqa: C901
params.pop("group_ids")
if "email" in params:
- require_allowed_email(params["email"])
+ _, domain = params["email"].split("@", 1)
+
+ if domain.lower() in blacklist or domain.lower() == "qq.com":
+ abort(400, message="Bad email address.")
email_address_changed = "email" in params and params["email"] != user.email
- needs_to_verify_email = email_address_changed and settings.email_server_is_configured()
+ needs_to_verify_email = (
+ email_address_changed and settings.email_server_is_configured()
+ )
if needs_to_verify_email:
user.is_email_verified = False
@@ -259,7 +288,7 @@ def post(self, user_id): # noqa: C901
message = "Email already taken."
else:
message = "Error updating record"
-
+ models.db.session.rollback()
abort(400, message=message)
self.record_event(
@@ -282,13 +311,13 @@ def delete(self, user_id):
abort(
403,
message="You cannot delete your own account. "
- "Please ask another admin to do this for you.", # fmt: skip
+ "Please ask another admin to do this for you.",
)
elif not user.is_invitation_pending:
abort(
403,
message="You cannot delete activated users. "
- "Please disable the user instead.", # fmt: skip
+ "Please disable the user instead.",
)
models.db.session.delete(user)
models.db.session.commit()
@@ -306,7 +335,7 @@ def post(self, user_id):
abort(
403,
message="You cannot disable your own account. "
- "Please ask another admin to do this for you.", # fmt: skip
+ "Please ask another admin to do this for you.",
)
user.disable()
models.db.session.commit()
diff --git a/redash/handlers/visualizations.py b/redash/handlers/visualizations.py
index f29a1fb36c..1621ea50cd 100644
--- a/redash/handlers/visualizations.py
+++ b/redash/handlers/visualizations.py
@@ -2,11 +2,9 @@
from redash import models
from redash.handlers.base import BaseResource, get_object_or_404
-from redash.permissions import (
- require_object_modify_permission,
- require_permission,
-)
from redash.serializers import serialize_visualization
+from redash.permissions import require_object_modify_permission, require_permission
+from redash.utils import json_dumps
class VisualizationListResource(BaseResource):
@@ -14,9 +12,12 @@ class VisualizationListResource(BaseResource):
def post(self):
kwargs = request.get_json(force=True)
- query = get_object_or_404(models.Query.get_by_id_and_org, kwargs.pop("query_id"), self.current_org)
+ query = get_object_or_404(
+ models.Query.get_by_id_and_org, kwargs.pop("query_id"), self.current_org
+ )
require_object_modify_permission(query, self.current_user)
+ kwargs["options"] = json_dumps(kwargs["options"])
kwargs["query_rel"] = query
vis = models.Visualization(**kwargs)
@@ -28,10 +29,14 @@ def post(self):
class VisualizationResource(BaseResource):
@require_permission("edit_query")
def post(self, visualization_id):
- vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)
+ vis = get_object_or_404(
+ models.Visualization.get_by_id_and_org, visualization_id, self.current_org
+ )
require_object_modify_permission(vis.query_rel, self.current_user)
kwargs = request.get_json(force=True)
+ if "options" in kwargs:
+ kwargs["options"] = json_dumps(kwargs["options"])
kwargs.pop("id", None)
kwargs.pop("query_id", None)
@@ -43,7 +48,9 @@ def post(self, visualization_id):
@require_permission("edit_query")
def delete(self, visualization_id):
- vis = get_object_or_404(models.Visualization.get_by_id_and_org, visualization_id, self.current_org)
+ vis = get_object_or_404(
+ models.Visualization.get_by_id_and_org, visualization_id, self.current_org
+ )
require_object_modify_permission(vis.query_rel, self.current_user)
self.record_event(
{
diff --git a/redash/handlers/webpack.py b/redash/handlers/webpack.py
index 8b7cb2dc50..01a0342549 100644
--- a/redash/handlers/webpack.py
+++ b/redash/handlers/webpack.py
@@ -1,9 +1,10 @@
-import json
import os
-
+import simplejson
from flask import url_for
-WEBPACK_MANIFEST_PATH = os.path.join(os.path.dirname(__file__), "../../client/dist/", "asset-manifest.json")
+WEBPACK_MANIFEST_PATH = os.path.join(
+ os.path.dirname(__file__), "../../client/dist/", "asset-manifest.json"
+)
def configure_webpack(app):
@@ -15,7 +16,7 @@ def get_asset(path):
if assets is None or app.debug:
try:
with open(WEBPACK_MANIFEST_PATH) as fp:
- assets = json.load(fp)
+ assets = simplejson.load(fp)
except IOError:
app.logger.exception("Unable to load webpack manifest")
assets = {}
diff --git a/redash/handlers/widgets.py b/redash/handlers/widgets.py
index 051b6e386c..6907943405 100644
--- a/redash/handlers/widgets.py
+++ b/redash/handlers/widgets.py
@@ -2,13 +2,14 @@
from redash import models
from redash.handlers.base import BaseResource
+from redash.serializers import serialize_widget
from redash.permissions import (
require_access,
require_object_modify_permission,
require_permission,
view_only,
)
-from redash.serializers import serialize_widget
+from redash.utils import json_dumps
class WidgetListResource(BaseResource):
@@ -26,14 +27,19 @@ def post(self):
:>json object widget: The created widget
"""
widget_properties = request.get_json(force=True)
- dashboard = models.Dashboard.get_by_id_and_org(widget_properties.get("dashboard_id"), self.current_org)
+ dashboard = models.Dashboard.get_by_id_and_org(
+ widget_properties.get("dashboard_id"), self.current_org
+ )
require_object_modify_permission(dashboard, self.current_user)
+ widget_properties["options"] = json_dumps(widget_properties["options"])
widget_properties.pop("id", None)
visualization_id = widget_properties.pop("visualization_id")
if visualization_id:
- visualization = models.Visualization.get_by_id_and_org(visualization_id, self.current_org)
+ visualization = models.Visualization.get_by_id_and_org(
+ visualization_id, self.current_org
+ )
require_access(visualization.query_rel, self.current_user, view_only)
else:
visualization = None
@@ -42,6 +48,7 @@ def post(self):
widget = models.Widget(**widget_properties)
models.db.session.add(widget)
+ models.db.session.commit()
models.db.session.commit()
return serialize_widget(widget)
@@ -62,7 +69,7 @@ def post(self, widget_id):
require_object_modify_permission(widget.dashboard, self.current_user)
widget_properties = request.get_json(force=True)
widget.text = widget_properties["text"]
- widget.options = widget_properties["options"]
+ widget.options = json_dumps(widget_properties["options"])
models.db.session.commit()
return serialize_widget(widget)
@@ -75,6 +82,8 @@ def delete(self, widget_id):
"""
widget = models.Widget.get_by_id_and_org(widget_id, self.current_org)
require_object_modify_permission(widget.dashboard, self.current_user)
- self.record_event({"action": "delete", "object_id": widget_id, "object_type": "widget"})
+ self.record_event(
+ {"action": "delete", "object_id": widget_id, "object_type": "widget"}
+ )
models.db.session.delete(widget)
models.db.session.commit()
diff --git a/redash/metrics/database.py b/redash/metrics/database.py
index 6f16bdbc6d..8b12765fe9 100644
--- a/redash/metrics/database.py
+++ b/redash/metrics/database.py
@@ -2,12 +2,12 @@
import time
from flask import g, has_request_context
+
+from redash import statsd_client
from sqlalchemy.engine import Engine
from sqlalchemy.event import listens_for
from sqlalchemy.orm.util import _ORMJoin
-from sqlalchemy.sql.selectable import Alias, Join
-
-from redash import statsd_client
+from sqlalchemy.sql.selectable import Alias
metrics_logger = logging.getLogger("metrics")
@@ -18,7 +18,7 @@ def _table_name_from_select_element(elt):
if isinstance(t, Alias):
t = t.original.froms[0]
- while isinstance(t, _ORMJoin) or isinstance(t, Join):
+ while isinstance(t, _ORMJoin):
t = t.left
return t.name
diff --git a/redash/metrics/request.py b/redash/metrics/request.py
index 9dc169f3b7..7f94da4ad9 100644
--- a/redash/metrics/request.py
+++ b/redash/metrics/request.py
@@ -9,7 +9,7 @@
metrics_logger = logging.getLogger("metrics")
-def record_request_start_time():
+def record_requets_start_time():
g.start_time = time.time()
@@ -35,12 +35,16 @@ def calculate_metrics(response):
queries_duration,
)
- statsd_client.timing("requests.{}.{}".format(endpoint, request.method.lower()), request_duration)
+ statsd_client.timing(
+ "requests.{}.{}".format(endpoint, request.method.lower()), request_duration
+ )
return response
-MockResponse = namedtuple("MockResponse", ["status_code", "content_type", "content_length"])
+MockResponse = namedtuple(
+ "MockResponse", ["status_code", "content_type", "content_length"]
+)
def calculate_metrics_on_exception(error):
@@ -49,6 +53,6 @@ def calculate_metrics_on_exception(error):
def init_app(app):
- app.before_request(record_request_start_time)
+ app.before_request(record_requets_start_time)
app.after_request(calculate_metrics)
app.teardown_request(calculate_metrics_on_exception)
diff --git a/redash/models/__init__.py b/redash/models/__init__.py
index 38f306bb73..3cc93ab6cc 100644
--- a/redash/models/__init__.py
+++ b/redash/models/__init__.py
@@ -1,12 +1,12 @@
-import calendar
import datetime
+import calendar
import logging
-import numbers
import time
-
+import numbers
import pytz
-from sqlalchemy import UniqueConstraint, and_, cast, distinct, func, or_
-from sqlalchemy.dialects.postgresql import ARRAY, DOUBLE_PRECISION, JSONB
+
+from sqlalchemy import distinct, or_, and_, UniqueConstraint
+from sqlalchemy.dialects import postgresql
from sqlalchemy.event import listens_for
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import (
@@ -14,77 +14,54 @@
contains_eager,
joinedload,
load_only,
- subqueryload,
+ relationship,
)
from sqlalchemy.orm.exc import NoResultFound # noqa: F401
+from sqlalchemy import func
from sqlalchemy_utils import generic_relationship
-from sqlalchemy_utils.models import generic_repr
from sqlalchemy_utils.types import TSVectorType
+from sqlalchemy_utils.models import generic_repr
from sqlalchemy_utils.types.encrypted.encrypted_type import FernetEngine
-from redash import redis_connection, settings, utils
+from redash import redis_connection, utils, settings
from redash.destinations import (
get_configuration_schema_for_destination_type,
get_destination,
)
from redash.metrics import database # noqa: F401
-from redash.models.base import (
- Column,
- GFKBase,
- SearchBaseQuery,
- db,
- gfk_type,
- key_type,
- primary_key,
-)
-from redash.models.changes import Change, ChangeTrackingMixin # noqa
-from redash.models.mixins import BelongsToOrgMixin, TimestampMixin
-from redash.models.organizations import Organization
-from redash.models.parameterized_query import (
- InvalidParameterError,
- ParameterizedQuery,
- QueryDetachedFromDataSourceError,
-)
-from redash.models.types import (
- Configuration,
- EncryptedConfiguration,
- JSONText,
- MutableDict,
- MutableList,
- json_cast_property,
-)
-from redash.models.users import ( # noqa
- AccessPermission,
- AnonymousUser,
- ApiUser,
- Group,
- User,
-)
from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- BaseQueryRunner,
+ with_ssh_tunnel,
get_configuration_schema_for_query_runner_type,
get_query_runner,
- with_ssh_tunnel,
)
from redash.utils import (
- base_url,
- gen_query_hash,
generate_token,
- json_dumps,
json_loads,
mustache_render,
- mustache_render_escape,
+ base_url,
sentry,
)
from redash.utils.configuration import ConfigurationContainer
+from redash.models.parameterized_query import ParameterizedQuery
+
+from .base import db, gfk_type, Column, GFKBase, SearchBaseQuery, key_type, primary_key
+from .changes import ChangeTrackingMixin, Change # noqa
+from .mixins import BelongsToOrgMixin, TimestampMixin
+from .organizations import Organization
+from .types import (
+ EncryptedConfiguration,
+ Configuration,
+ MutableDict,
+ MutableList,
+ PseudoJSON,
+ pseudo_json_cast_property
+)
+from .users import AccessPermission, AnonymousUser, ApiUser, Group, User # noqa
logger = logging.getLogger(__name__)
-class ScheduledQueriesExecutions:
+class ScheduledQueriesExecutions(object):
KEY_NAME = "sq:executed_at"
def __init__(self):
@@ -94,7 +71,7 @@ def refresh(self):
self.executions = redis_connection.hgetall(self.KEY_NAME)
def update(self, query_id):
- redis_connection.hset(self.KEY_NAME, mapping={query_id: time.time()})
+ redis_connection.hmset(self.KEY_NAME, {query_id: time.time()})
def get(self, query_id):
timestamp = self.executions.get(str(query_id))
@@ -107,6 +84,219 @@ def get(self, query_id):
scheduled_queries_executions = ScheduledQueriesExecutions()
+def cleanup_data_in_table(table_model):
+ ttl_days_ago = utils.utcnow() - datetime.timedelta(
+ days=settings.SCHEMA_METADATA_TTL_DAYS
+ )
+
+ table_model.query.filter(
+ table_model.exists.is_(False), table_model.updated_at < ttl_days_ago
+ ).delete()
+
+ db.session.commit()
+
+
+class SchemaCache:
+ """
+ This caches schema requests in redis and uses a method to
+ serve stale values while the cache is being populated or
+ updated to handle the thundering herd problem.
+ """
+
+ # SCHEMAS_REFRESH_SCHEDULE is in minutes, converting to seconds here:
+ timeout = settings.SCHEMAS_REFRESH_SCHEDULE * 60
+ # keeping the stale cached items for 10 minutes longer
+ # than its timeout to make sure repopulation can work
+ stale_cache_timeout = 60 * 10
+
+ def __init__(self, data_source):
+ self.data_source = data_source
+ self.client = redis_connection
+ self.cache_key = f"data_source:schema:cache:{self.data_source.id}"
+ self.lock_key = f"{self.cache_key}:lock"
+ self.fresh_key = f"{self.cache_key}:fresh"
+
+ def populate(self, schema=None, forced=False):
+ """
+ This is the central method to populate the cache and return
+ either the provided fallback schema or the value loaded
+ from the database.
+
+ It uses Redis locking to make sure the retrieval from the
+ database isn't run many times at once.
+
+ It also sets a separate key that indicates freshness that has
+ a shorter ttl than the actual cache key that contains the
+ schema.
+
+ In the get_schema method it'll check the freshness key first
+ and trigger a repopulation of the cache key if it's stale.
+ """
+ lock = redis_connection.lock(self.lock_key, timeout=self.timeout)
+ acquired = lock.acquire(blocking=False)
+
+ if acquired or forced:
+ try:
+ schema = TableMetadata.load(self.data_source)
+ except Exception:
+ raise
+ else:
+ key_timeout = self.timeout + self.stale_cache_timeout
+ pipeline = redis_connection.pipeline()
+ pipeline.set(self.cache_key, utils.json_dumps(schema), key_timeout)
+ pipeline.set(self.fresh_key, 1, self.timeout)
+ pipeline.execute()
+ finally:
+ if acquired:
+ lock.release()
+
+ return schema or []
+
+
+@generic_repr("id", "name", "data_source_id", "org_id", "exists", "column_metadata")
+class TableMetadata(TimestampMixin, db.Model):
+ id = Column(db.Integer, primary_key=True)
+ org_id = Column(db.Integer, db.ForeignKey("organizations.id"))
+ data_source_id = Column(
+ db.Integer, db.ForeignKey("data_sources.id", ondelete="CASCADE"), index=True
+ )
+ exists = Column(db.Boolean, default=True, index=True)
+ visible = Column(db.Boolean, default=True)
+ name = Column(db.String(255), index=True)
+ description = Column(db.String(4096), nullable=True)
+ column_metadata = Column(db.Boolean, default=False)
+ sample_updated_at = Column(db.DateTime(True), nullable=True, index=True)
+ sample_queries = relationship(
+ "Query", secondary="tablemetadata_queries_link", backref="relevant_tables"
+ )
+ existing_columns = db.relationship(
+ "ColumnMetadata",
+ backref="table",
+ order_by="ColumnMetadata.name",
+ primaryjoin="and_(TableMetadata.id == ColumnMetadata.table_id, ColumnMetadata.exists.is_(True))",
+ )
+
+ __tablename__ = "table_metadata"
+ __table_args__ = (
+ db.Index("ix_table_metadata_data_source_id_exists", "data_source_id", "exists"),
+ db.Index(
+ "ix_table_metadata_data_source_id_name_exists",
+ "data_source_id",
+ "exists",
+ "name",
+ ),
+ )
+
+ def __str__(self):
+ return str(self.name)
+
+ @classmethod
+ def store(cls, data_source, existing_tables_set, table_data):
+ """
+ Insert new or update all existing tables to reflect the provided data.
+ """
+ existing_tables = cls.query.filter(
+ cls.name.in_(existing_tables_set), cls.data_source_id == data_source.id,
+ )
+ table_names = set()
+ for table in existing_tables:
+ table_names.add(table.name)
+ for name, value in table_data[table.name].items():
+ setattr(table, name, value)
+ db.session.add(table)
+
+ # Find the tables that need to be created by subtracting the sets:
+ for table_name in existing_tables_set.difference(table_names):
+ db.session.add(cls(**table_data[table_name]))
+ db.session.commit()
+
+ @classmethod
+ def load(cls, data_source):
+ """
+ When called will fetch all table and column metadata from
+ the database and serialize it with the TableMetadataSerializer.
+ """
+ # due to the unfortunate import time side effects of
+ # Redash's package layout this needs to be done inline
+ from redash.serializers import TableMetadataSerializer
+
+ schema = []
+ tables = (
+ cls.query.filter(
+ cls.data_source_id == data_source.id, cls.exists.is_(True),
+ )
+ .order_by(cls.name)
+ .options(joinedload(cls.existing_columns), joinedload(cls.sample_queries),)
+ )
+
+ for table in tables:
+ schema.append(
+ TableMetadataSerializer(table, with_favorite_state=False).serialize()
+ )
+
+ return schema
+
+
+@generic_repr("id", "name", "type", "table_id", "org_id", "exists")
+class ColumnMetadata(TimestampMixin, db.Model):
+ id = Column(db.Integer, primary_key=True)
+ org_id = Column(db.Integer, db.ForeignKey("organizations.id"))
+ table_id = Column(
+ db.Integer, db.ForeignKey("table_metadata.id", ondelete="CASCADE"), index=True
+ )
+ name = Column(db.String(300), index=True)
+ type = Column(db.String(255), nullable=True)
+ example = Column(db.String(4096), nullable=True)
+ exists = Column(db.Boolean, default=True, index=True)
+ description = Column(db.String(4096), nullable=True)
+
+ __tablename__ = "column_metadata"
+ __table_args__ = (
+ db.Index("ix_column_metadata_table_id_pkey", "table_id", "id"),
+ db.Index("ix_column_metadata_table_id_exists", "table_id", "exists"),
+ db.Index(
+ "ix_column_metadata_table_id_name_exists", "table_id", "exists", "name"
+ ),
+ )
+
+ def __str__(self):
+ return str(self.name)
+
+ @classmethod
+ def store(cls, table, existing_columns_set, column_data):
+ existing_columns = cls.query.filter(
+ cls.name.in_(existing_columns_set), cls.table_id == table.id,
+ ).all()
+
+ column_names = set()
+ for column in existing_columns:
+ column_names.add(column.name)
+ for name, value in column_data[column.name].items():
+ setattr(column, name, value)
+ db.session.add(column)
+
+ # Find the columns that need to be created by subtracting the sets:
+ for column_name in existing_columns_set.difference(column_names):
+ db.session.add(cls(**column_data[column_name]))
+ db.session.commit()
+
+
+class TableMetadataQueriesLink(db.Model):
+ table_id = Column(
+ db.Integer,
+ db.ForeignKey("table_metadata.id", ondelete="CASCADE"),
+ primary_key=True,
+ )
+ query_id = Column(
+ db.Integer, db.ForeignKey("queries.id", ondelete="CASCADE"), primary_key=True
+ )
+
+ __tablename__ = "tablemetadata_queries_link"
+
+ def __str__(self):
+ return str(self.id)
+
+
@generic_repr("id", "name", "type", "org_id", "created_at")
class DataSource(BelongsToOrgMixin, db.Model):
id = primary_key("DataSource")
@@ -118,19 +308,21 @@ class DataSource(BelongsToOrgMixin, db.Model):
options = Column(
"encrypted_options",
ConfigurationContainer.as_mutable(
- EncryptedConfiguration(db.Text, settings.DATASOURCE_SECRET_KEY, FernetEngine)
+ EncryptedConfiguration(
+ db.Text, settings.DATASOURCE_SECRET_KEY, FernetEngine
+ )
),
)
+ description = Column(db.String(4096), nullable=True)
queue_name = Column(db.String(255), default="queries")
scheduled_queue_name = Column(db.String(255), default="scheduled_queries")
created_at = Column(db.DateTime(True), default=db.func.now())
- data_source_groups = db.relationship("DataSourceGroup", back_populates="data_source", cascade="all")
- __tablename__ = "data_sources"
- __table_args__ = (
- db.Index("data_sources_org_id_name", "org_id", "name"),
- {"extend_existing": True},
+ data_source_groups = db.relationship(
+ "DataSourceGroup", back_populates="data_source", cascade="all"
)
+ __tablename__ = "data_sources"
+ __table_args__ = (db.Index("data_sources_org_id_name", "org_id", "name"),)
def __eq__(self, other):
return self.id == other.id
@@ -143,10 +335,10 @@ def to_dict(self, all=False, with_permissions_for=None):
"id": self.id,
"name": self.name,
"type": self.type,
+ "description": self.description,
"syntax": self.query_runner.syntax,
"paused": self.paused,
"pause_reason": self.pause_reason,
- "supports_auto_limit": self.query_runner.supports_auto_limit,
}
if all:
@@ -175,7 +367,9 @@ def __str__(self):
@classmethod
def create_with_group(cls, *args, **kwargs):
data_source = cls(*args, **kwargs)
- data_source_group = DataSourceGroup(data_source=data_source, group=data_source.org.default_group)
+ data_source_group = DataSourceGroup(
+ data_source=data_source, group=data_source.org.default_group
+ )
db.session.add_all([data_source, data_source_group])
return data_source
@@ -184,7 +378,9 @@ def all(cls, org, group_ids=None):
data_sources = cls.query.filter(cls.org == org).order_by(cls.id.asc())
if group_ids:
- data_sources = data_sources.join(DataSourceGroup).filter(DataSourceGroup.group_id.in_(group_ids))
+ data_sources = data_sources.join(DataSourceGroup).filter(
+ DataSourceGroup.group_id.in_(group_ids)
+ )
return data_sources.distinct()
@@ -192,49 +388,88 @@ def all(cls, org, group_ids=None):
def get_by_id(cls, _id):
return cls.query.filter(cls.id == _id).one()
+ @classmethod
+ def save_schema(cls, schema_info):
+ # There was a change in column data.
+ if "columnId" in schema_info:
+ ColumnMetadata.query.filter(
+ ColumnMetadata.table_id == schema_info["tableId"],
+ ColumnMetadata.id == schema_info["columnId"],
+ ).update(schema_info["schema"])
+ db.session.commit()
+ return
+
+ sample_queries = schema_info["schema"].pop("sample_queries", None)
+ if sample_queries is not None:
+ table_metadata_object = TableMetadata.query.filter(
+ TableMetadata.id == schema_info["tableId"]
+ ).first()
+ table_metadata_object.sample_queries = []
+
+ query_ids = [sample_query["id"] for sample_query in sample_queries.values()]
+ query_objects = Query.query.filter(Query.id.in_(query_ids))
+ table_metadata_object.sample_queries.extend(query_objects)
+ db.session.add(table_metadata_object)
+ db.session.commit()
+
+ TableMetadata.query.filter(TableMetadata.id == schema_info["tableId"]).update(
+ schema_info["schema"]
+ )
+ db.session.commit()
+
def delete(self):
- Query.query.filter(Query.data_source == self).update(dict(data_source_id=None, latest_query_data_id=None))
+ Query.query.filter(Query.data_source == self).update(
+ dict(data_source_id=None, latest_query_data_id=None)
+ )
QueryResult.query.filter(QueryResult.data_source == self).delete()
res = db.session.delete(self)
db.session.commit()
-
- redis_connection.delete(self._schema_key)
-
return res
- def get_cached_schema(self):
- cache = redis_connection.get(self._schema_key)
- return json_loads(cache) if cache else None
-
def get_schema(self, refresh=False):
- out_schema = None
- if not refresh:
- out_schema = self.get_cached_schema()
-
- if out_schema is None:
- query_runner = self.query_runner
- schema = query_runner.get_schema(get_stats=refresh)
-
- try:
- out_schema = self._sort_schema(schema)
- except Exception:
- logging.exception("Error sorting schema columns for data_source {}".format(self.id))
- out_schema = schema
- finally:
- ttl = int(datetime.timedelta(minutes=settings.SCHEMAS_REFRESH_SCHEDULE, days=7).total_seconds())
- redis_connection.set(self._schema_key, json_dumps(out_schema), ex=ttl)
-
- return out_schema
-
- def _sort_schema(self, schema):
- return [
- {"name": i["name"], "columns": sorted(i["columns"], key=lambda x: x["name"] if isinstance(x, dict) else x)}
- for i in sorted(schema, key=lambda x: x["name"])
- ]
+ """
+ Get or set the schema from Redis.
+
+ This will first check for the fresh key and either
+ return the schema value if it's still fresh or
+ repopulate the cache key and return the stale value.
+
+ This will refresh the schema from the data source's API
+ when requested with the refresh parameter, which will also
+ (re)populate the cache.
+ """
+ if refresh:
+ from redash.tasks.queries import refresh_schema
+
+ refresh_schema.delay(self.id)
+
+ # First let's try to find out if there is a cached schema
+ # already and hasn't timed out yet and load it with json.
+ schema = redis_connection.get(self.schema_cache.cache_key)
+ if schema:
+ schema = utils.json_loads(schema)
+ else:
+ # Otherwise we assume the cache key has timed out or was
+ # never populated before.
+ schema = []
+
+ # Now check if there is a fresh key from the last time populating.
+ is_fresh = redis_connection.get(self.schema_cache.fresh_key)
+ if is_fresh:
+ # If the cache value is still fresh, just return it.
+ return schema
+ else:
+ # Otherwise pass the stale value to the populate method
+ # so it can use it as a fallback in case a population
+ # lock is in place already (e.g. another user has already
+ # tried to fetch the schema). If the lock can be created
+ # successfully, it'll actually load the schema using the
+ # load method and set the cache and refresh keys.
+ return self.schema_cache.populate(schema)
@property
- def _schema_key(self):
- return "data_source:schema:{}".format(self.id)
+ def schema_cache(self):
+ return SchemaCache(self)
@property
def _pause_key(self):
@@ -260,18 +495,22 @@ def add_group(self, group, view_only=False):
return dsg
def remove_group(self, group):
- DataSourceGroup.query.filter(DataSourceGroup.group == group, DataSourceGroup.data_source == self).delete()
+ DataSourceGroup.query.filter(
+ DataSourceGroup.group == group, DataSourceGroup.data_source == self
+ ).delete()
db.session.commit()
def update_group_permission(self, group, view_only):
- dsg = DataSourceGroup.query.filter(DataSourceGroup.group == group, DataSourceGroup.data_source == self).one()
+ dsg = DataSourceGroup.query.filter(
+ DataSourceGroup.group == group, DataSourceGroup.data_source == self
+ ).one()
dsg.view_only = view_only
db.session.add(dsg)
return dsg
@property
def uses_ssh_tunnel(self):
- return self.options and "ssh_tunnel" in self.options
+ return "ssh_tunnel" in self.options
@property
def query_runner(self):
@@ -304,11 +543,36 @@ class DataSourceGroup(db.Model):
view_only = Column(db.Boolean, default=False)
__tablename__ = "data_source_groups"
- __table_args__ = ({"extend_existing": True},)
+
+
+DESERIALIZED_DATA_ATTR = "_deserialized_data"
+
+
+class DBPersistence(object):
+ @property
+ def data(self):
+ if self._data is None:
+ return None
+
+ if not hasattr(self, DESERIALIZED_DATA_ATTR):
+ setattr(self, DESERIALIZED_DATA_ATTR, json_loads(self._data))
+
+ return self._deserialized_data
+
+ @data.setter
+ def data(self, data):
+ if hasattr(self, DESERIALIZED_DATA_ATTR):
+ delattr(self, DESERIALIZED_DATA_ATTR)
+ self._data = data
+
+
+QueryResultPersistence = (
+ settings.dynamic_settings.QueryResultPersistence or DBPersistence
+)
@generic_repr("id", "org_id", "data_source_id", "query_hash", "runtime", "retrieved_at")
-class QueryResult(db.Model, BelongsToOrgMixin):
+class QueryResult(db.Model, QueryResultPersistence, BelongsToOrgMixin):
id = primary_key("QueryResult")
org_id = Column(key_type("Organization"), db.ForeignKey("organizations.id"))
org = db.relationship(Organization)
@@ -316,8 +580,8 @@ class QueryResult(db.Model, BelongsToOrgMixin):
data_source = db.relationship(DataSource, backref=backref("query_results"))
query_hash = Column(db.String(32), index=True)
query_text = Column("query", db.Text)
- data = Column(JSONText, nullable=True)
- runtime = Column(DOUBLE_PRECISION)
+ _data = Column("data", db.Text)
+ runtime = Column(postgresql.DOUBLE_PRECISION)
retrieved_at = Column(db.DateTime(True))
__tablename__ = "query_results"
@@ -339,25 +603,27 @@ def to_dict(self):
@classmethod
def unused(cls, days=7):
age_threshold = datetime.datetime.now() - datetime.timedelta(days=days)
- return (cls.query.filter(Query.id.is_(None), cls.retrieved_at < age_threshold).outerjoin(Query)).options(
- load_only("id")
- )
+ return (
+ cls.query.filter(
+ Query.id.is_(None), cls.retrieved_at < age_threshold
+ ).outerjoin(Query)
+ ).options(load_only("id"))
@classmethod
def get_latest(cls, data_source, query, max_age=0):
- query_hash = gen_query_hash(query)
-
- if max_age == -1 and settings.QUERY_RESULTS_EXPIRED_TTL_ENABLED:
- max_age = settings.QUERY_RESULTS_EXPIRED_TTL
+ query_hash = utils.gen_query_hash(query)
if max_age == -1:
- query = cls.query.filter(cls.query_hash == query_hash, cls.data_source == data_source)
+ query = cls.query.filter(
+ cls.query_hash == query_hash, cls.data_source == data_source
+ )
else:
query = cls.query.filter(
cls.query_hash == query_hash,
cls.data_source == data_source,
(
- db.func.timezone("utc", cls.retrieved_at) + datetime.timedelta(seconds=max_age)
+ db.func.timezone("utc", cls.retrieved_at)
+ + datetime.timedelta(seconds=max_age)
>= db.func.timezone("utc", db.func.now())
),
)
@@ -365,7 +631,9 @@ def get_latest(cls, data_source, query, max_age=0):
return query.order_by(cls.retrieved_at.desc()).first()
@classmethod
- def store_result(cls, org, data_source, query_hash, query, data, run_time, retrieved_at):
+ def store_result(
+ cls, org, data_source, query_hash, query, data, run_time, retrieved_at
+ ):
query_result = cls(
org_id=org,
query_hash=query_hash,
@@ -386,11 +654,9 @@ def groups(self):
return self.data_source.groups
-def should_schedule_next(previous_iteration, now, interval, time=None, day_of_week=None, failures=0):
- # if previous_iteration is None, it means the query has never been run before
- # so we should schedule it immediately
- if previous_iteration is None:
- return True
+def should_schedule_next(
+ previous_iteration, now, interval, time=None, day_of_week=None, failures=0
+):
# if time exists then interval > 23 hours (82800s)
# if day_of_week exists then interval > 6 days (518400s)
if time is None:
@@ -404,23 +670,32 @@ def should_schedule_next(previous_iteration, now, interval, time=None, day_of_we
# - The query scheduled to run at 23:59.
# - The scheduler wakes up at 00:01.
# - Using naive implementation of comparing timestamps, it will skip the execution.
- normalized_previous_iteration = previous_iteration.replace(hour=hour, minute=minute)
+ normalized_previous_iteration = previous_iteration.replace(
+ hour=hour, minute=minute
+ )
if normalized_previous_iteration > previous_iteration:
- previous_iteration = normalized_previous_iteration - datetime.timedelta(days=1)
+ previous_iteration = normalized_previous_iteration - datetime.timedelta(
+ days=1
+ )
days_delay = int(interval) / 60 / 60 / 24
days_to_add = 0
if day_of_week is not None:
- days_to_add = list(calendar.day_name).index(day_of_week) - normalized_previous_iteration.weekday()
+ days_to_add = (
+ list(calendar.day_name).index(day_of_week)
+ - normalized_previous_iteration.weekday()
+ )
next_iteration = (
- previous_iteration + datetime.timedelta(days=days_delay) + datetime.timedelta(days=days_to_add)
+ previous_iteration
+ + datetime.timedelta(days=days_delay)
+ + datetime.timedelta(days=days_to_add)
).replace(hour=hour, minute=minute)
if failures:
try:
- next_iteration += datetime.timedelta(minutes=2**failures)
+ next_iteration += datetime.timedelta(minutes=2 ** failures)
except OverflowError:
return False
return now > next_iteration
@@ -449,7 +724,9 @@ class Query(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model):
org = db.relationship(Organization, backref="queries")
data_source_id = Column(key_type("DataSource"), db.ForeignKey("data_sources.id"), nullable=True)
data_source = db.relationship(DataSource, backref="queries")
- latest_query_data_id = Column(key_type("QueryResult"), db.ForeignKey("query_results.id"), nullable=True)
+ latest_query_data_id = Column(
+ key_type("QueryResult"), db.ForeignKey("query_results.id"), nullable=True
+ )
latest_query_data = db.relationship(QueryResult)
name = Column(db.String(255))
description = Column(db.String(4096), nullable=True)
@@ -459,14 +736,16 @@ class Query(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model):
user_id = Column(key_type("User"), db.ForeignKey("users.id"))
user = db.relationship(User, foreign_keys=[user_id])
last_modified_by_id = Column(key_type("User"), db.ForeignKey("users.id"), nullable=True)
- last_modified_by = db.relationship(User, backref="modified_queries", foreign_keys=[last_modified_by_id])
+ last_modified_by = db.relationship(
+ User, backref="modified_queries", foreign_keys=[last_modified_by_id]
+ )
is_archived = Column(db.Boolean, default=False, index=True)
is_draft = Column(db.Boolean, default=True, index=True)
- schedule = Column(MutableDict.as_mutable(JSONB), nullable=True)
- interval = json_cast_property(db.Integer, "schedule", "interval", default=0)
+ schedule = Column(MutableDict.as_mutable(PseudoJSON), nullable=True)
+ interval = pseudo_json_cast_property(db.Integer, "schedule", "interval", default=0)
schedule_failures = Column(db.Integer, default=0)
visualizations = db.relationship("Visualization", cascade="all, delete-orphan")
- options = Column(MutableDict.as_mutable(JSONB), default={})
+ options = Column(MutableDict.as_mutable(PseudoJSON), default={})
search_vector = Column(
TSVectorType(
"id",
@@ -477,7 +756,9 @@ class Query(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model):
),
nullable=True,
)
- tags = Column("tags", MutableList.as_mutable(ARRAY(db.Unicode)), nullable=True)
+ tags = Column(
+ "tags", MutableList.as_mutable(postgresql.ARRAY(db.Unicode)), nullable=True
+ )
query_class = SearchBaseQuery
__tablename__ = "queries"
@@ -513,33 +794,43 @@ def create(cls, **kwargs):
name="Table",
description="",
type="TABLE",
- options={},
+ options="{}",
)
)
return query
@classmethod
- def all_queries(cls, group_ids, user_id=None, include_drafts=False, include_archived=False):
+ def all_queries(
+ cls, group_ids, user_id=None, include_drafts=False, include_archived=False
+ ):
query_ids = (
db.session.query(distinct(cls.id))
- .join(DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id)
+ .join(
+ DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id
+ )
.filter(Query.is_archived.is_(include_archived))
.filter(DataSourceGroup.group_id.in_(group_ids))
)
queries = (
cls.query.options(
joinedload(Query.user),
- joinedload(Query.latest_query_data).load_only("runtime", "retrieved_at"),
+ joinedload(Query.latest_query_data).load_only(
+ "runtime", "retrieved_at"
+ ),
)
.filter(cls.id.in_(query_ids))
# Adding outer joins to be able to order by relationship
.outerjoin(User, User.id == Query.user_id)
.outerjoin(QueryResult, QueryResult.id == Query.latest_query_data_id)
- .options(contains_eager(Query.user), contains_eager(Query.latest_query_data))
+ .options(
+ contains_eager(Query.user), contains_eager(Query.latest_query_data)
+ )
)
if not include_drafts:
- queries = queries.filter(or_(Query.is_draft.is_(False), Query.user_id == user_id))
+ queries = queries.filter(
+ or_(Query.is_draft.is_(False), Query.user_id == user_id)
+ )
return queries
@classmethod
@@ -555,7 +846,9 @@ def favorites(cls, user, base_query=None):
@classmethod
def all_tags(cls, user, include_drafts=False):
- queries = cls.all_queries(group_ids=user.group_ids, user_id=user.id, include_drafts=include_drafts)
+ queries = cls.all_queries(
+ group_ids=user.group_ids, user_id=user.id, include_drafts=include_drafts
+ )
tag_column = func.unnest(cls.tags).label("tag")
usage_count = func.count(1).label("usage_count")
@@ -579,22 +872,25 @@ def by_api_key(cls, api_key):
@classmethod
def past_scheduled_queries(cls):
now = utils.utcnow()
- queries = Query.query.filter(func.jsonb_typeof(Query.schedule) != "null").order_by(Query.id)
+ queries = Query.query.filter(Query.schedule.isnot(None)).order_by(Query.id)
return [
query
for query in queries
- if "until" in query.schedule
- and query.schedule["until"] is not None
- and pytz.utc.localize(datetime.datetime.strptime(query.schedule["until"], "%Y-%m-%d")) <= now
+ if query.schedule["until"] is not None
+ and pytz.utc.localize(
+ datetime.datetime.strptime(query.schedule["until"], "%Y-%m-%d")
+ )
+ <= now
]
@classmethod
def outdated_queries(cls):
queries = (
- Query.query.options(joinedload(Query.latest_query_data).load_only("retrieved_at"))
- .filter(func.jsonb_typeof(Query.schedule) != "null")
+ Query.query.options(
+ joinedload(Query.latest_query_data).load_only("retrieved_at")
+ )
+ .filter(Query.schedule.isnot(None))
.order_by(Query.id)
- .all()
)
now = utils.utcnow()
@@ -606,13 +902,10 @@ def outdated_queries(cls):
if query.schedule.get("disabled"):
continue
- # Skip queries that have None for all schedule values. It's unclear whether this
- # something that can happen in practice, but we have a test case for it.
- if all(value is None for value in query.schedule.values()):
- continue
-
if query.schedule["until"]:
- schedule_until = pytz.utc.localize(datetime.datetime.strptime(query.schedule["until"], "%Y-%m-%d"))
+ schedule_until = pytz.utc.localize(
+ datetime.datetime.strptime(query.schedule["until"], "%Y-%m-%d")
+ )
if schedule_until <= now:
continue
@@ -622,7 +915,7 @@ def outdated_queries(cls):
)
if should_schedule_next(
- retrieved_at,
+ retrieved_at or now,
now,
query.schedule["interval"],
query.schedule["time"],
@@ -640,7 +933,9 @@ def outdated_queries(cls):
% (query.id, repr(e))
)
logging.info(message)
- sentry.capture_exception(type(e)(message).with_traceback(e.__traceback__))
+ sentry.capture_exception(
+ type(e)(message).with_traceback(e.__traceback__)
+ )
return list(outdated_queries.values())
@@ -666,7 +961,9 @@ def search(
# Since tsvector doesn't work well with CJK languages, use `ilike` too
pattern = "%{}%".format(term)
return (
- all_queries.filter(or_(cls.name.ilike(pattern), cls.description.ilike(pattern)))
+ all_queries.filter(
+ or_(cls.name.ilike(pattern), cls.description.ilike(pattern))
+ )
.order_by(Query.id)
.limit(limit)
)
@@ -675,17 +972,7 @@ def search(
return all_queries.search(term, sort=True).limit(limit)
@classmethod
- def search_by_user(cls, term, user, limit=None, multi_byte_search=False):
- if multi_byte_search:
- # Since tsvector doesn't work well with CJK languages, use `ilike` too
- pattern = "%{}%".format(term)
- return (
- cls.by_user(user)
- .filter(or_(cls.name.ilike(pattern), cls.description.ilike(pattern)))
- .order_by(Query.id)
- .limit(limit)
- )
-
+ def search_by_user(cls, term, user, limit=None):
return cls.by_user(user).search(term, sort=True).limit(limit)
@classmethod
@@ -693,14 +980,18 @@ def recent(cls, group_ids, user_id=None, limit=20):
query = (
cls.query.filter(Event.created_at > (db.func.current_date() - 7))
.join(Event, Query.id == Event.object_id.cast(db.Integer))
- .join(DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id)
+ .join(
+ DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id
+ )
.filter(
- Event.action.in_(["edit", "execute", "edit_name", "edit_description", "view_source"]),
- Event.object_id is not None,
+ Event.action.in_(
+ ["edit", "execute", "edit_name", "edit_description", "view_source"]
+ ),
+ Event.object_id != None,
Event.object_type == "query",
DataSourceGroup.group_id.in_(group_ids),
- or_(Query.is_draft.is_(False), Query.user_id is user_id),
- Query.is_archived.is_(False),
+ or_(Query.is_draft == False, Query.user_id == user_id),
+ Query.is_archived == False,
)
.group_by(Event.object_id, Query.id)
.order_by(db.desc(db.func.count(0)))
@@ -732,7 +1023,6 @@ def update_latest_result(cls, query_result):
queries = Query.query.filter(
Query.query_hash == query_result.query_hash,
Query.data_source == query_result.data_source,
- Query.is_archived.is_(False),
)
for q in queries:
@@ -764,12 +1054,16 @@ def fork(self, user):
kwargs = {a: getattr(self, a) for a in forked_list}
# Query.create will add default TABLE visualization, so use constructor to create bare copy of query
- forked_query = Query(name="Copy of (#{}) {}".format(self.id, self.name), user=user, **kwargs)
+ forked_query = Query(
+ name="Copy of (#{}) {}".format(self.id, self.name), user=user, **kwargs
+ )
for v in sorted(self.visualizations, key=lambda v: v.id):
forked_v = v.copy()
forked_v["query_rel"] = forked_query
- fv = Visualization(**forked_v) # it will magically add it to `forked_query.visualizations`
+ fv = Visualization(
+ **forked_v
+ ) # it will magically add it to `forked_query.visualizations`
db.session.add(fv)
db.session.add(forked_query)
@@ -822,29 +1116,11 @@ def dashboard_api_keys(self):
api_keys = db.session.execute(query, {"id": self.id}).fetchall()
return [api_key[0] for api_key in api_keys]
- def update_query_hash(self):
- should_apply_auto_limit = self.options.get("apply_auto_limit", False) if self.options else False
- query_runner = self.data_source.query_runner if self.data_source else BaseQueryRunner({})
- query_text = self.query_text
-
- parameters_dict = {p["name"]: p.get("value") for p in self.parameters} if self.options else {}
- if any(parameters_dict):
- try:
- query_text = self.parameterized.apply(parameters_dict).query
- except InvalidParameterError as e:
- logging.info(f"Unable to update hash for query {self.id} because of invalid parameters: {str(e)}")
- except QueryDetachedFromDataSourceError as e:
- logging.info(
- f"Unable to update hash for query {self.id} because of dropdown query {e.query_id} is unattached from datasource"
- )
-
- self.query_hash = query_runner.gen_query_hash(query_text, should_apply_auto_limit)
-
-@listens_for(Query, "before_insert")
-@listens_for(Query, "before_update")
-def receive_before_insert_update(mapper, connection, target):
- target.update_query_hash()
+@listens_for(Query.query_text, "set")
+def gen_query_hash(target, val, oldval, initiator):
+ target.query_hash = utils.gen_query_hash(val)
+ target.schedule_failures = 0
@listens_for(Query.user_id, "set")
@@ -865,7 +1141,9 @@ class Favorite(TimestampMixin, db.Model):
user = db.relationship(User, backref="favorites")
__tablename__ = "favorites"
- __table_args__ = (UniqueConstraint("object_type", "object_id", "user_id", name="unique_favorite"),)
+ __table_args__ = (
+ UniqueConstraint("object_type", "object_id", "user_id", name="unique_favorite"),
+ )
@classmethod
def is_favorite(cls, user, object):
@@ -908,7 +1186,6 @@ def next_state(op, value, threshold):
# boolean value is Python specific and most likely will be confusing to
# users.
value = str(value).lower()
- value_is_number = False
else:
try:
value = float(value)
@@ -926,20 +1203,19 @@ def next_state(op, value, threshold):
if op(value, threshold):
new_state = Alert.TRIGGERED_STATE
- elif not value_is_number and op not in [OPERATORS.get("!="), OPERATORS.get("=="), OPERATORS.get("equals")]:
- new_state = Alert.UNKNOWN_STATE
else:
new_state = Alert.OK_STATE
return new_state
-@generic_repr("id", "name", "query_id", "user_id", "state", "last_triggered_at", "rearm")
+@generic_repr(
+ "id", "name", "query_id", "user_id", "state", "last_triggered_at", "rearm"
+)
class Alert(TimestampMixin, BelongsToOrgMixin, db.Model):
UNKNOWN_STATE = "unknown"
OK_STATE = "ok"
TRIGGERED_STATE = "triggered"
- TEST_STATE = "test"
id = primary_key("Alert")
name = Column(db.String(255))
@@ -947,7 +1223,7 @@ class Alert(TimestampMixin, BelongsToOrgMixin, db.Model):
query_rel = db.relationship(Query, backref=backref("alerts", cascade="all"))
user_id = Column(key_type("User"), db.ForeignKey("users.id"))
user = db.relationship(User, backref="alerts")
- options = Column(MutableDict.as_mutable(JSONB), nullable=True)
+ options = Column(MutableDict.as_mutable(PseudoJSON))
state = Column(db.String(255), default=UNKNOWN_STATE)
subscriptions = db.relationship("AlertSubscription", cascade="all, delete-orphan")
last_triggered_at = Column(db.DateTime(True), nullable=True)
@@ -960,7 +1236,9 @@ def all(cls, group_ids):
return (
cls.query.options(joinedload(Alert.user), joinedload(Alert.query_rel))
.join(Query)
- .join(DataSourceGroup, DataSourceGroup.data_source_id == Query.data_source_id)
+ .join(
+ DataSourceGroup, DataSourceGroup.data_source_id == Query.data_source_id
+ )
.filter(DataSourceGroup.group_id.in_(group_ids))
)
@@ -969,43 +1247,24 @@ def get_by_id_and_org(cls, object_id, org):
return super(Alert, cls).get_by_id_and_org(object_id, org, Query)
def evaluate(self):
- data = self.query_rel.latest_query_data.data if self.query_rel.latest_query_data else None
- new_state = self.UNKNOWN_STATE
+ data = self.query_rel.latest_query_data.data
- if data and data["rows"] and self.options["column"] in data["rows"][0]:
+ if data["rows"] and self.options["column"] in data["rows"][0]:
op = OPERATORS.get(self.options["op"], lambda v, t: False)
- if "selector" not in self.options:
- selector = "first"
- else:
- selector = self.options["selector"]
-
- try:
- if selector == "max":
- max_val = float("-inf")
- for i in range(len(data["rows"])):
- max_val = max(max_val, float(data["rows"][i][self.options["column"]]))
- value = max_val
- elif selector == "min":
- min_val = float("inf")
- for i in range(len(data["rows"])):
- min_val = min(min_val, float(data["rows"][i][self.options["column"]]))
- value = min_val
- else:
- value = data["rows"][0][self.options["column"]]
-
- except ValueError:
- return self.UNKNOWN_STATE
-
+ value = data["rows"][0][self.options["column"]]
threshold = self.options["value"]
- if value is not None:
- new_state = next_state(op, value, threshold)
+ new_state = next_state(op, value, threshold)
+ else:
+ new_state = self.UNKNOWN_STATE
return new_state
def subscribers(self):
- return User.query.join(AlertSubscription).filter(AlertSubscription.alert == self)
+ return User.query.join(AlertSubscription).filter(
+ AlertSubscription.alert == self
+ )
def render_template(self, template):
if template is None:
@@ -1020,24 +1279,21 @@ def render_template(self, template):
else:
result_value = None
- result_table = [] # A two-dimensional array which can rendered as a table in Mustache
- for row in data["rows"]:
- result_table.append([row[col["name"]] for col in data["columns"]])
context = {
"ALERT_NAME": self.name,
"ALERT_URL": "{host}/alerts/{alert_id}".format(host=host, alert_id=self.id),
"ALERT_STATUS": self.state.upper(),
- "ALERT_SELECTOR": self.options["selector"],
"ALERT_CONDITION": self.options["op"],
"ALERT_THRESHOLD": self.options["value"],
"QUERY_NAME": self.query_rel.name,
- "QUERY_URL": "{host}/queries/{query_id}".format(host=host, query_id=self.query_rel.id),
+ "QUERY_URL": "{host}/queries/{query_id}".format(
+ host=host, query_id=self.query_rel.id
+ ),
"QUERY_RESULT_VALUE": result_value,
"QUERY_RESULT_ROWS": data["rows"],
"QUERY_RESULT_COLS": data["columns"],
- "QUERY_RESULT_TABLE": result_table,
}
- return mustache_render_escape(template, context)
+ return mustache_render(template, context)
@property
def custom_body(self):
@@ -1068,7 +1324,9 @@ def generate_slug(ctx):
@gfk_type
-@generic_repr("id", "name", "slug", "user_id", "org_id", "version", "is_archived", "is_draft")
+@generic_repr(
+ "id", "name", "slug", "user_id", "org_id", "version", "is_archived", "is_draft"
+)
class Dashboard(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model):
id = primary_key("Dashboard")
version = Column(db.Integer)
@@ -1079,13 +1337,14 @@ class Dashboard(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model
user_id = Column(key_type("User"), db.ForeignKey("users.id"))
user = db.relationship(User)
# layout is no longer used, but kept so we know how to render old dashboards.
- layout = Column(MutableList.as_mutable(JSONB), default=[])
+ layout = Column(db.Text)
dashboard_filters_enabled = Column(db.Boolean, default=False)
is_archived = Column(db.Boolean, default=False, index=True)
is_draft = Column(db.Boolean, default=True, index=True)
widgets = db.relationship("Widget", backref="dashboard", lazy="dynamic")
- tags = Column("tags", MutableList.as_mutable(ARRAY(db.Unicode)), nullable=True)
- options = Column(MutableDict.as_mutable(JSONB), default={})
+ tags = Column(
+ "tags", MutableList.as_mutable(postgresql.ARRAY(db.Unicode)), nullable=True
+ )
__tablename__ = "dashboards"
__mapper_args__ = {"version_id_col": version}
@@ -1093,38 +1352,43 @@ class Dashboard(ChangeTrackingMixin, TimestampMixin, BelongsToOrgMixin, db.Model
def __str__(self):
return "%s=%s" % (self.id, self.name)
- @property
- def name_as_slug(self):
- return utils.slugify(self.name)
-
@classmethod
def all(cls, org, group_ids, user_id):
query = (
- Dashboard.query.options(joinedload(Dashboard.user).load_only("id", "name", "details", "email"))
- .distinct(cls.lowercase_name, Dashboard.created_at, Dashboard.slug)
+ Dashboard.query.options(
+ joinedload(Dashboard.user).load_only(
+ "id", "name", "_profile_image_url", "email"
+ )
+ )
.outerjoin(Widget)
.outerjoin(Visualization)
.outerjoin(Query)
- .outerjoin(DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id)
+ .outerjoin(
+ DataSourceGroup, Query.data_source_id == DataSourceGroup.data_source_id
+ )
.filter(
- Dashboard.is_archived.is_(False),
- (DataSourceGroup.group_id.in_(group_ids) | (Dashboard.user_id == user_id)),
+ Dashboard.is_archived == False,
+ (
+ DataSourceGroup.group_id.in_(group_ids)
+ | (Dashboard.user_id == user_id)
+ ),
Dashboard.org == org,
)
+ .distinct()
)
- query = query.filter(or_(Dashboard.user_id == user_id, Dashboard.is_draft.is_(False)))
+ query = query.filter(
+ or_(Dashboard.user_id == user_id, Dashboard.is_draft == False)
+ )
return query
@classmethod
def search(cls, org, groups_ids, user_id, search_term):
# TODO: switch to FTS
- return cls.all(org, groups_ids, user_id).filter(cls.name.ilike("%{}%".format(search_term)))
-
- @classmethod
- def search_by_user(cls, term, user, limit=None):
- return cls.by_user(user).filter(cls.name.ilike("%{}%".format(term))).limit(limit)
+ return cls.all(org, groups_ids, user_id).filter(
+ cls.name.ilike("%{}%".format(search_term))
+ )
@classmethod
def all_tags(cls, org, user):
@@ -1155,29 +1419,10 @@ def favorites(cls, user, base_query=None):
)
).filter(Favorite.user_id == user.id)
- @classmethod
- def by_user(cls, user):
- return cls.all(user.org, user.group_ids, user.id).filter(Dashboard.user == user)
-
@classmethod
def get_by_slug_and_org(cls, slug, org):
return cls.query.filter(cls.slug == slug, cls.org == org).one()
- def fork(self, user):
- forked_list = ["org", "layout", "dashboard_filters_enabled", "tags"]
-
- kwargs = {a: getattr(self, a) for a in forked_list}
- forked_dashboard = Dashboard(name="Copy of (#{}) {}".format(self.id, self.name), user=user, **kwargs)
-
- for w in self.widgets:
- forked_w = w.copy(forked_dashboard.id)
- fw = Widget(**forked_w)
- db.session.add(fw)
-
- forked_dashboard.slug = forked_dashboard.id
- db.session.add(forked_dashboard)
- return forked_dashboard
-
@hybrid_property
def lowercase_name(self):
"Optional property useful for sorting purposes."
@@ -1198,7 +1443,7 @@ class Visualization(TimestampMixin, BelongsToOrgMixin, db.Model):
query_rel = db.relationship(Query, back_populates="visualizations")
name = Column(db.String(255))
description = Column(db.String(4096), nullable=True)
- options = Column(MutableDict.as_mutable(JSONB), nullable=True)
+ options = Column(db.Text)
__tablename__ = "visualizations"
@@ -1221,11 +1466,15 @@ def copy(self):
@generic_repr("id", "visualization_id", "dashboard_id")
class Widget(TimestampMixin, BelongsToOrgMixin, db.Model):
id = primary_key("Widget")
- visualization_id = Column(key_type("Visualization"), db.ForeignKey("visualizations.id"), nullable=True)
- visualization = db.relationship(Visualization, backref=backref("widgets", cascade="delete"))
+ visualization_id = Column(
+ key_type("Visualization"), db.ForeignKey("visualizations.id"), nullable=True
+ )
+ visualization = db.relationship(
+ Visualization, backref=backref("widgets", cascade="delete")
+ )
text = Column(db.Text, nullable=True)
width = Column(db.Integer)
- options = Column(MutableDict.as_mutable(JSONB), default={})
+ options = Column(db.Text)
dashboard_id = Column(key_type("Dashboard"), db.ForeignKey("dashboards.id"), index=True)
__tablename__ = "widgets"
@@ -1237,17 +1486,10 @@ def __str__(self):
def get_by_id_and_org(cls, object_id, org):
return super(Widget, cls).get_by_id_and_org(object_id, org, Dashboard)
- def copy(self, dashboard_id):
- return {
- "options": self.options,
- "width": self.width,
- "text": self.text,
- "visualization_id": self.visualization_id,
- "dashboard_id": dashboard_id,
- }
-
-@generic_repr("id", "object_type", "object_id", "action", "user_id", "org_id", "created_at")
+@generic_repr(
+ "id", "object_type", "object_id", "action", "user_id", "org_id", "created_at"
+)
class Event(db.Model):
id = primary_key("Event")
org_id = Column(key_type("Organization"), db.ForeignKey("organizations.id"))
@@ -1257,7 +1499,9 @@ class Event(db.Model):
action = Column(db.String(255))
object_type = Column(db.String(255))
object_id = Column(db.String(255), nullable=True)
- additional_properties = Column(MutableDict.as_mutable(JSONB), nullable=True, default={})
+ additional_properties = Column(
+ MutableDict.as_mutable(PseudoJSON), nullable=True, default={}
+ )
created_at = Column(db.DateTime(True), default=db.func.now())
__tablename__ = "events"
@@ -1312,23 +1556,24 @@ class ApiKey(TimestampMixin, GFKBase, db.Model):
api_key = Column(db.String(255), index=True, default=lambda: generate_token(40))
active = Column(db.Boolean, default=True)
# 'object' provided by GFKBase
- object_id = Column(key_type("ApiKey"))
created_by_id = Column(key_type("User"), db.ForeignKey("users.id"), nullable=True)
created_by = db.relationship(User)
__tablename__ = "api_keys"
- __table_args__ = (db.Index("api_keys_object_type_object_id", "object_type", "object_id"),)
+ __table_args__ = (
+ db.Index("api_keys_object_type_object_id", "object_type", "object_id"),
+ )
@classmethod
def get_by_api_key(cls, api_key):
- return cls.query.filter(cls.api_key == api_key, cls.active.is_(True)).one()
+ return cls.query.filter(cls.api_key == api_key, cls.active == True).one()
@classmethod
def get_by_object(cls, object):
return cls.query.filter(
cls.object_type == object.__class__.__tablename__,
cls.object_id == object.id,
- cls.active.is_(True),
+ cls.active == True,
).first()
@classmethod
@@ -1347,16 +1592,15 @@ class NotificationDestination(BelongsToOrgMixin, db.Model):
user = db.relationship(User, backref="notification_destinations")
name = Column(db.String(255))
type = Column(db.String(255))
- options = Column(
- "encrypted_options",
- ConfigurationContainer.as_mutable(
- EncryptedConfiguration(db.Text, settings.DATASOURCE_SECRET_KEY, FernetEngine)
- ),
- )
+ options = Column(ConfigurationContainer.as_mutable(Configuration))
created_at = Column(db.DateTime(True), default=db.func.now())
__tablename__ = "notification_destinations"
- __table_args__ = (db.Index("notification_destinations_org_id_name", "org_id", "name", unique=True),)
+ __table_args__ = (
+ db.Index(
+ "notification_destinations_org_id_name", "org_id", "name", unique=True
+ ),
+ )
def __str__(self):
return str(self.name)
@@ -1382,14 +1626,18 @@ def destination(self):
@classmethod
def all(cls, org):
- notification_destinations = cls.query.filter(cls.org == org).order_by(cls.id.asc())
+ notification_destinations = cls.query.filter(cls.org == org).order_by(
+ cls.id.asc()
+ )
return notification_destinations
- def notify(self, alert, query, user, new_state, app, host, metadata):
+ def notify(self, alert, query, user, new_state, app, host):
schema = get_configuration_schema_for_destination_type(self.type)
self.options.set_schema(schema)
- return self.destination.notify(alert, query, user, new_state, app, host, metadata, self.options)
+ return self.destination.notify(
+ alert, query, user, new_state, app, host, self.options
+ )
@generic_repr("id", "user_id", "destination_id", "alert_id")
@@ -1424,18 +1672,20 @@ def to_dict(self):
@classmethod
def all(cls, alert_id):
- return AlertSubscription.query.join(User).filter(AlertSubscription.alert_id == alert_id)
+ return AlertSubscription.query.join(User).filter(
+ AlertSubscription.alert_id == alert_id
+ )
- def notify(self, alert, query, user, new_state, app, host, metadata):
+ def notify(self, alert, query, user, new_state, app, host):
if self.destination:
- return self.destination.notify(alert, query, user, new_state, app, host, metadata)
+ return self.destination.notify(alert, query, user, new_state, app, host)
else:
# User email subscription, so create an email destination object
config = {"addresses": self.user.email}
schema = get_configuration_schema_for_destination_type("email")
options = ConfigurationContainer(config, schema)
destination = get_destination("email", options)
- return destination.notify(alert, query, user, new_state, app, host, metadata, options)
+ return destination.notify(alert, query, user, new_state, app, host, options)
@generic_repr("id", "trigger", "user_id", "org_id")
@@ -1473,7 +1723,7 @@ def init_db():
default_org = Organization(name="Default", slug="default", settings={})
admin_group = Group(
name="admin",
- permissions=Group.ADMIN_PERMISSIONS,
+ permissions=["admin", "super_admin"],
org=default_org,
type=Group.BUILTIN_GROUP,
)
diff --git a/redash/models/base.py b/redash/models/base.py
index 2ed95c38fb..027d906670 100644
--- a/redash/models/base.py
+++ b/redash/models/base.py
@@ -1,13 +1,12 @@
import functools
from flask_sqlalchemy import BaseQuery, SQLAlchemy
-from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import object_session
from sqlalchemy.pool import NullPool
-from sqlalchemy_searchable import SearchQueryMixin, make_searchable, vectorizer
+from sqlalchemy_searchable import make_searchable, vectorizer, SearchQueryMixin
from redash import settings
-from redash.utils import json_dumps, json_loads
+from redash.utils import json_dumps
class RedashSQLAlchemy(SQLAlchemy):
@@ -15,7 +14,7 @@ def apply_driver_hacks(self, app, info, options):
options.update(json_serializer=json_dumps)
if settings.SQLALCHEMY_ENABLE_POOL_PRE_PING:
options.update(pool_pre_ping=True)
- return super(RedashSQLAlchemy, self).apply_driver_hacks(app, info, options)
+ super(RedashSQLAlchemy, self).apply_driver_hacks(app, info, options)
def apply_pool_defaults(self, app, options):
super(RedashSQLAlchemy, self).apply_pool_defaults(app, options)
@@ -25,13 +24,9 @@ def apply_pool_defaults(self, app, options):
options["poolclass"] = NullPool
# Remove options NullPool does not support:
options.pop("max_overflow", None)
- return options
-db = RedashSQLAlchemy(
- session_options={"expire_on_commit": False},
- engine_options={"json_serializer": json_dumps, "json_deserializer": json_loads},
-)
+db = RedashSQLAlchemy(session_options={"expire_on_commit": False})
# Make sure the SQLAlchemy mappers are all properly configured first.
# This is required by SQLAlchemy-Searchable as it adds DDL listeners
# on the configuration phase of models.
@@ -39,7 +34,7 @@ def apply_pool_defaults(self, app, options):
# listen to a few database events to set up functions, trigger updates
# and indexes for the full text search
-make_searchable(db.metadata, options={"regconfig": "pg_catalog.simple"})
+make_searchable(options={"regconfig": "pg_catalog.simple"})
class SearchBaseQuery(BaseQuery, SearchQueryMixin):
@@ -53,11 +48,6 @@ def integer_vectorizer(column):
return db.func.cast(column, db.Text)
-@vectorizer(UUID)
-def uuid_vectorizer(column):
- return db.func.cast(column, db.Text)
-
-
Column = functools.partial(db.Column, nullable=False)
# AccessPermission and Change use a 'generic foreign key' approach to refer to
@@ -71,7 +61,7 @@ def gfk_type(cls):
return cls
-class GFKBase:
+class GFKBase(object):
"""
Compatibility with 'generic foreign key' approach Peewee used.
"""
@@ -88,7 +78,11 @@ def object(self):
return self._object
else:
object_class = _gfk_types[self.object_type]
- self._object = session.query(object_class).filter(object_class.id == self.object_id).first()
+ self._object = (
+ session.query(object_class)
+ .filter(object_class.id == self.object_id)
+ .first()
+ )
return self._object
@object.setter
diff --git a/redash/models/changes.py b/redash/models/changes.py
index 3858f91415..c0d437ee7b 100644
--- a/redash/models/changes.py
+++ b/redash/models/changes.py
@@ -1,19 +1,18 @@
-from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.inspection import inspect
from sqlalchemy_utils.models import generic_repr
-from .base import Column, GFKBase, db, key_type, primary_key
+from .base import GFKBase, db, Column, primary_key, key_type
+from .types import PseudoJSON
@generic_repr("id", "object_type", "object_id", "created_at")
class Change(GFKBase, db.Model):
id = primary_key("Change")
# 'object' defined in GFKBase
- object_id = Column(key_type("Change"))
object_version = Column(db.Integer, default=0)
user_id = Column(key_type("User"), db.ForeignKey("users.id"))
user = db.relationship("User", backref="changes")
- change = Column(JSONB)
+ change = Column(PseudoJSON)
created_at = Column(db.DateTime(True), default=db.func.now())
__tablename__ = "changes"
@@ -39,13 +38,15 @@ def to_dict(self, full=True):
@classmethod
def last_change(cls, obj):
return (
- cls.query.filter(cls.object_id == obj.id, cls.object_type == obj.__class__.__tablename__)
+ cls.query.filter(
+ cls.object_id == obj.id, cls.object_type == obj.__class__.__tablename__
+ )
.order_by(cls.object_version.desc())
.first()
)
-class ChangeTrackingMixin:
+class ChangeTrackingMixin(object):
skipped_fields = ("id", "created_at", "updated_at", "version")
_clean_values = None
diff --git a/redash/models/mixins.py b/redash/models/mixins.py
index e721554906..9116fe46de 100644
--- a/redash/models/mixins.py
+++ b/redash/models/mixins.py
@@ -1,9 +1,9 @@
from sqlalchemy.event import listens_for
-from .base import Column, db
+from .base import db, Column
-class TimestampMixin:
+class TimestampMixin(object):
updated_at = Column(db.DateTime(True), default=db.func.now(), nullable=False)
created_at = Column(db.DateTime(True), default=db.func.now(), nullable=False)
@@ -17,7 +17,7 @@ def timestamp_before_update(mapper, connection, target):
target.updated_at = db.func.now()
-class BelongsToOrgMixin:
+class BelongsToOrgMixin(object):
@classmethod
def get_by_id_and_org(cls, object_id, org, org_cls=None):
query = cls.query.filter(cls.id == object_id)
diff --git a/redash/models/organizations.py b/redash/models/organizations.py
index 0bf57499d6..18dd9f1898 100644
--- a/redash/models/organizations.py
+++ b/redash/models/organizations.py
@@ -1,13 +1,12 @@
-from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm.attributes import flag_modified
from sqlalchemy_utils.models import generic_repr
from redash.settings.organization import settings as org_settings
-from .base import Column, db, primary_key
+from .base import db, Column, primary_key
from .mixins import TimestampMixin
-from .types import MutableDict
-from .users import Group, User
+from .types import MutableDict, PseudoJSON
+from .users import User, Group
@generic_repr("id", "name", "slug")
@@ -18,7 +17,7 @@ class Organization(TimestampMixin, db.Model):
id = primary_key("Organization")
name = Column(db.String(255))
slug = Column(db.String(255), unique=True)
- settings = Column(MutableDict.as_mutable(JSONB), default={})
+ settings = Column(MutableDict.as_mutable(PseudoJSON))
groups = db.relationship("Group", lazy="dynamic")
events = db.relationship("Event", lazy="dynamic", order_by="desc(Event.created_at)")
@@ -37,7 +36,9 @@ def get_by_id(cls, _id):
@property
def default_group(self):
- return self.groups.filter(Group.name == "default", Group.type == Group.BUILTIN_GROUP).first()
+ return self.groups.filter(
+ Group.name == "default", Group.type == Group.BUILTIN_GROUP
+ ).first()
@property
def google_apps_domains(self):
@@ -79,7 +80,9 @@ def get_setting(self, key, raise_on_missing=True):
@property
def admin_group(self):
- return self.groups.filter(Group.name == "admin", Group.type == Group.BUILTIN_GROUP).first()
+ return self.groups.filter(
+ Group.name == "admin", Group.type == Group.BUILTIN_GROUP
+ ).first()
def has_user(self, email):
return self.users.filter(User.email == email).count() == 1
diff --git a/redash/models/parameterized_query.py b/redash/models/parameterized_query.py
index 6799296675..0094c0aa7d 100644
--- a/redash/models/parameterized_query.py
+++ b/redash/models/parameterized_query.py
@@ -1,12 +1,10 @@
-import re
+import pystache
from functools import partial
from numbers import Number
-
-import pystache
-from dateutil.parser import parse
+from redash.utils import mustache_render, json_loads
+from redash.permissions import require_access, view_only
from funcy import distinct
-
-from redash.utils import mustache_render
+from dateutil.parser import parse
def _pluck_name_and_value(default_column, row):
@@ -23,7 +21,9 @@ def _load_result(query_id, org):
query = models.Query.get_by_id_and_org(query_id, org)
if query.data_source:
- query_result = models.QueryResult.get_by_id_and_org(query.latest_query_data_id, org)
+ query_result = models.QueryResult.get_by_id_and_org(
+ query.latest_query_data_id, org
+ )
return query_result.data
else:
raise QueryDetachedFromDataSourceError(query_id)
@@ -38,14 +38,18 @@ def dropdown_values(query_id, org):
def join_parameter_list_values(parameters, schema):
updated_parameters = {}
- for key, value in parameters.items():
+ for (key, value) in parameters.items():
if isinstance(value, list):
- definition = next((definition for definition in schema if definition["name"] == key), {})
+ definition = next(
+ (definition for definition in schema if definition["name"] == key), {}
+ )
multi_values_options = definition.get("multiValuesOptions", {})
separator = str(multi_values_options.get("separator", ","))
prefix = str(multi_values_options.get("prefix", ""))
suffix = str(multi_values_options.get("suffix", ""))
- updated_parameters[key] = separator.join([prefix + v + suffix for v in value])
+ updated_parameters[key] = separator.join(
+ [prefix + v + suffix for v in value]
+ )
else:
updated_parameters[key] = value
return updated_parameters
@@ -85,27 +89,26 @@ def _is_number(string):
if isinstance(string, Number):
return True
else:
- float(string)
- return True
-
-
-def _is_regex_pattern(value, regex):
- try:
- if re.compile(regex).fullmatch(value):
+ try:
+ float(string)
return True
- else:
+ except ValueError:
return False
- except re.error:
- return False
def _is_date(string):
- parse(string)
- return True
+ try:
+ parse(string)
+ return True
+ except (ValueError, TypeError):
+ return False
def _is_date_range(obj):
- return _is_date(obj["start"]) and _is_date(obj["end"])
+ try:
+ return _is_date(obj["start"]) and _is_date(obj["end"])
+ except (KeyError, TypeError):
+ return False
def _is_value_within_options(value, dropdown_options, allow_list=False):
@@ -114,7 +117,7 @@ def _is_value_within_options(value, dropdown_options, allow_list=False):
return str(value) in dropdown_options
-class ParameterizedQuery:
+class ParameterizedQuery(object):
def __init__(self, template, schema=None, org=None):
self.schema = schema or []
self.org = org
@@ -123,12 +126,16 @@ def __init__(self, template, schema=None, org=None):
self.parameters = {}
def apply(self, parameters):
- invalid_parameter_names = [key for (key, value) in parameters.items() if not self._valid(key, value)]
+ invalid_parameter_names = [
+ key for (key, value) in parameters.items() if not self._valid(key, value)
+ ]
if invalid_parameter_names:
raise InvalidParameterError(invalid_parameter_names)
else:
self.parameters.update(parameters)
- self.query = mustache_render(self.template, join_parameter_list_values(parameters, self.schema))
+ self.query = mustache_render(
+ self.template, join_parameter_list_values(parameters, self.schema)
+ )
return self
@@ -146,7 +153,6 @@ def _valid(self, name, value):
enum_options = definition.get("enumOptions")
query_id = definition.get("queryId")
- regex = definition.get("regex")
allow_multiple_values = isinstance(definition.get("multiValuesOptions"), dict)
if isinstance(enum_options, str):
@@ -154,9 +160,10 @@ def _valid(self, name, value):
validators = {
"text": lambda value: isinstance(value, str),
- "text-pattern": lambda value: _is_regex_pattern(value, regex),
"number": _is_number,
- "enum": lambda value: _is_value_within_options(value, enum_options, allow_multiple_values),
+ "enum": lambda value: _is_value_within_options(
+ value, enum_options, allow_multiple_values
+ ),
"query": lambda value: _is_value_within_options(
value,
[v["value"] for v in dropdown_values(query_id, self.org)],
@@ -172,14 +179,7 @@ def _valid(self, name, value):
validate = validators.get(definition["type"], lambda x: False)
- try:
- # multiple error types can be raised here; but we want to convert
- # all except QueryDetached to InvalidParameterError in `apply`
- return validate(value)
- except QueryDetachedFromDataSourceError:
- raise
- except Exception:
- return False
+ return validate(value)
@property
def is_safe(self):
@@ -199,7 +199,9 @@ def text(self):
class InvalidParameterError(Exception):
def __init__(self, parameters):
parameter_names = ", ".join(parameters)
- message = "The following parameter values are incompatible with their definitions: {}".format(parameter_names)
+ message = "The following parameter values are incompatible with their definitions: {}".format(
+ parameter_names
+ )
super(InvalidParameterError, self).__init__(message)
diff --git a/redash/models/types.py b/redash/models/types.py
index b3aa467dcf..dd7873ab06 100644
--- a/redash/models/types.py
+++ b/redash/models/types.py
@@ -1,7 +1,10 @@
+import pytz
+from sqlalchemy.types import TypeDecorator
from sqlalchemy.ext.indexable import index_property
from sqlalchemy.ext.mutable import Mutable
-from sqlalchemy.types import TypeDecorator
from sqlalchemy_utils import EncryptedType
+from sqlalchemy import cast
+from sqlalchemy.dialects.postgresql import JSON
from redash.utils import json_dumps, json_loads
from redash.utils.configuration import ConfigurationContainer
@@ -21,7 +24,9 @@ def process_result_value(self, value, dialect):
class EncryptedConfiguration(EncryptedType):
def process_bind_param(self, value, dialect):
- return super(EncryptedConfiguration, self).process_bind_param(value.to_json(), dialect)
+ return super(EncryptedConfiguration, self).process_bind_param(
+ value.to_json(), dialect
+ )
def process_result_value(self, value, dialect):
return ConfigurationContainer.from_json(
@@ -29,8 +34,8 @@ def process_result_value(self, value, dialect):
)
-# Utilized for cases when JSON size is bigger than JSONB (255MB) or JSON (10MB) limit
-class JSONText(TypeDecorator):
+# XXX replace PseudoJSON and MutableDict with real JSON field
+class PseudoJSON(TypeDecorator):
impl = db.Text
def process_bind_param(self, value, dialect):
@@ -105,3 +110,17 @@ def __init__(self, cast_type, *args, **kwargs):
def expr(self, model):
expr = super(json_cast_property, self).expr(model)
return expr.astext.cast(self.cast_type)
+
+
+class pseudo_json_cast_property(index_property):
+ """
+ A SQLAlchemy index property that is able to cast the
+ entity attribute as the specified cast type. Useful
+ for PseudoJSON colums for easier querying/filtering.
+ """
+ def __init__(self, cast_type, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.cast_type = cast_type
+ def expr(self, model):
+ expr = cast(getattr(model, self.attr_name), JSON)[self.index]
+ return expr.astext.cast(self.cast_type)
diff --git a/redash/models/users.py b/redash/models/users.py
index 16b6f3b905..10c37461c8 100644
--- a/redash/models/users.py
+++ b/redash/models/users.py
@@ -5,19 +5,21 @@
from functools import reduce
from operator import or_
-from flask import current_app, request_started, url_for
-from flask_login import AnonymousUserMixin, UserMixin, current_user
+from flask import current_app as app, url_for, request_started
+from flask_login import current_user, AnonymousUserMixin, UserMixin
from passlib.apps import custom_app_context as pwd_context
-from sqlalchemy.dialects.postgresql import ARRAY, JSONB
+from sqlalchemy.exc import DBAPIError
+from sqlalchemy.dialects import postgresql
+
from sqlalchemy_utils import EmailType
from sqlalchemy_utils.models import generic_repr
from redash import redis_connection
-from redash.utils import dt_from_timestamp, generate_token
+from redash.utils import generate_token, utcnow, dt_from_timestamp
-from .base import Column, GFKBase, db, key_type, primary_key
-from .mixins import BelongsToOrgMixin, TimestampMixin
-from .types import MutableDict, MutableList, json_cast_property
+from .base import db, Column, GFKBase, key_type, primary_key
+from .mixins import TimestampMixin, BelongsToOrgMixin
+from .types import json_cast_property, MutableDict, MutableList
logger = logging.getLogger(__name__)
@@ -60,7 +62,7 @@ def init_app(app):
request_started.connect(update_user_active_at, app)
-class PermissionsCheckMixin:
+class PermissionsCheckMixin(object):
def has_permission(self, permission):
return self.has_permissions((permission,))
@@ -75,31 +77,37 @@ def has_permissions(self, permissions):
@generic_repr("id", "name", "email")
-class User(TimestampMixin, db.Model, BelongsToOrgMixin, UserMixin, PermissionsCheckMixin):
+class User(
+ TimestampMixin, db.Model, BelongsToOrgMixin, UserMixin, PermissionsCheckMixin
+):
id = primary_key("User")
org_id = Column(key_type("Organization"), db.ForeignKey("organizations.id"))
org = db.relationship("Organization", backref=db.backref("users", lazy="dynamic"))
name = Column(db.String(320))
email = Column(EmailType)
+ _profile_image_url = Column("profile_image_url", db.String(320), nullable=True)
password_hash = Column(db.String(128), nullable=True)
group_ids = Column(
- "groups",
- MutableList.as_mutable(ARRAY(key_type("Group"))),
- nullable=True,
+ "groups", MutableList.as_mutable(postgresql.ARRAY(key_type("Group"))), nullable=True
)
api_key = Column(db.String(40), default=lambda: generate_token(40), unique=True)
disabled_at = Column(db.DateTime(True), default=None, nullable=True)
details = Column(
- MutableDict.as_mutable(JSONB),
+ MutableDict.as_mutable(postgresql.JSON),
nullable=True,
server_default="{}",
default={},
)
- active_at = json_cast_property(db.DateTime(True), "details", "active_at", default=None)
- _profile_image_url = json_cast_property(db.Text(), "details", "profile_image_url", default=None)
- is_invitation_pending = json_cast_property(db.Boolean(True), "details", "is_invitation_pending", default=False)
- is_email_verified = json_cast_property(db.Boolean(True), "details", "is_email_verified", default=True)
+ active_at = json_cast_property(
+ db.DateTime(True), "details", "active_at", default=None
+ )
+ is_invitation_pending = json_cast_property(
+ db.Boolean(True), "details", "is_invitation_pending", default=False
+ )
+ is_email_verified = json_cast_property(
+ db.Boolean(True), "details", "is_email_verified", default=True
+ )
__tablename__ = "users"
__table_args__ = (db.Index("users_org_id_email", "org_id", "email", unique=True),)
@@ -128,7 +136,7 @@ def regenerate_api_key(self):
def to_dict(self, with_api_key=False):
profile_image_url = self.profile_image_url
if self.is_disabled:
- assets = current_app.extensions["webpack"]["assets"] or {}
+ assets = app.extensions["webpack"]["assets"] or {}
path = "images/avatar.svg"
profile_image_url = url_for("static", filename=assets.get(path, path))
@@ -157,22 +165,28 @@ def to_dict(self, with_api_key=False):
return d
- @staticmethod
- def is_api_user():
+ def is_api_user(self):
return False
@property
def profile_image_url(self):
- if self._profile_image_url:
+ if self._profile_image_url is not None:
return self._profile_image_url
- email_md5 = hashlib.md5(self.email.lower().encode(), usedforsecurity=False).hexdigest()
+ email_md5 = hashlib.md5(self.email.lower().encode()).hexdigest()
return "https://www.gravatar.com/avatar/{}?s=40&d=identicon".format(email_md5)
@property
def permissions(self):
# TODO: this should be cached.
- return list(itertools.chain(*[g.permissions for g in Group.query.filter(Group.id.in_(self.group_ids))]))
+ return list(
+ itertools.chain(
+ *[
+ g.permissions
+ for g in Group.query.filter(Group.id.in_(self.group_ids))
+ ]
+ )
+ )
@classmethod
def get_by_org(cls, org):
@@ -210,14 +224,16 @@ def pending(cls, base_query, pending):
if pending:
return base_query.filter(cls.is_invitation_pending.is_(True))
else:
- return base_query.filter(cls.is_invitation_pending.isnot(True)) # check for both `false`/`null`
+ return base_query.filter(
+ cls.is_invitation_pending.isnot(True)
+ ) # check for both `false`/`null`
@classmethod
def find_by_email(cls, email):
return cls.query.filter(cls.email == email)
def hash_password(self, password):
- self.password_hash = pwd_context.hash(password)
+ self.password_hash = pwd_context.encrypt(password)
def verify_password(self, password):
return self.password_hash and pwd_context.verify(password, self.password_hash)
@@ -234,13 +250,10 @@ def has_access(self, obj, access_type):
def get_id(self):
identity = hashlib.md5(
- "{},{}".format(self.email, self.password_hash).encode(), usedforsecurity=False
+ "{},{}".format(self.email, self.password_hash).encode()
).hexdigest()
return "{0}-{1}".format(self.id, identity)
- def get_actual_user(self):
- return repr(self) if self.is_api_user() else self.email
-
@generic_repr("id", "name", "type", "org_id")
class Group(db.Model, BelongsToOrgMixin):
@@ -258,18 +271,19 @@ class Group(db.Model, BelongsToOrgMixin):
"list_alerts",
"list_data_sources",
]
- ADMIN_PERMISSIONS = ["admin", "super_admin"]
BUILTIN_GROUP = "builtin"
REGULAR_GROUP = "regular"
id = primary_key("Group")
- data_sources = db.relationship("DataSourceGroup", back_populates="group", cascade="all")
+ data_sources = db.relationship(
+ "DataSourceGroup", back_populates="group", cascade="all"
+ )
org_id = Column(key_type("Organization"), db.ForeignKey("organizations.id"))
org = db.relationship("Organization", back_populates="groups")
type = Column(db.String(255), default=REGULAR_GROUP)
name = Column(db.String(100))
- permissions = Column(ARRAY(db.String(255)), default=DEFAULT_PERMISSIONS)
+ permissions = Column(postgresql.ARRAY(db.String(255)), default=DEFAULT_PERMISSIONS)
created_at = Column(db.DateTime(True), default=db.func.now())
__tablename__ = "groups"
@@ -300,7 +314,9 @@ def find_by_name(cls, org, group_names):
return list(result)
-@generic_repr("id", "object_type", "object_id", "access_type", "grantor_id", "grantee_id")
+@generic_repr(
+ "id", "object_type", "object_id", "access_type", "grantor_id", "grantee_id"
+)
class AccessPermission(GFKBase, db.Model):
id = primary_key("AccessPermission")
# 'object' defined in GFKBase
@@ -349,7 +365,9 @@ def exists(cls, obj, access_type, grantee):
@classmethod
def _query(cls, obj, access_type=None, grantee=None, grantor=None):
- q = cls.query.filter(cls.object_id == obj.id, cls.object_type == obj.__tablename__)
+ q = cls.query.filter(
+ cls.object_id == obj.id, cls.object_type == obj.__tablename__
+ )
if access_type:
q = q.filter(AccessPermission.access_type == access_type)
@@ -379,8 +397,7 @@ class AnonymousUser(AnonymousUserMixin, PermissionsCheckMixin):
def permissions(self):
return []
- @staticmethod
- def is_api_user():
+ def is_api_user(self):
return False
@@ -400,8 +417,7 @@ def __init__(self, api_key, org, groups, name=None):
def __repr__(self):
return "<{}>".format(self.name)
- @staticmethod
- def is_api_user():
+ def is_api_user(self):
return True
@property
@@ -414,9 +430,5 @@ def org_id(self):
def permissions(self):
return ["view_query"]
- @staticmethod
- def has_access(obj, access_type):
+ def has_access(self, obj, access_type):
return False
-
- def get_actual_user(self):
- return repr(self)
diff --git a/redash/monitor.py b/redash/monitor.py
index 4ba3f00956..b6f7054783 100644
--- a/redash/monitor.py
+++ b/redash/monitor.py
@@ -1,11 +1,14 @@
+from __future__ import absolute_import
+import itertools
from funcy import flatten
+from sqlalchemy import union_all
+from redash import redis_connection, rq_redis_connection, __version__, settings
+from redash.models import db, DataSource, Query, QueryResult, Dashboard, Widget
+from redash.utils import json_loads
from rq import Queue, Worker
from rq.job import Job
from rq.registry import StartedJobRegistry
-from redash import __version__, redis_connection, rq_redis_connection, settings
-from redash.models import Dashboard, Query, QueryResult, Widget, db
-
def get_redis_status():
info = redis_connection.info()
@@ -20,14 +23,14 @@ def get_object_counts():
status["queries_count"] = Query.query.count()
if settings.FEATURE_SHOW_QUERY_RESULTS_COUNT:
status["query_results_count"] = QueryResult.query.count()
- status["unused_query_results_count"] = QueryResult.unused(settings.QUERY_RESULTS_CLEANUP_MAX_AGE).count()
+ status["unused_query_results_count"] = QueryResult.unused().count()
status["dashboards_count"] = Dashboard.query.count()
status["widgets_count"] = Widget.query.count()
return status
def get_queues_status():
- return {queue.name: {"size": len(queue)} for queue in Queue.all(connection=rq_redis_connection)}
+ return {queue.name: {"size": len(queue)} for queue in Queue.all()}
def get_db_sizes():
@@ -59,7 +62,7 @@ def get_status():
def rq_job_ids():
- queues = Queue.all(connection=rq_redis_connection)
+ queues = Queue.all(connection=redis_connection)
started_jobs = [StartedJobRegistry(queue=q).get_job_ids() for q in queues]
queued_jobs = [q.job_ids for q in queues]
diff --git a/redash/permissions.py b/redash/permissions.py
index cca017d19a..92c34dc157 100644
--- a/redash/permissions.py
+++ b/redash/permissions.py
@@ -54,7 +54,7 @@ def require_access(obj, user, need_view_only):
abort(403)
-class require_permissions:
+class require_permissions(object):
def __init__(self, permissions, allow_one=False):
self.permissions = permissions
self.allow_one = allow_one
@@ -92,7 +92,9 @@ def require_super_admin(fn):
def has_permission_or_owner(permission, object_owner_id):
- return int(object_owner_id) == current_user.id or current_user.has_permission(permission)
+ return int(object_owner_id) == current_user.id or current_user.has_permission(
+ permission
+ )
def is_admin_or_owner(object_owner_id):
diff --git a/redash/query_runner/__init__.py b/redash/query_runner/__init__.py
index 10d6d6edf5..4e4cc2cd1f 100644
--- a/redash/query_runner/__init__.py
+++ b/redash/query_runner/__init__.py
@@ -1,19 +1,19 @@
import logging
-from collections import defaultdict
+
from contextlib import ExitStack
+from dateutil import parser
from functools import wraps
+import socket
+import ipaddress
+from urllib.parse import urlparse
-import sqlparse
-from dateutil import parser
-from rq.timeouts import JobTimeoutException
+from six import text_type
from sshtunnel import open_tunnel
+from redash import settings
+from redash.utils import json_loads
+from rq.timeouts import JobTimeoutException
-from redash import settings, utils
-from redash.utils.requests_session import (
- UnacceptableAddressException,
- requests_or_advocate,
- requests_session,
-)
+from redash.utils.requests_session import requests, requests_session
logger = logging.getLogger(__name__)
@@ -44,65 +44,9 @@
TYPE_DATETIME = "datetime"
TYPE_DATE = "date"
-SUPPORTED_COLUMN_TYPES = set([TYPE_INTEGER, TYPE_FLOAT, TYPE_BOOLEAN, TYPE_STRING, TYPE_DATETIME, TYPE_DATE])
-
-
-def split_sql_statements(query):
- def strip_trailing_comments(stmt):
- idx = len(stmt.tokens) - 1
- while idx >= 0:
- tok = stmt.tokens[idx]
- if tok.is_whitespace or sqlparse.utils.imt(tok, i=sqlparse.sql.Comment, t=sqlparse.tokens.Comment):
- stmt.tokens[idx] = sqlparse.sql.Token(sqlparse.tokens.Whitespace, " ")
- else:
- break
- idx -= 1
- return stmt
-
- def strip_trailing_semicolon(stmt):
- idx = len(stmt.tokens) - 1
- while idx >= 0:
- tok = stmt.tokens[idx]
- # we expect that trailing comments already are removed
- if not tok.is_whitespace:
- if sqlparse.utils.imt(tok, t=sqlparse.tokens.Punctuation) and tok.value == ";":
- stmt.tokens[idx] = sqlparse.sql.Token(sqlparse.tokens.Whitespace, " ")
- break
- idx -= 1
- return stmt
-
- def is_empty_statement(stmt):
- # copy statement object. `copy.deepcopy` fails to do this, so just re-parse it
- st = sqlparse.engine.FilterStack()
- st.stmtprocess.append(sqlparse.filters.StripCommentsFilter())
- stmt = next(st.run(str(stmt)), None)
- if stmt is None:
- return True
-
- return str(stmt).strip() == ""
-
- stack = sqlparse.engine.FilterStack()
-
- result = [stmt for stmt in stack.run(query)]
- result = [strip_trailing_comments(stmt) for stmt in result]
- result = [strip_trailing_semicolon(stmt) for stmt in result]
- result = [str(stmt).strip() for stmt in result if not is_empty_statement(stmt)]
-
- if len(result) > 0:
- return result
-
- return [""] # if all statements were empty - return a single empty statement
-
-
-def combine_sql_statements(queries):
- return ";\n".join(queries)
-
-
-def find_last_keyword_idx(parsed_query):
- for i in reversed(range(len(parsed_query.tokens))):
- if parsed_query.tokens[i].ttype in sqlparse.tokens.Keyword:
- return i
- return -1
+SUPPORTED_COLUMN_TYPES = set(
+ [TYPE_INTEGER, TYPE_FLOAT, TYPE_BOOLEAN, TYPE_STRING, TYPE_DATETIME, TYPE_DATE]
+)
class InterruptException(Exception):
@@ -113,13 +57,11 @@ class NotSupported(Exception):
pass
-class BaseQueryRunner:
+class BaseQueryRunner(object):
deprecated = False
should_annotate_query = True
noop_query = None
- limit_query = " LIMIT 1000"
- limit_keywords = ["LIMIT", "OFFSET"]
- limit_after_select = False
+ sample_query = None
def __init__(self, configuration):
self.syntax = "sql"
@@ -142,7 +84,7 @@ def host(self):
"""Returns this query runner's configured host.
This is used primarily for temporarily swapping endpoints when using SSH tunnels to connect to a data source.
- `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
+ `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
configuration values. If your query runner uses a different schema (e.g. a web address), you should override this function.
"""
if "host" in self.configuration:
@@ -155,7 +97,7 @@ def host(self, host):
"""Sets this query runner's configured host.
This is used primarily for temporarily swapping endpoints when using SSH tunnels to connect to a data source.
- `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
+ `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
configuration values. If your query runner uses a different schema (e.g. a web address), you should override this function.
"""
if "host" in self.configuration:
@@ -168,7 +110,7 @@ def port(self):
"""Returns this query runner's configured port.
This is used primarily for temporarily swapping endpoints when using SSH tunnels to connect to a data source.
- `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
+ `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
configuration values. If your query runner uses a different schema (e.g. a web address), you should override this function.
"""
if "port" in self.configuration:
@@ -181,7 +123,7 @@ def port(self, port):
"""Sets this query runner's configured port.
This is used primarily for temporarily swapping endpoints when using SSH tunnels to connect to a data source.
- `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
+ `BaseQueryRunner`'s naĂŻve implementation supports query runner implementations that store endpoints using `host` and `port`
configuration values. If your query runner uses a different schema (e.g. a web address), you should override this function.
"""
if "port" in self.configuration:
@@ -213,37 +155,51 @@ def run_query(self, query, user):
raise NotImplementedError()
def fetch_columns(self, columns):
- column_names = set()
- duplicates_counters = defaultdict(int)
+ column_names = []
+ duplicates_counter = 1
new_columns = []
for col in columns:
column_name = col[0]
- while column_name in column_names:
- duplicates_counters[col[0]] += 1
- column_name = "{}{}".format(col[0], duplicates_counters[col[0]])
+ if column_name in column_names:
+ column_name = "{}{}".format(column_name, duplicates_counter)
+ duplicates_counter += 1
- column_names.add(column_name)
- new_columns.append({"name": column_name, "friendly_name": column_name, "type": col[1]})
+ column_names.append(column_name)
+ new_columns.append(
+ {"name": column_name, "friendly_name": column_name, "type": col[1]}
+ )
return new_columns
def get_schema(self, get_stats=False):
raise NotSupported()
- def _handle_run_query_error(self, error):
- if error is None:
- return
-
- logger.error(error)
- raise Exception(f"Error during query execution. Reason: {error}")
-
def _run_query_internal(self, query):
results, error = self.run_query(query, None)
if error is not None:
raise Exception("Failed running query [%s]." % query)
- return results["rows"]
+ return json_loads(results)["rows"]
+
+ def get_table_sample(self, table_name):
+ if self.sample_query is None:
+ raise NotImplementedError()
+
+ query = self.sample_query.format(table=table_name)
+
+ results, error = self.run_query(query, None)
+ if error is not None:
+ logger.exception(error)
+ raise NotSupported()
+
+ rows = json_loads(results).get("rows", [])
+ if len(rows) > 0:
+ sample = rows[0]
+ else:
+ sample = {}
+
+ return sample
@classmethod
def to_dict(cls):
@@ -254,17 +210,6 @@ def to_dict(cls):
**({"deprecated": True} if cls.deprecated else {}),
}
- @property
- def supports_auto_limit(self):
- return False
-
- def apply_auto_limit(self, query_text, should_apply_auto_limit):
- return query_text
-
- def gen_query_hash(self, query_text, set_auto_limit=False):
- query_text = self.apply_auto_limit(query_text, set_auto_limit)
- return utils.gen_query_hash(query_text)
-
class BaseSQLQueryRunner(BaseQueryRunner):
def get_schema(self, get_stats=False):
@@ -279,52 +224,15 @@ def _get_tables(self, schema_dict):
def _get_tables_stats(self, tables_dict):
for t in tables_dict.keys():
- if isinstance(tables_dict[t], dict):
+ if type(tables_dict[t]) == dict:
res = self._run_query_internal("select count(*) as cnt from %s" % t)
tables_dict[t]["size"] = res[0]["cnt"]
- @property
- def supports_auto_limit(self):
- return True
-
- def query_is_select_no_limit(self, query):
- parsed_query = sqlparse.parse(query)[0]
- last_keyword_idx = find_last_keyword_idx(parsed_query)
- # Either invalid query or query that is not select
- if last_keyword_idx == -1 or parsed_query.tokens[0].value.upper() != "SELECT":
- return False
-
- no_limit = parsed_query.tokens[last_keyword_idx].value.upper() not in self.limit_keywords
-
- return no_limit
-
- def add_limit_to_query(self, query):
- parsed_query = sqlparse.parse(query)[0]
- limit_tokens = sqlparse.parse(self.limit_query)[0].tokens
- length = len(parsed_query.tokens)
- if not self.limit_after_select:
- if parsed_query.tokens[length - 1].ttype == sqlparse.tokens.Punctuation:
- parsed_query.tokens[length - 1 : length - 1] = limit_tokens
- else:
- parsed_query.tokens += limit_tokens
- else:
- for i in range(length - 1, -1, -1):
- if parsed_query[i].value.upper() == "SELECT":
- index = parsed_query.token_index(parsed_query[i + 1])
- parsed_query = sqlparse.sql.Statement(
- parsed_query.tokens[:index] + limit_tokens + parsed_query.tokens[index:]
- )
- break
- return str(parsed_query)
- def apply_auto_limit(self, query_text, should_apply_auto_limit):
- queries = split_sql_statements(query_text)
- if should_apply_auto_limit:
- # we only check for last one in the list because it is the one that we show result
- last_query = queries[-1]
- if self.query_is_select_no_limit(last_query):
- queries[-1] = self.add_limit_to_query(last_query)
- return combine_sql_statements(queries)
+def is_private_address(url):
+ hostname = urlparse(url).hostname
+ ip_address = socket.gethostbyname(hostname)
+ return ipaddress.ip_address(text_type(ip_address)).is_private
class BaseHTTPQueryRunner(BaseQueryRunner):
@@ -344,6 +252,12 @@ def configuration_schema(cls):
"url": {"type": "string", "title": cls.url_title},
"username": {"type": "string", "title": cls.username_title},
"password": {"type": "string", "title": cls.password_title},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"secret": ["password"],
"order": ["url", "username", "password"],
@@ -370,6 +284,9 @@ def get_auth(self):
return None
def get_response(self, url, auth=None, http_method="get", **kwargs):
+ if is_private_address(url) and settings.ENFORCE_PRIVATE_ADDRESS_BLOCK:
+ raise Exception("Can't query private addresses.")
+
# Get authentication values if not given
if auth is None:
auth = self.get_auth()
@@ -389,14 +306,12 @@ def get_response(self, url, auth=None, http_method="get", **kwargs):
if response.status_code != 200:
error = "{} ({}).".format(self.response_error, response.status_code)
- except requests_or_advocate.HTTPError as exc:
+ except requests.HTTPError as exc:
logger.exception(exc)
- error = "Failed to execute query. "
- f"Return Code: {response.status_code} Reason: {response.text}"
- except UnacceptableAddressException as exc:
- logger.exception(exc)
- error = "Can't query private addresses."
- except requests_or_advocate.RequestException as exc:
+ error = "Failed to execute query. " "Return Code: {} Reason: {}".format(
+ response.status_code, response.text
+ )
+ except requests.RequestException as exc:
# Catch all other requests exceptions and return the error.
logger.exception(exc)
error = str(exc)
@@ -492,7 +407,9 @@ def wrapper(*args, **kwargs):
try:
remote_host, remote_port = query_runner.host, query_runner.port
except NotImplementedError:
- raise NotImplementedError("SSH tunneling is not implemented for this query runner yet.")
+ raise NotImplementedError(
+ "SSH tunneling is not implemented for this query runner yet."
+ )
stack = ExitStack()
try:
@@ -502,7 +419,11 @@ def wrapper(*args, **kwargs):
"ssh_username": details["ssh_username"],
**settings.dynamic_settings.ssh_tunnel_auth(),
}
- server = stack.enter_context(open_tunnel(bastion_address, remote_bind_address=remote_address, **auth))
+ server = stack.enter_context(
+ open_tunnel(
+ bastion_address, remote_bind_address=remote_address, **auth
+ )
+ )
except Exception as error:
raise type(error)("SSH tunnel: {}".format(str(error)))
diff --git a/redash/query_runner/amazon_elasticsearch.py b/redash/query_runner/amazon_elasticsearch.py
index 1b36cad75c..cf81969874 100644
--- a/redash/query_runner/amazon_elasticsearch.py
+++ b/redash/query_runner/amazon_elasticsearch.py
@@ -1,16 +1,16 @@
+from .elasticsearch import ElasticSearch
from . import register
-from .elasticsearch2 import ElasticSearch2
try:
- from botocore import credentials, session
from requests_aws_sign import AWSV4Sign
+ from botocore import session, credentials
enabled = True
except ImportError:
enabled = False
-class AmazonElasticsearchService(ElasticSearch2):
+class AmazonElasticsearchService(ElasticSearch):
@classmethod
def name(cls):
return "Amazon Elasticsearch Service"
@@ -63,8 +63,5 @@ def __init__(self, configuration):
self.auth = AWSV4Sign(cred, region, "es")
- def get_auth(self):
- return self.auth
-
register(AmazonElasticsearchService)
diff --git a/redash/query_runner/arango.py b/redash/query_runner/arango.py
deleted file mode 100644
index c47e7cd3b1..0000000000
--- a/redash/query_runner/arango.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import logging
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_FLOAT,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-
-logger = logging.getLogger(__name__)
-
-try:
- from arango import ArangoClient
-
- enabled = True
-except ImportError:
- enabled = False
-
-
-_TYPE_MAPPINGS = {
- "boolean": TYPE_BOOLEAN,
- "number": TYPE_FLOAT,
- "string": TYPE_STRING,
- "array": TYPE_STRING,
- "object": TYPE_STRING,
-}
-
-
-class Arango(BaseQueryRunner):
- noop_query = "RETURN {'id': 1}"
-
- @classmethod
- def name(cls):
- return "ArangoDB"
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "user": {"type": "string"},
- "password": {"type": "string"},
- "host": {"type": "string", "default": "127.0.0.1"},
- "port": {"type": "number", "default": 8529},
- "dbname": {"type": "string", "title": "Database Name"},
- "timeout": {"type": "number", "default": 0.0, "title": "AQL Timeout in seconds (0 = no timeout)"},
- },
- "order": ["host", "port", "user", "password", "dbname"],
- "required": ["host", "user", "password", "dbname"],
- "secret": ["password"],
- }
-
- @classmethod
- def enabled(cls):
- try:
- import arango # noqa: F401
- except ImportError:
- return False
-
- return True
-
- @classmethod
- def type(cls):
- return "arangodb"
-
- def run_query(self, query, user):
- client = ArangoClient(hosts="{}:{}".format(self.configuration["host"], self.configuration.get("port", 8529)))
- db = client.db(
- self.configuration["dbname"], username=self.configuration["user"], password=self.configuration["password"]
- )
-
- try:
- cursor = db.aql.execute(query, max_runtime=self.configuration.get("timeout", 0.0))
- result = [i for i in cursor]
- column_tuples = [(i, TYPE_STRING) for i in result[0].keys()]
- columns = self.fetch_columns(column_tuples)
- data = {
- "columns": columns,
- "rows": result,
- }
-
- error = None
- except Exception:
- raise
-
- return data, error
-
-
-register(Arango)
diff --git a/redash/query_runner/athena.py b/redash/query_runner/athena.py
index 957e8bf983..fb8aba9621 100644
--- a/redash/query_runner/athena.py
+++ b/redash/query_runner/athena.py
@@ -1,27 +1,23 @@
import logging
import os
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from redash.query_runner import *
from redash.settings import parse_boolean
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
ANNOTATE_QUERY = parse_boolean(os.environ.get("ATHENA_ANNOTATE_QUERY", "true"))
-SHOW_EXTRA_SETTINGS = parse_boolean(os.environ.get("ATHENA_SHOW_EXTRA_SETTINGS", "true"))
+SHOW_EXTRA_SETTINGS = parse_boolean(
+ os.environ.get("ATHENA_SHOW_EXTRA_SETTINGS", "true")
+)
ASSUME_ROLE = parse_boolean(os.environ.get("ATHENA_ASSUME_ROLE", "false"))
-OPTIONAL_CREDENTIALS = parse_boolean(os.environ.get("ATHENA_OPTIONAL_CREDENTIALS", "true"))
+OPTIONAL_CREDENTIALS = parse_boolean(
+ os.environ.get("ATHENA_OPTIONAL_CREDENTIALS", "true")
+)
try:
- import boto3
import pyathena
+ import boto3
enabled = True
except ImportError:
@@ -46,7 +42,7 @@
}
-class SimpleFormatter:
+class SimpleFormatter(object):
def format(self, operation, parameters=None):
return operation
@@ -54,6 +50,10 @@ def format(self, operation, parameters=None):
class Athena(BaseQueryRunner):
noop_query = "SELECT 1"
+ # This takes a 1% random sample from {table}, reducing
+ # the runtime and data scanned for the query
+ sample_query = "SELECT * FROM {table} TABLESAMPLE SYSTEM (1) LIMIT 1"
+
@classmethod
def name(cls):
return "Amazon Athena"
@@ -76,10 +76,6 @@ def configuration_schema(cls):
"default": "default",
},
"glue": {"type": "boolean", "title": "Use Glue Data Catalog"},
- "catalog_ids": {
- "type": "string",
- "title": "Enter Glue Data Catalog IDs, separated by commas (leave blank for default catalog)",
- },
"work_group": {
"type": "string",
"title": "Athena Work Group",
@@ -90,26 +86,22 @@ def configuration_schema(cls):
"title": "Athena cost per Tb scanned (USD)",
"default": 5,
},
- "result_reuse_enable": {
- "type": "boolean",
- "title": "Reuse Athena query results",
- },
- "result_reuse_minutes": {
- "type": "number",
- "title": "Minutes to reuse Athena query results",
- "default": 60,
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
},
+ "samples": {"type": "boolean", "title": "Show Data Samples"},
},
"required": ["region", "s3_staging_dir"],
- "extra_options": ["glue", "catalog_ids", "cost_per_tb", "result_reuse_enable", "result_reuse_minutes"],
+ "extra_options": ["glue", "cost_per_tb"],
"order": [
"region",
"s3_staging_dir",
"schema",
"work_group",
"cost_per_tb",
- "result_reuse_enable",
- "result_reuse_minutes",
],
"secret": ["aws_secret_key"],
}
@@ -187,66 +179,63 @@ def _get_iam_credentials(self, user=None):
"region_name": self.configuration["region"],
}
- def __get_schema_from_glue(self, catalog_id=""):
+ def __get_schema_from_glue(self):
client = boto3.client("glue", **self._get_iam_credentials())
schema = {}
database_paginator = client.get_paginator("get_databases")
table_paginator = client.get_paginator("get_tables")
- databases_iterator = database_paginator.paginate(
- **({"CatalogId": catalog_id} if catalog_id != "" else {}),
- )
-
- for databases in databases_iterator:
+ for databases in database_paginator.paginate():
for database in databases["DatabaseList"]:
- iterator = table_paginator.paginate(
- DatabaseName=database["Name"],
- **({"CatalogId": catalog_id} if catalog_id != "" else {}),
- )
+ iterator = table_paginator.paginate(DatabaseName=database["Name"])
for table in iterator.search("TableList[]"):
table_name = "%s.%s" % (database["Name"], table["Name"])
- if "StorageDescriptor" not in table:
- logger.warning("Glue table doesn't have StorageDescriptor: %s", table_name)
- continue
if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
- for column_data in table["StorageDescriptor"]["Columns"]:
- column = {
- "name": column_data["Name"],
- "type": column_data["Type"] if "Type" in column_data else None,
- }
- schema[table_name]["columns"].append(column)
- for partition in table.get("PartitionKeys", []):
- partition_column = {
- "name": partition["Name"],
- "type": partition["Type"] if "Type" in partition else None,
+ column = [
+ columns["Name"]
+ for columns in table["StorageDescriptor"]["Columns"]
+ ]
+ metadata = [
+ {"name": column_data["Name"], "type": column_data["Type"]}
+ for column_data in table["StorageDescriptor"]["Columns"]
+ ]
+ schema[table_name] = {
+ "name": table_name,
+ "columns": column,
+ "metadata": metadata,
}
- schema[table_name]["columns"].append(partition_column)
+ for partition in table.get("PartitionKeys", []):
+ schema[table_name]["columns"].append(partition["Name"])
+ schema[table_name]["metadata"].append(
+ {"name": partition["Name"], "type": partition["Type"]}
+ )
return list(schema.values())
def get_schema(self, get_stats=False):
if self.configuration.get("glue", False):
- catalog_ids = [id.strip() for id in self.configuration.get("catalog_ids", "").split(",")]
- return sum([self.__get_schema_from_glue(catalog_id) for catalog_id in catalog_ids], [])
+ return self.__get_schema_from_glue()
schema = {}
query = """
- SELECT table_schema, table_name, column_name, data_type
+ SELECT table_schema, table_name, column_name, data_type AS column_type
FROM information_schema.columns
WHERE table_schema NOT IN ('information_schema')
"""
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+ results = json_loads(results)
for row in results["rows"]:
table_name = "{0}.{1}".format(row["table_schema"], row["table_name"])
if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
- schema[table_name]["columns"].append({"name": row["column_name"], "type": row["data_type"]})
+ schema[table_name] = {"name": table_name, "columns": [], "metadata": []}
+ schema[table_name]["columns"].append(row["column_name"])
+ schema[table_name]["metadata"].append(
+ {"name": row["column_name"], "type": row["column_type"],}
+ )
return list(schema.values())
@@ -258,16 +247,19 @@ def run_query(self, query, user):
kms_key=self.configuration.get("kms_key", None),
work_group=self.configuration.get("work_group", "primary"),
formatter=SimpleFormatter(),
- result_reuse_enable=self.configuration.get("result_reuse_enable", False),
- result_reuse_minutes=self.configuration.get("result_reuse_minutes", 60),
- **self._get_iam_credentials(user=user),
+ **self._get_iam_credentials(user=user)
).cursor()
try:
cursor.execute(query)
- column_tuples = [(i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description]
+ column_tuples = [
+ (i[0], _TYPE_MAPPINGS.get(i[1], None)) for i in cursor.description
+ ]
columns = self.fetch_columns(column_tuples)
- rows = [dict(zip(([c["name"] for c in columns]), r)) for i, r in enumerate(cursor.fetchall())]
+ rows = [
+ dict(zip(([c["name"] for c in columns]), r))
+ for i, r in enumerate(cursor.fetchall())
+ ]
qbytes = None
athena_query_id = None
try:
@@ -290,13 +282,14 @@ def run_query(self, query, user):
},
}
+ json_data = json_dumps(data, ignore_nan=True)
error = None
except Exception:
if cursor.query_id:
cursor.cancel()
raise
- return data, error
+ return json_data, error
register(Athena)
diff --git a/redash/query_runner/axibase_tsd.py b/redash/query_runner/axibase_tsd.py
index 3c535c4568..bd9bf8df1a 100644
--- a/redash/query_runner/axibase_tsd.py
+++ b/redash/query_runner/axibase_tsd.py
@@ -1,26 +1,18 @@
-import csv
+from io import StringIO
import logging
+import sys
import uuid
+import csv
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- InterruptException,
- JobTimeoutException,
- register,
-)
-from redash.utils import json_loads
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
try:
import atsd_client
from atsd_client.exceptions import SQLException
- from atsd_client.services import MetricsService, SQLService
+ from atsd_client.services import SQLService, MetricsService
enabled = True
except ImportError:
@@ -123,6 +115,12 @@ def configuration_schema(cls):
"type": "boolean",
"title": "Trust SSL Certificate",
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["username", "password", "hostname", "protocol", "port"],
"secret": ["password"],
@@ -157,16 +155,17 @@ def run_query(self, query, user):
columns, rows = generate_rows_and_columns(data)
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
except SQLException as e:
- data = None
+ json_data = None
error = e.content
except (KeyboardInterrupt, InterruptException, JobTimeoutException):
sql.cancel_query(query_id)
raise
- return data, error
+ return json_data, error
def get_schema(self, get_stats=False):
connection = atsd_client.connect_url(
diff --git a/redash/query_runner/azure_kusto.py b/redash/query_runner/azure_kusto.py
index c7372fe184..24293618a6 100644
--- a/redash/query_runner/azure_kusto.py
+++ b/redash/query_runner/azure_kusto.py
@@ -1,22 +1,18 @@
+from redash.query_runner import BaseQueryRunner, register
from redash.query_runner import (
- TYPE_BOOLEAN,
+ TYPE_STRING,
TYPE_DATE,
TYPE_DATETIME,
- TYPE_FLOAT,
TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
+ TYPE_FLOAT,
+ TYPE_BOOLEAN,
)
-from redash.utils import json_loads
+from redash.utils import json_dumps, json_loads
+
try:
+ from azure.kusto.data.request import KustoClient, KustoConnectionStringBuilder, ClientRequestProperties
from azure.kusto.data.exceptions import KustoServiceError
- from azure.kusto.data.request import (
- ClientRequestProperties,
- KustoClient,
- KustoConnectionStringBuilder,
- )
enabled = True
except ImportError:
@@ -91,6 +87,7 @@ def name(cls):
return "Azure Data Explorer (Kusto)"
def run_query(self, query, user):
+
kcsb = KustoConnectionStringBuilder.with_aad_application_key_authentication(
connection_string=self.configuration["cluster"],
aad_app_id=self.configuration["azure_ad_client_id"],
@@ -124,15 +121,16 @@ def run_query(self, query, user):
error = None
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
except KustoServiceError as err:
- data = None
+ json_data = None
try:
error = err.args[1][0]["error"]["@message"]
except (IndexError, KeyError):
error = err.args[1]
- return data, error
+ return json_data, error
def get_schema(self, get_stats=False):
query = ".show database schema as json"
@@ -140,10 +138,14 @@ def get_schema(self, get_stats=False):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
schema_as_json = json_loads(results["rows"][0]["DatabaseSchema"])
- tables_list = schema_as_json["Databases"][self.configuration["database"]]["Tables"].values()
+ tables_list = schema_as_json["Databases"][self.configuration["database"]][
+ "Tables"
+ ].values()
schema = {}
diff --git a/redash/query_runner/big_query.py b/redash/query_runner/big_query.py
index 88a31b7b2c..0e29536fe1 100644
--- a/redash/query_runner/big_query.py
+++ b/redash/query_runner/big_query.py
@@ -1,32 +1,24 @@
import datetime
import logging
-import socket
+import operator
+import sys
import time
from base64 import b64decode
+import httplib2
+import requests
+
from redash import settings
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- InterruptException,
- JobTimeoutException,
- register,
-)
-from redash.utils import json_loads
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
try:
import apiclient.errors
- import google.auth
from apiclient.discovery import build
- from apiclient.errors import HttpError # noqa: F401
- from google.oauth2.service_account import Credentials
+ from apiclient.errors import HttpError
+ from oauth2client.service_account import ServiceAccountCredentials
enabled = True
except ImportError:
@@ -38,8 +30,6 @@
"BOOLEAN": TYPE_BOOLEAN,
"STRING": TYPE_STRING,
"TIMESTAMP": TYPE_DATETIME,
- "DATETIME": TYPE_DATETIME,
- "DATE": TYPE_DATE,
}
@@ -63,7 +53,9 @@ def transform_row(row, fields):
for column_index, cell in enumerate(row["f"]):
field = fields[column_index]
if field.get("mode") == "REPEATED":
- cell_value = [transform_cell(field["type"], item["v"]) for item in cell["v"]]
+ cell_value = [
+ transform_cell(field["type"], item["v"]) for item in cell["v"]
+ ]
else:
cell_value = transform_cell(field["type"], cell["v"])
@@ -73,7 +65,7 @@ def transform_row(row, fields):
def _load_key(filename):
- f = open(filename, "rb")
+ f = file(filename, "rb")
try:
return f.read()
finally:
@@ -86,24 +78,16 @@ def _get_query_results(jobs, project_id, location, job_id, start_index):
).execute()
logging.debug("query_reply %s", query_reply)
if not query_reply["jobComplete"]:
- time.sleep(1)
+ time.sleep(10)
return _get_query_results(jobs, project_id, location, job_id, start_index)
return query_reply
-def _get_total_bytes_processed_for_resp(bq_response):
- # BigQuery hides the total bytes processed for queries to tables with row-level access controls.
- # For these queries the "totalBytesProcessed" field may not be defined in the response.
- return int(bq_response.get("totalBytesProcessed", "0"))
-
-
class BigQuery(BaseQueryRunner):
+ should_annotate_query = False
noop_query = "SELECT 1"
-
- def __init__(self, configuration):
- super().__init__(configuration)
- self.should_annotate_query = configuration.get("useQueryAnnotation", False)
+ sample_query = "#standardSQL\n SELECT * FROM {table} LIMIT 1"
@classmethod
def enabled(cls):
@@ -115,7 +99,7 @@ def configuration_schema(cls):
"type": "object",
"properties": {
"projectId": {"type": "string", "title": "Project ID"},
- "jsonKeyFile": {"type": "string", "title": "JSON Key File (ADC is used if omitted)"},
+ "jsonKeyFile": {"type": "string", "title": "JSON Key File"},
"totalMBytesProcessedLimit": {
"type": "number",
"title": "Scanned Data Limit (MB)",
@@ -135,13 +119,15 @@ def configuration_schema(cls):
"type": "number",
"title": "Maximum Billing Tier",
},
- "useQueryAnnotation": {
- "type": "boolean",
- "title": "Use Query Annotation",
- "default": False,
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
},
+ "samples": {"type": "boolean", "title": "Show Data Samples"},
},
- "required": ["projectId"],
+ "required": ["jsonKeyFile", "projectId"],
"order": [
"projectId",
"jsonKeyFile",
@@ -151,26 +137,23 @@ def configuration_schema(cls):
"totalMBytesProcessedLimit",
"maximumBillingTier",
"userDefinedFunctionResourceUri",
- "useQueryAnnotation",
],
"secret": ["jsonKeyFile"],
}
def _get_bigquery_service(self):
- socket.setdefaulttimeout(settings.BIGQUERY_HTTP_TIMEOUT)
-
- scopes = [
+ scope = [
"https://www.googleapis.com/auth/bigquery",
"https://www.googleapis.com/auth/drive",
]
- try:
- key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
- creds = Credentials.from_service_account_info(key, scopes=scopes)
- except KeyError:
- creds = google.auth.default(scopes=scopes)[0]
+ key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
+
+ creds = ServiceAccountCredentials.from_json_keyfile_dict(key, scope)
+ http = httplib2.Http(timeout=settings.BIGQUERY_HTTP_TIMEOUT)
+ http = creds.authorize(http)
- return build("bigquery", "v2", credentials=creds, cache_discovery=False)
+ return build("bigquery", "v2", http=http)
def _get_project_id(self):
return self.configuration["projectId"]
@@ -188,7 +171,7 @@ def _get_total_bytes_processed(self, jobs, query):
job_data["useLegacySql"] = False
response = jobs.query(projectId=self._get_project_id(), body=job_data).execute()
- return _get_total_bytes_processed_for_resp(response)
+ return int(response["totalBytesProcessed"])
def _get_job_data(self, query):
job_data = {"configuration": {"query": {"query": query}}}
@@ -200,13 +183,17 @@ def _get_job_data(self, query):
job_data["configuration"]["query"]["useLegacySql"] = False
if self.configuration.get("userDefinedFunctionResourceUri"):
- resource_uris = self.configuration["userDefinedFunctionResourceUri"].split(",")
+ resource_uris = self.configuration["userDefinedFunctionResourceUri"].split(
+ ","
+ )
job_data["configuration"]["query"]["userDefinedFunctionResources"] = [
{"resourceUri": resource_uri} for resource_uri in resource_uris
]
if "maximumBillingTier" in self.configuration:
- job_data["configuration"]["query"]["maximumBillingTier"] = self.configuration["maximumBillingTier"]
+ job_data["configuration"]["query"][
+ "maximumBillingTier"
+ ] = self.configuration["maximumBillingTier"]
return job_data
@@ -249,7 +236,9 @@ def _get_query_result(self, jobs, query):
{
"name": f["name"],
"friendly_name": f["name"],
- "type": "string" if f.get("mode") == "REPEATED" else types_map.get(f["type"], "string"),
+ "type": "string"
+ if f.get("mode") == "REPEATED"
+ else types_map.get(f["type"], "string"),
}
for f in query_reply["schema"]["fields"]
]
@@ -257,77 +246,188 @@ def _get_query_result(self, jobs, query):
data = {
"columns": columns,
"rows": rows,
- "metadata": {"data_scanned": _get_total_bytes_processed_for_resp(query_reply)},
+ "metadata": {"data_scanned": int(query_reply["totalBytesProcessed"])},
}
return data
def _get_columns_schema(self, table_data):
columns = []
+ metadata = []
for column in table_data.get("schema", {}).get("fields", []):
- columns.extend(self._get_columns_schema_column(column))
+ metadatum = self._get_column_metadata(column)
+ metadata.extend(metadatum)
+ columns.extend(map(operator.itemgetter("name"), metadatum))
project_id = self._get_project_id()
table_name = table_data["id"].replace("%s:" % project_id, "")
- return {"name": table_name, "columns": columns}
+ return {"name": table_name, "columns": columns, "metadata": metadata}
- def _get_columns_schema_column(self, column):
- columns = []
+ def _get_column_metadata(self, column):
+ metadata = []
if column["type"] == "RECORD":
for field in column["fields"]:
- columns.append("{}.{}".format(column["name"], field["name"]))
+ field_name = u"{}.{}".format(column["name"], field["name"])
+ metadata.append({"name": field_name, "type": field["type"]})
else:
- columns.append(column["name"])
+ metadata.append({"name": column["name"], "type": column["type"]})
+ return metadata
+
+ def _columns_and_samples_to_dict(self, schema, samples):
+ samples_dict = {}
+ if not samples:
+ return samples_dict
+
+ # If a sample exists, its shape/length should be analogous to
+ # the schema provided (i.e their lengths should match up)
+ for i, column in enumerate(schema):
+ if column["type"] == "RECORD":
+ if column.get("mode", None) == "REPEATED":
+ # Repeated fields have multiple samples of the same format.
+ # We only need to show the first one as an example.
+ associated_sample = [] if len(samples[i]) == 0 else samples[i][0]
+ else:
+ associated_sample = samples[i] or []
+
+ for j, field in enumerate(column["fields"]):
+ field_name = u"{}.{}".format(column["name"], field["name"])
+ samples_dict[field_name] = None
+ if len(associated_sample) > 0:
+ samples_dict[field_name] = associated_sample[j]
+ else:
+ samples_dict[column["name"]] = samples[i]
+
+ return samples_dict
+
+ def _flatten_samples(self, samples):
+ samples_list = []
+ for field in samples:
+ value = field["v"]
+ if isinstance(value, dict):
+ samples_list.append(self._flatten_samples(value.get("f", [])))
+ elif isinstance(value, list):
+ samples_list.append(self._flatten_samples(value))
+ else:
+ samples_list.append(value)
- return columns
+ return samples_list
+
+ def get_table_sample(self, table_name):
+ if not self.configuration.get("loadSchema", False):
+ return {}
- def _get_project_datasets(self, project_id):
- result = []
service = self._get_bigquery_service()
+ project_id = self._get_project_id()
+
+ dataset_id, table_id = table_name.split(".", 1)
+
+ try:
+ # NOTE: the `sample_response` is limited by `maxResults` here.
+ # Without this limit, the response would be very large and require
+ # pagination using `nextPageToken`.
+ sample_response = (
+ service.tabledata()
+ .list(
+ projectId=project_id,
+ datasetId=dataset_id,
+ tableId=table_id,
+ fields="rows",
+ maxResults=1,
+ )
+ .execute()
+ )
+ schema_response = (
+ service.tables()
+ .get(
+ projectId=project_id,
+ datasetId=dataset_id,
+ tableId=table_id,
+ fields="schema,id",
+ )
+ .execute()
+ )
+ table_rows = sample_response.get("rows", [])
+
+ if len(table_rows) == 0:
+ samples = []
+ else:
+ samples = table_rows[0].get("f", [])
- datasets = service.datasets().list(projectId=project_id).execute()
- result.extend(datasets.get("datasets", []))
- nextPageToken = datasets.get("nextPageToken", None)
+ schema = schema_response.get("schema", {}).get("fields", [])
+ columns = self._get_columns_schema(schema_response).get("columns", [])
- while nextPageToken is not None:
- datasets = service.datasets().list(projectId=project_id, pageToken=nextPageToken).execute()
- result.extend(datasets.get("datasets", []))
- nextPageToken = datasets.get("nextPageToken", None)
+ flattened_samples = self._flatten_samples(samples)
+ samples_dict = self._columns_and_samples_to_dict(schema, flattened_samples)
+ return samples_dict
+ except HttpError as http_error:
+ logger.exception(
+ "Error communicating with server for sample for table %s: %s",
+ table_name,
+ http_error,
+ )
- return result
+ # If there is an error getting the sample using the API,
+ # try to do it by running a `select *` with a limit.
+ return super().get_table_sample(table_name)
def get_schema(self, get_stats=False):
if not self.configuration.get("loadSchema", False):
return []
+ service = self._get_bigquery_service()
project_id = self._get_project_id()
- datasets = self._get_project_datasets(project_id)
-
- query_base = """
- SELECT table_schema, table_name, field_path, data_type
- FROM `{dataset_id}`.INFORMATION_SCHEMA.COLUMN_FIELD_PATHS
- WHERE table_schema NOT IN ('information_schema')
- """
-
- schema = {}
- queries = []
+ # get a list of Big Query datasets
+ datasets_request = service.datasets().list(
+ projectId=project_id,
+ fields="datasets/datasetReference/datasetId,nextPageToken",
+ )
+ datasets = []
+ while datasets_request:
+ # request datasets
+ datasets_response = datasets_request.execute()
+ # store results
+ datasets.extend(datasets_response.get("datasets", []))
+ # try loading next page
+ datasets_request = service.datasets().list_next(
+ datasets_request,
+ datasets_response,
+ )
+
+ schema = []
+ # load all tables for all datasets
for dataset in datasets:
dataset_id = dataset["datasetReference"]["datasetId"]
- query = query_base.format(dataset_id=dataset_id)
- queries.append(query)
-
- query = "\nUNION ALL\n".join(queries)
- results, error = self.run_query(query, None)
- if error is not None:
- self._handle_run_query_error(error)
+ tables_request = service.tables().list(
+ projectId=project_id,
+ datasetId=dataset_id,
+ fields="tables/tableReference/tableId,nextPageToken",
+ )
+ while tables_request:
+ # request tables with fields above
+ tables_response = tables_request.execute()
+ for table in tables_response.get("tables", []):
+ # load schema for given table
+ table_data = (
+ service.tables()
+ .get(
+ projectId=project_id,
+ datasetId=dataset_id,
+ tableId=table["tableReference"]["tableId"],
+ fields="id,schema",
+ )
+ .execute()
+ )
+ # build schema data with given table data
+ table_schema = self._get_columns_schema(table_data)
+ schema.append(table_schema)
- for row in results["rows"]:
- table_name = "{0}.{1}".format(row["table_schema"], row["table_name"])
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
- schema[table_name]["columns"].append({"name": row["field_path"], "type": row["data_type"]})
+ # try loading next page of results
+ tables_request = service.tables().list_next(
+ tables_request,
+ tables_response,
+ )
- return list(schema.values())
+ return schema
def run_query(self, query, user):
logger.debug("BigQuery got query: %s", query)
@@ -338,19 +438,23 @@ def run_query(self, query, user):
try:
if "totalMBytesProcessedLimit" in self.configuration:
limitMB = self.configuration["totalMBytesProcessedLimit"]
- processedMB = self._get_total_bytes_processed(jobs, query) / 1000.0 / 1000.0
+ processedMB = (
+ self._get_total_bytes_processed(jobs, query) / 1000.0 / 1000.0
+ )
if limitMB < processedMB:
return (
None,
- "Larger than %d MBytes will be processed (%f MBytes)" % (limitMB, processedMB),
+ "Larger than %d MBytes will be processed (%f MBytes)"
+ % (limitMB, processedMB),
)
data = self._get_query_result(jobs, query)
error = None
+ json_data = json_dumps(data, ignore_nan=True)
except apiclient.errors.HttpError as e:
- data = None
- if e.resp.status in [400, 404]:
+ json_data = None
+ if e.resp.status == 400:
error = json_loads(e.content)["error"]["message"]
else:
error = e.content
@@ -364,7 +468,7 @@ def run_query(self, query, user):
raise
- return data, error
+ return json_data, error
register(BigQuery)
diff --git a/redash/query_runner/big_query_gce.py b/redash/query_runner/big_query_gce.py
index 8ff22191d7..bc7a38d91d 100644
--- a/redash/query_runner/big_query_gce.py
+++ b/redash/query_runner/big_query_gce.py
@@ -1,15 +1,15 @@
import requests
+import httplib2
try:
- import google.auth
from apiclient.discovery import build
+ from oauth2client.contrib import gce
enabled = True
except ImportError:
enabled = False
from redash.query_runner import register
-
from .big_query import BigQuery
@@ -59,11 +59,19 @@ def configuration_schema(cls):
}
def _get_project_id(self):
- google.auth.default()[1]
+ return requests.get(
+ "http://metadata/computeMetadata/v1/project/project-id",
+ headers={"Metadata-Flavor": "Google"},
+ ).content
def _get_bigquery_service(self):
- creds = google.auth.default(scopes=["https://www.googleapis.com/auth/bigquery"])[0]
- return build("bigquery", "v2", credentials=creds, cache_discovery=False)
+ credentials = gce.AppAssertionCredentials(
+ scope="https://www.googleapis.com/auth/bigquery"
+ )
+ http = httplib2.Http()
+ http = credentials.authorize(http)
+
+ return build("bigquery", "v2", http=http)
register(BigQueryGCE)
diff --git a/redash/query_runner/cass.py b/redash/query_runner/cass.py
index fb5ce3ee5d..94d95d1854 100644
--- a/redash/query_runner/cass.py
+++ b/redash/query_runner/cass.py
@@ -5,12 +5,13 @@
from tempfile import NamedTemporaryFile
from redash.query_runner import BaseQueryRunner, register
+from redash.utils import JSONEncoder, json_dumps, json_loads
logger = logging.getLogger(__name__)
try:
- from cassandra.auth import PlainTextAuthProvider
from cassandra.cluster import Cluster
+ from cassandra.auth import PlainTextAuthProvider
from cassandra.util import sortedset
enabled = True
@@ -19,13 +20,22 @@
def generate_ssl_options_dict(protocol, cert_path=None):
- ssl_options = {"ssl_version": getattr(ssl, protocol)}
+ ssl_options = {
+ 'ssl_version': getattr(ssl, protocol)
+ }
if cert_path is not None:
- ssl_options["ca_certs"] = cert_path
- ssl_options["cert_reqs"] = ssl.CERT_REQUIRED
+ ssl_options['ca_certs'] = cert_path
+ ssl_options['cert_reqs'] = ssl.CERT_REQUIRED
return ssl_options
+class CassandraJSONEncoder(JSONEncoder):
+ def default(self, o):
+ if isinstance(o, sortedset):
+ return list(o)
+ return super(CassandraJSONEncoder, self).default(o)
+
+
class Cassandra(BaseQueryRunner):
noop_query = "SELECT dateof(now()) FROM system.local"
@@ -33,12 +43,6 @@ class Cassandra(BaseQueryRunner):
def enabled(cls):
return enabled
- @classmethod
- def custom_json_encoder(cls, dec, o):
- if isinstance(o, sortedset):
- return list(o)
- return None
-
@classmethod
def configuration_schema(cls):
return {
@@ -56,7 +60,10 @@ def configuration_schema(cls):
},
"timeout": {"type": "number", "title": "Timeout", "default": 10},
"useSsl": {"type": "boolean", "title": "Use SSL", "default": False},
- "sslCertificateFile": {"type": "string", "title": "SSL Certificate File"},
+ "sslCertificateFile": {
+ "type": "string",
+ "title": "SSL Certificate File"
+ },
"sslProtocol": {
"type": "string",
"title": "SSL Protocol",
@@ -70,6 +77,12 @@ def configuration_schema(cls):
"PROTOCOL_TLSv1_2",
],
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["keyspace", "host", "useSsl"],
"secret": ["sslCertificateFile"],
@@ -84,6 +97,7 @@ def get_schema(self, get_stats=False):
select release_version from system.local;
"""
results, error = self.run_query(query, None)
+ results = json_loads(results)
release_version = results["rows"][0]["release_version"]
query = """
@@ -104,6 +118,7 @@ def get_schema(self, get_stats=False):
)
results, error = self.run_query(query, None)
+ results = json_loads(results)
schema = {}
for row in results["rows"]:
@@ -118,7 +133,9 @@ def get_schema(self, get_stats=False):
def run_query(self, query, user):
connection = None
cert_path = self._generate_cert_file()
- if self.configuration.get("username", "") and self.configuration.get("password", ""):
+ if self.configuration.get("username", "") and self.configuration.get(
+ "password", ""
+ ):
auth_provider = PlainTextAuthProvider(
username="{}".format(self.configuration.get("username", "")),
password="{}".format(self.configuration.get("password", "")),
@@ -151,13 +168,14 @@ def run_query(self, query, user):
rows = [dict(zip(column_names, row)) for row in result]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data, cls=CassandraJSONEncoder)
- return data, None
+ return json_data, None
def _generate_cert_file(self):
cert_encoded_bytes = self.configuration.get("sslCertificateFile", None)
if cert_encoded_bytes:
- with NamedTemporaryFile(mode="w", delete=False) as cert_file:
+ with NamedTemporaryFile(mode='w', delete=False) as cert_file:
cert_bytes = b64decode(cert_encoded_bytes)
cert_file.write(cert_bytes.decode("utf-8"))
return cert_file.name
@@ -170,7 +188,10 @@ def _cleanup_cert_file(self, cert_path):
def _get_ssl_options(self, cert_path):
ssl_options = None
if self.configuration.get("useSsl", False):
- ssl_options = generate_ssl_options_dict(protocol=self.configuration["sslProtocol"], cert_path=cert_path)
+ ssl_options = generate_ssl_options_dict(
+ protocol=self.configuration["sslProtocol"],
+ cert_path=cert_path
+ )
return ssl_options
diff --git a/redash/query_runner/clickhouse.py b/redash/query_runner/clickhouse.py
index a443659237..cc5f8e4f92 100644
--- a/redash/query_runner/clickhouse.py
+++ b/redash/query_runner/clickhouse.py
@@ -1,28 +1,15 @@
import logging
import re
from urllib.parse import urlparse
-from uuid import uuid4
import requests
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
- split_sql_statements,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
-def split_multi_query(query):
- return [st for st in split_sql_statements(query) if st != ""]
-
-
class ClickHouse(BaseSQLQueryRunner):
noop_query = "SELECT 1"
@@ -45,6 +32,12 @@ def configuration_schema(cls):
"title": "Verify SSL certificate",
"default": True,
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"order": ["url", "user", "password", "dbname"],
"required": ["dbname"],
@@ -52,6 +45,10 @@ def configuration_schema(cls):
"secret": ["password"],
}
+ @classmethod
+ def type(cls):
+ return "clickhouse"
+
@property
def _url(self):
return urlparse(self.configuration["url"])
@@ -82,7 +79,9 @@ def _get_tables(self, schema):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
table_name = "{}.{}".format(row["database"], row["table"])
@@ -94,49 +93,31 @@ def _get_tables(self, schema):
return list(schema.values())
- def _send_query(self, data, session_id=None, session_check=None):
+ def _send_query(self, data, stream=False):
url = self.configuration.get("url", "http://127.0.0.1:8123")
- timeout = self.configuration.get("timeout", 30)
-
- params = {
- "user": self.configuration.get("user", "default"),
- "password": self.configuration.get("password", ""),
- "database": self.configuration["dbname"],
- "default_format": "JSON",
- }
-
- if session_id:
- params["session_id"] = session_id
- params["session_check"] = "1" if session_check else "0"
- params["session_timeout"] = timeout
-
try:
verify = self.configuration.get("verify", True)
r = requests.post(
url,
- data=data.encode("utf-8", "ignore"),
- stream=False,
- timeout=timeout,
- params=params,
+ data=data.encode("utf-8","ignore"),
+ stream=stream,
+ timeout=self.configuration.get("timeout", 30),
+ params={
+ "user": self.configuration.get("user", "default"),
+ "password": self.configuration.get("password", ""),
+ "database": self.configuration["dbname"],
+ },
verify=verify,
)
-
- if not r.ok:
+ if r.status_code != 200:
raise Exception(r.text)
-
- # In certain situations the response body can be empty even if the query was successful, for example
- # when creating temporary tables.
- if not r.text:
- return {}
-
- response = r.json()
- if "exception" in response:
- raise Exception(response["exception"])
-
- return response
+ # logging.warning(r.json())
+ return r.json()
except requests.RequestException as e:
if e.response:
- details = "({}, Status Code: {})".format(e.__class__.__name__, e.response.status_code)
+ details = "({}, Status Code: {})".format(
+ e.__class__.__name__, e.response.status_code
+ )
else:
details = "({})".format(e.__class__.__name__)
raise Exception("Connection error to: {} {}.".format(url, details))
@@ -158,30 +139,29 @@ def _define_column_type(column):
else:
return TYPE_STRING
- def _clickhouse_query(self, query, session_id=None, session_check=None):
- logger.debug(f"{self.name()} is about to execute query: %s", query)
-
+ def _clickhouse_query(self, query):
query += "\nFORMAT JSON"
-
- response = self._send_query(query, session_id, session_check)
-
+ result = self._send_query(query)
columns = []
columns_int64 = [] # db converts value to string if its type equals UInt64
columns_totals = {}
- meta = response.get("meta", [])
- for r in meta:
+ for r in result["meta"]:
column_name = r["name"]
column_type = self._define_column_type(r["type"])
if r["type"] in ("Int64", "UInt64", "Nullable(Int64)", "Nullable(UInt64)"):
columns_int64.append(column_name)
else:
- columns_totals[column_name] = "Total" if column_type == TYPE_STRING else None
+ columns_totals[column_name] = (
+ "Total" if column_type == TYPE_STRING else None
+ )
- columns.append({"name": column_name, "friendly_name": column_name, "type": column_type})
+ columns.append(
+ {"name": column_name, "friendly_name": column_name, "type": column_type}
+ )
- rows = response.get("data", [])
+ rows = result["data"]
for row in rows:
for column in columns_int64:
try:
@@ -189,8 +169,8 @@ def _clickhouse_query(self, query, session_id=None, session_check=None):
except TypeError:
row[column] = None
- if "totals" in response:
- totals = response["totals"]
+ if "totals" in result:
+ totals = result["totals"]
for column, value in columns_totals.items():
totals[column] = value
rows.append(totals)
@@ -198,27 +178,14 @@ def _clickhouse_query(self, query, session_id=None, session_check=None):
return {"columns": columns, "rows": rows}
def run_query(self, query, user):
- queries = split_multi_query(query)
-
- if not queries:
- data = None
+ logger.debug("Clickhouse is about to execute query: %s", query)
+ if query == "":
+ json_data = None
error = "Query is empty"
- return data, error
-
+ return json_data, error
try:
- # If just one query was given no session is needed
- if len(queries) == 1:
- data = self._clickhouse_query(queries[0])
- else:
- # If more than one query was given, a session is needed. Parameter session_check must be false
- # for the first query
- session_id = "redash_{}".format(uuid4().hex)
-
- data = self._clickhouse_query(queries[0], session_id, session_check=False)
-
- for query in queries[1:]:
- data = self._clickhouse_query(query, session_id, session_check=True)
-
+ q = self._clickhouse_query(query)
+ data = json_dumps(q)
error = None
except Exception as e:
data = None
diff --git a/redash/query_runner/cloudwatch.py b/redash/query_runner/cloudwatch.py
index 699834c0a9..c4640a537d 100644
--- a/redash/query_runner/cloudwatch.py
+++ b/redash/query_runner/cloudwatch.py
@@ -1,18 +1,15 @@
-import datetime
-
import yaml
+import datetime
from redash.query_runner import BaseQueryRunner, register
-from redash.utils import parse_human_time
+from redash.utils import json_dumps, parse_human_time
try:
import boto3
-
enabled = True
except ImportError:
enabled = False
-
def parse_response(results):
columns = [
{"name": "id", "type": "string"},
@@ -121,7 +118,7 @@ def run_query(self, query, user):
rows, columns = parse_response(results)
- return {"rows": rows, "columns": columns}, None
+ return json_dumps({"rows": rows, "columns": columns}), None
register(CloudWatch)
diff --git a/redash/query_runner/cloudwatch_insights.py b/redash/query_runner/cloudwatch_insights.py
index f0ebcea117..139d5b678a 100644
--- a/redash/query_runner/cloudwatch_insights.py
+++ b/redash/query_runner/cloudwatch_insights.py
@@ -1,15 +1,13 @@
+import yaml
import datetime
import time
-import yaml
-
from redash.query_runner import BaseQueryRunner, register
-from redash.utils import parse_human_time
+from redash.utils import json_dumps, parse_human_time
try:
import boto3
- from botocore.exceptions import ParamValidationError # noqa: F401
-
+ from botocore.exceptions import ParamValidationError
enabled = True
except ImportError:
enabled = False
@@ -120,7 +118,9 @@ def get_schema(self, get_stats=False):
log_groups.append(
{
"name": group_name,
- "columns": [field["name"] for field in fields["logGroupFields"]],
+ "columns": [
+ field["name"] for field in fields["logGroupFields"]
+ ],
}
)
@@ -139,14 +139,18 @@ def run_query(self, query, user):
data = parse_response(result)
break
if result["status"] in ("Failed", "Timeout", "Unknown", "Cancelled"):
- raise Exception("CloudWatch Insights Query Execution Status: {}".format(result["status"]))
+ raise Exception(
+ "CloudWatch Insights Query Execution Status: {}".format(
+ result["status"]
+ )
+ )
elif elapsed > TIMEOUT:
raise Exception("Request exceeded timeout.")
else:
time.sleep(POLL_INTERVAL)
elapsed += POLL_INTERVAL
- return data, None
+ return json_dumps(data), None
register(CloudWatchInsights)
diff --git a/redash/query_runner/corporate_memory.py b/redash/query_runner/corporate_memory.py
deleted file mode 100644
index 0eb33c89f1..0000000000
--- a/redash/query_runner/corporate_memory.py
+++ /dev/null
@@ -1,270 +0,0 @@
-"""Provide the query runner for eccenca Corporate Memory.
-
-seeAlso: https://documentation.eccenca.com/
-seeAlso: https://eccenca.com/
-"""
-
-import json
-import logging
-from os import environ
-
-from redash.query_runner import BaseQueryRunner
-
-from . import register
-
-try:
- from cmem.cmempy.dp.proxy.graph import get_graphs_list
- from cmem.cmempy.queries import ( # noqa: F401
- QUERY_STRING,
- QueryCatalog,
- SparqlQuery,
- )
-
- enabled = True
-except ImportError:
- enabled = False
-
-logger = logging.getLogger(__name__)
-
-
-class CorporateMemoryQueryRunner(BaseQueryRunner):
- """Use eccenca Corporate Memory as redash data source"""
-
- # These environment keys are used by cmempy
- KNOWN_CONFIG_KEYS = (
- "CMEM_BASE_PROTOCOL",
- "CMEM_BASE_DOMAIN",
- "CMEM_BASE_URI",
- "SSL_VERIFY",
- "REQUESTS_CA_BUNDLE",
- "DP_API_ENDPOINT",
- "DI_API_ENDPOINT",
- "OAUTH_TOKEN_URI",
- "OAUTH_GRANT_TYPE",
- "OAUTH_USER",
- "OAUTH_PASSWORD",
- "OAUTH_CLIENT_ID",
- "OAUTH_CLIENT_SECRET",
- )
-
- # These variables hold secret data and should NOT be logged
- KNOWN_SECRET_KEYS = ("OAUTH_PASSWORD", "OAUTH_CLIENT_SECRET")
-
- # This allows for an easy connection test
- noop_query = "SELECT ?noop WHERE {BIND('noop' as ?noop)}"
-
- # We do not want to have comment in our sparql queries
- # FEATURE?: Implement annotate_query in case the metadata is useful somewhere
- should_annotate_query = False
-
- def __init__(self, configuration):
- """init the class and configuration"""
- super(CorporateMemoryQueryRunner, self).__init__(configuration)
- """
- FEATURE?: activate SPARQL support in the redash query editor
- Currently SPARQL syntax seems not to be available for react-ace
- component. However, the ace editor itself supports sparql mode:
- https://github.com/ajaxorg/ace/blob/master/lib/ace/mode/sparql.js
- then we can hopefully do: self.syntax = "sparql"
- FEATURE?: implement the retrieve Query catalog URIs in order to use them in queries
- FEATURE?: implement a way to use queries from the query catalog
- FEATURE?: allow a checkbox to NOT use owl:imports imported graphs
- FEATURE?: allow to use a context graph per data source
- """
- self.configuration = configuration
-
- def _setup_environment(self):
- """provide environment for cmempy
-
- cmempy environment variables need to match key in the properties
- object of the configuration_schema
- """
- for key in self.KNOWN_CONFIG_KEYS:
- if key in environ:
- environ.pop(key)
- value = self.configuration.get(key, None)
- if value is not None:
- environ[key] = str(value)
- if key in self.KNOWN_SECRET_KEYS:
- logger.info("{} set by config".format(key))
- else:
- logger.info("{} set by config to {}".format(key, environ[key]))
-
- @staticmethod
- def _transform_sparql_results(results):
- """transforms a SPARQL query result to a redash query result
-
- source structure: SPARQL 1.1 Query Results JSON Format
- - seeAlso: https://www.w3.org/TR/sparql11-results-json/
-
- target structure: redash result set
- there is no good documentation available
- so here an example result set as needed for redash:
- data = {
- "columns": [ {"name": "name", "type": "string", "friendly_name": "friendly name"}],
- "rows": [
- {"name": "value 1"},
- {"name": "value 2"}
- ]}
-
- FEATURE?: During the sparql_row loop, we could check the data types of the
- values and, in case they are all the same, choose something better than
- just string.
- """
- logger.info("results are: {}".format(results))
- # Not sure why we do not use the json package here but all other
- # query runner do it the same way :-)
- sparql_results = results
- # transform all bindings to redash rows
- rows = []
- for sparql_row in sparql_results["results"]["bindings"]:
- row = {}
- for var in sparql_results["head"]["vars"]:
- try:
- row[var] = sparql_row[var]["value"]
- except KeyError:
- # not bound SPARQL variables are set as empty strings
- row[var] = ""
- rows.append(row)
- # transform all vars to redash columns
- columns = []
- for var in sparql_results["head"]["vars"]:
- columns.append({"name": var, "friendly_name": var, "type": "string"})
- # Not sure why we do not use the json package here but all other
- # query runner do it the same way :-)
- return {"columns": columns, "rows": rows}
-
- @classmethod
- def name(cls):
- return "eccenca Corporate Memory (with SPARQL)"
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def type(cls):
- return "corporate_memory"
-
- def run_query(self, query, user):
- """send a sparql query to corporate memory"""
- query_text = query
- logger.info("about to execute query (user='{}'): {}".format(user, query_text))
- query = SparqlQuery(query_text)
- query_type = query.get_query_type()
- # type of None means, there is an error in the query
- # so execution is at least tried on endpoint
- if query_type not in ["SELECT", None]:
- raise ValueError("Queries of type {} can not be processed by redash.".format(query_type))
-
- self._setup_environment()
- try:
- data = self._transform_sparql_results(query.get_results())
- except Exception as error:
- logger.info("Error: {}".format(error))
- try:
- # try to load Problem Details for HTTP API JSON
- details = json.loads(error.response.text)
- error = ""
- if "title" in details:
- error += details["title"] + ": "
- if "detail" in details:
- error += details["detail"]
- return None, error
- except Exception:
- pass
-
- return None, error
-
- error = None
- return data, error
-
- @classmethod
- def configuration_schema(cls):
- """provide the configuration of the data source as json schema"""
- return {
- "type": "object",
- "properties": {
- "CMEM_BASE_URI": {"type": "string", "title": "Base URL"},
- "OAUTH_GRANT_TYPE": {
- "type": "string",
- "title": "Grant Type",
- "default": "client_credentials",
- "extendedEnum": [
- {"value": "client_credentials", "name": "client_credentials"},
- {"value": "password", "name": "password"},
- ],
- },
- "OAUTH_CLIENT_ID": {
- "type": "string",
- "title": "Client ID (e.g. cmem-service-account)",
- "default": "cmem-service-account",
- },
- "OAUTH_CLIENT_SECRET": {
- "type": "string",
- "title": "Client Secret - only needed for grant type 'client_credentials'",
- },
- "OAUTH_USER": {
- "type": "string",
- "title": "User account - only needed for grant type 'password'",
- },
- "OAUTH_PASSWORD": {
- "type": "string",
- "title": "User Password - only needed for grant type 'password'",
- },
- "SSL_VERIFY": {
- "type": "boolean",
- "title": "Verify SSL certificates for API requests",
- "default": True,
- },
- "REQUESTS_CA_BUNDLE": {
- "type": "string",
- "title": "Path to the CA Bundle file (.pem)",
- },
- },
- "required": ["CMEM_BASE_URI", "OAUTH_GRANT_TYPE", "OAUTH_CLIENT_ID"],
- "secret": ["OAUTH_CLIENT_SECRET", "OAUTH_PASSWORD"],
- "extra_options": [
- "OAUTH_GRANT_TYPE",
- "OAUTH_USER",
- "OAUTH_PASSWORD",
- "SSL_VERIFY",
- "REQUESTS_CA_BUNDLE",
- ],
- }
-
- def get_schema(self, get_stats=False):
- """Get the schema structure (prefixes, graphs)."""
- schema = dict()
- schema["1"] = {
- "name": "-> Common Prefixes <-",
- "columns": self._get_common_prefixes_schema(),
- }
- schema["2"] = {"name": "-> Graphs <-", "columns": self._get_graphs_schema()}
- # schema.update(self._get_query_schema())
- logger.info(schema.values())
- return schema.values()
-
- def _get_graphs_schema(self):
- """Get a list of readable graph FROM clause strings."""
- self._setup_environment()
- graphs = []
- for graph in get_graphs_list():
- graphs.append("FROM <{}>".format(graph["iri"]))
- return graphs
-
- @staticmethod
- def _get_common_prefixes_schema():
- """Get a list of SPARQL prefix declarations."""
- common_prefixes = [
- "PREFIX rdf: ",
- "PREFIX rdfs: ",
- "PREFIX owl: ",
- "PREFIX schema: ",
- "PREFIX dct: ",
- "PREFIX skos: ",
- ]
- return common_prefixes
-
-
-register(CorporateMemoryQueryRunner)
diff --git a/redash/query_runner/couchbase.py b/redash/query_runner/couchbase.py
index 4a40ad7499..6f264c5333 100644
--- a/redash/query_runner/couchbase.py
+++ b/redash/query_runner/couchbase.py
@@ -1,20 +1,16 @@
import datetime
import logging
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from dateutil.parser import parse
+
+from redash.query_runner import *
+from redash.utils import JSONEncoder, json_dumps, json_loads, parse_human_time
+import json
logger = logging.getLogger(__name__)
try:
- import httplib2 # noqa: F401
import requests
+ import httplib2
except ImportError as e:
logger.error("Failed to import: " + str(e))
@@ -52,7 +48,9 @@ def parse_results(results):
{
"name": column_name,
"friendly_name": column_name,
- "type": TYPES_MAP.get(type(row[key][inner_key]), TYPE_STRING),
+ "type": TYPES_MAP.get(
+ type(row[key][inner_key]), TYPE_STRING
+ ),
}
)
@@ -106,7 +104,7 @@ def enabled(cls):
return True
def test_connection(self):
- self.call_service(self.noop_query, "")
+ result = self.call_service(self.noop_query, "")
def get_buckets(self, query, name_param):
defaultColumns = ["meta().id"]
@@ -119,6 +117,7 @@ def get_buckets(self, query, name_param):
return list(schema.values())
def get_schema(self, get_stats=False):
+
try:
# Try fetch from Analytics
return self.get_buckets(
@@ -154,7 +153,7 @@ def run_query(self, query, user):
rows, columns = parse_results(result.json()["results"])
data = {"columns": columns, "rows": rows}
- return data, None
+ return json_dumps(data), None
@classmethod
def name(cls):
diff --git a/redash/query_runner/csv.py b/redash/query_runner/csv.py
deleted file mode 100644
index 3d3cf61b9c..0000000000
--- a/redash/query_runner/csv.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import io
-import logging
-
-import yaml
-
-from redash.query_runner import BaseQueryRunner, NotSupported, register
-from redash.utils.requests_session import (
- UnacceptableAddressException,
- requests_or_advocate,
-)
-
-logger = logging.getLogger(__name__)
-
-try:
- import numpy as np
- import pandas as pd
-
- enabled = True
-except ImportError:
- enabled = False
-
-
-class CSV(BaseQueryRunner):
- should_annotate_query = False
-
- @classmethod
- def name(cls):
- return "CSV"
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {},
- }
-
- def __init__(self, configuration):
- super(CSV, self).__init__(configuration)
- self.syntax = "yaml"
-
- def test_connection(self):
- pass
-
- def run_query(self, query, user):
- path = ""
- ua = ""
- args = {}
- try:
- args = yaml.safe_load(query)
- path = args["url"]
- args.pop("url", None)
- ua = args["user-agent"]
- args.pop("user-agent", None)
- except Exception:
- pass
-
- try:
- response = requests_or_advocate.get(url=path, headers={"User-agent": ua})
- workbook = pd.read_csv(io.BytesIO(response.content), sep=",", **args)
-
- df = workbook.copy()
- data = {"columns": [], "rows": []}
- conversions = [
- {
- "pandas_type": np.integer,
- "redash_type": "integer",
- },
- {
- "pandas_type": np.inexact,
- "redash_type": "float",
- },
- {
- "pandas_type": np.datetime64,
- "redash_type": "datetime",
- "to_redash": lambda x: x.strftime("%Y-%m-%d %H:%M:%S"),
- },
- {"pandas_type": np.bool_, "redash_type": "boolean"},
- {"pandas_type": np.object_, "redash_type": "string"},
- ]
- labels = []
- for dtype, label in zip(df.dtypes, df.columns):
- for conversion in conversions:
- if issubclass(dtype.type, conversion["pandas_type"]):
- data["columns"].append(
- {"name": label, "friendly_name": label, "type": conversion["redash_type"]}
- )
- labels.append(label)
- func = conversion.get("to_redash")
- if func:
- df[label] = df[label].apply(func)
- break
- data["rows"] = df[labels].replace({np.nan: None}).to_dict(orient="records")
-
- error = None
- except KeyboardInterrupt:
- error = "Query cancelled by user."
- data = None
- except UnacceptableAddressException:
- error = "Can't query private addresses."
- data = None
- except Exception as e:
- error = "Error reading {0}. {1}".format(path, str(e))
- data = None
-
- return data, error
-
- def get_schema(self):
- raise NotSupported()
-
-
-register(CSV)
diff --git a/redash/query_runner/databend.py b/redash/query_runner/databend.py
deleted file mode 100644
index 5e8062061b..0000000000
--- a/redash/query_runner/databend.py
+++ /dev/null
@@ -1,145 +0,0 @@
-try:
- import re
-
- from databend_sqlalchemy import connector
-
- enabled = True
-except ImportError:
- enabled = False
-
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-
-
-class Databend(BaseQueryRunner):
- noop_query = "SELECT 1"
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "host": {"type": "string", "default": "localhost"},
- "port": {"type": "string", "default": "8000"},
- "username": {"type": "string"},
- "password": {"type": "string", "default": ""},
- "database": {"type": "string"},
- "secure": {"type": "boolean", "default": False},
- },
- "order": ["username", "password", "host", "port", "database"],
- "required": ["username", "database"],
- "secret": ["password"],
- }
-
- @classmethod
- def name(cls):
- return "Databend"
-
- @classmethod
- def type(cls):
- return "databend"
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @staticmethod
- def _define_column_type(column_type):
- c = column_type.lower()
- f = re.search(r"^nullable\((.*)\)$", c)
- if f is not None:
- c = f.group(1)
- if c.startswith("int") or c.startswith("uint"):
- return TYPE_INTEGER
- elif c.startswith("float"):
- return TYPE_FLOAT
- elif c == "datetime":
- return TYPE_DATETIME
- elif c == "date":
- return TYPE_DATE
- else:
- return TYPE_STRING
-
- def run_query(self, query, user):
- host = self.configuration.get("host") or "localhost"
- port = self.configuration.get("port") or "8000"
- username = self.configuration.get("username") or "root"
- password = self.configuration.get("password") or ""
- database = self.configuration.get("database") or "default"
- secure = self.configuration.get("secure") or False
- connection = connector.connect(f"databend://{username}:{password}@{host}:{port}/{database}?secure={secure}")
- cursor = connection.cursor()
-
- try:
- cursor.execute(query)
- columns = self.fetch_columns([(i[0], self._define_column_type(i[1])) for i in cursor.description])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
-
- data = {"columns": columns, "rows": rows}
- error = None
- finally:
- connection.close()
-
- return data, error
-
- def get_schema(self, get_stats=False):
- query = """
- SELECT TABLE_SCHEMA,
- TABLE_NAME,
- COLUMN_NAME
- FROM INFORMATION_SCHEMA.COLUMNS
- WHERE TABLE_SCHEMA NOT IN ('information_schema', 'system')
- """
-
- results, error = self.run_query(query, None)
-
- if error is not None:
- self._handle_run_query_error(error)
-
- schema = {}
-
- for row in results["rows"]:
- table_name = "{}.{}".format(row["table_schema"], row["table_name"])
-
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
- schema[table_name]["columns"].append(row["column_name"])
-
- return list(schema.values())
-
- def _get_tables(self):
- query = """
- SELECT TABLE_SCHEMA,
- TABLE_NAME,
- COLUMN_NAME
- FROM INFORMATION_SCHEMA.COLUMNS
- WHERE TABLE_SCHEMA NOT IN ('information_schema', 'system')
- """
-
- results, error = self.run_query(query, None)
-
- if error is not None:
- self._handle_run_query_error(error)
-
- schema = {}
-
- for row in results["rows"]:
- table_name = "{}.{}".format(row["table_schema"], row["table_name"])
-
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
- schema[table_name]["columns"].append(row["column_name"])
-
- return list(schema.values())
-
-
-register(Databend)
diff --git a/redash/query_runner/databricks.py b/redash/query_runner/databricks.py
index 886ba9b8b4..14ce2abb19 100644
--- a/redash/query_runner/databricks.py
+++ b/redash/query_runner/databricks.py
@@ -1,21 +1,17 @@
import datetime
-import logging
-import os
-
-from redash import __version__, statsd_client
from redash.query_runner import (
+ NotSupported,
+ register,
+ BaseSQLQueryRunner,
+ TYPE_STRING,
TYPE_BOOLEAN,
TYPE_DATE,
TYPE_DATETIME,
- TYPE_FLOAT,
TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- NotSupported,
- register,
- split_sql_statements,
+ TYPE_FLOAT,
)
-from redash.settings import cast_int_or_default
+from redash.utils import json_dumps, json_loads
+from redash import __version__
try:
import pyodbc
@@ -24,6 +20,7 @@
except ImportError:
enabled = False
+
TYPES_MAP = {
str: TYPE_STRING,
bool: TYPE_BOOLEAN,
@@ -33,10 +30,6 @@
float: TYPE_FLOAT,
}
-ROW_LIMIT = cast_int_or_default(os.environ.get("DATABRICKS_ROW_LIMIT"), 20000)
-
-logger = logging.getLogger(__name__)
-
def _build_odbc_connection_string(**kwargs):
return ";".join([f"{k}={v}" for k, v in kwargs.items()])
@@ -98,29 +91,33 @@ def run_query(self, query, user):
try:
cursor = self._get_cursor()
- statements = split_sql_statements(query)
- for stmt in statements:
- cursor.execute(stmt)
+ cursor.execute(query)
if cursor.description is not None:
- result_set = cursor.fetchmany(ROW_LIMIT)
- columns = self.fetch_columns([(i[0], TYPES_MAP.get(i[1], TYPE_STRING)) for i in cursor.description])
-
- rows = [dict(zip((column["name"] for column in columns), row)) for row in result_set]
+ data = cursor.fetchall()
+ columns = self.fetch_columns(
+ [
+ (i[0], TYPES_MAP.get(i[1], TYPE_STRING))
+ for i in cursor.description
+ ]
+ )
+
+ rows = [
+ dict(zip((column["name"] for column in columns), row))
+ for row in data
+ ]
data = {"columns": columns, "rows": rows}
-
- if len(result_set) >= ROW_LIMIT and cursor.fetchone() is not None:
- logger.warning("Truncated result set.")
- statsd_client.incr("redash.query_runner.databricks.truncated")
- data["truncated"] = True
+ json_data = json_dumps(data)
error = None
else:
error = None
- data = {
- "columns": [{"name": "result", "type": TYPE_STRING}],
- "rows": [{"result": "No data was returned."}],
- }
+ json_data = json_dumps(
+ {
+ "columns": [{"name": "result", "type": TYPE_STRING}],
+ "rows": [{"result": "No data was returned."}],
+ }
+ )
cursor.close()
except pyodbc.Error as e:
@@ -128,9 +125,9 @@ def run_query(self, query, user):
error = str(e.args[1])
else:
error = str(e)
- data = None
+ json_data = None
- return data, error
+ return json_data, error
def get_schema(self):
raise NotSupported()
@@ -140,38 +137,16 @@ def get_databases(self):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
- first_column_name = results["columns"][0]["name"]
- return [row[first_column_name] for row in results["rows"]]
+ results = json_loads(results)
- def get_database_tables(self, database_name):
- schema = {}
- cursor = self._get_cursor()
-
- cursor.tables(schema=database_name)
-
- for table in cursor:
- table_name = "{}.{}".format(table[1], table[2])
-
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
- return list(schema.values())
+ return [row["namespace"] for row in results["rows"]]
- def get_database_tables_with_columns(self, database_name):
+ def get_database_schema(self, database_name):
schema = {}
cursor = self._get_cursor()
- # load tables first, otherwise tables without columns are not showed
- cursor.tables(schema=database_name)
-
- for table in cursor:
- table_name = "{}.{}".format(table[1], table[2])
-
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
cursor.columns(schema=database_name)
for column in cursor:
@@ -180,14 +155,9 @@ def get_database_tables_with_columns(self, database_name):
if table_name not in schema:
schema[table_name] = {"name": table_name, "columns": []}
- schema[table_name]["columns"].append({"name": column[3], "type": column[5]})
+ schema[table_name]["columns"].append(column[3])
return list(schema.values())
- def get_table_columns(self, database_name, table_name):
- cursor = self._get_cursor()
- cursor.columns(schema=database_name, table=table_name)
- return [{"name": column[3], "type": column[5]} for column in cursor]
-
register(Databricks)
diff --git a/redash/query_runner/db2.py b/redash/query_runner/db2.py
index 88a843af98..ea09ad6da8 100644
--- a/redash/query_runner/db2.py
+++ b/redash/query_runner/db2.py
@@ -1,22 +1,12 @@
import logging
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- InterruptException,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
try:
import select
-
import ibm_db_dbi
types_map = {
@@ -65,7 +55,7 @@ def type(cls):
@classmethod
def enabled(cls):
try:
- import ibm_db # noqa: F401
+ import ibm_db
except ImportError:
return False
@@ -75,7 +65,9 @@ def _get_definitions(self, schema, query):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
if row["TABLE_SCHEMA"] != "public":
@@ -122,27 +114,33 @@ def run_query(self, query, user):
cursor.execute(query)
if cursor.description is not None:
- columns = self.fetch_columns([(i[0], types_map.get(i[1], None)) for i in cursor.description])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
+ columns = self.fetch_columns(
+ [(i[0], types_map.get(i[1], None)) for i in cursor.description]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row))
+ for row in cursor
+ ]
data = {"columns": columns, "rows": rows}
error = None
+ json_data = json_dumps(data)
else:
error = "Query completed but it returned no data."
- data = None
- except (select.error, OSError):
+ json_data = None
+ except (select.error, OSError) as e:
error = "Query interrupted. Please retry."
- data = None
+ json_data = None
except ibm_db_dbi.DatabaseError as e:
error = str(e)
- data = None
+ json_data = None
except (KeyboardInterrupt, InterruptException, JobTimeoutException):
connection.cancel()
raise
finally:
connection.close()
- return data, error
+ return json_data, error
register(DB2)
diff --git a/redash/query_runner/dgraph.py b/redash/query_runner/dgraph.py
index 302a474b91..f48f8d91f3 100644
--- a/redash/query_runner/dgraph.py
+++ b/redash/query_runner/dgraph.py
@@ -8,17 +8,18 @@
enabled = False
from redash.query_runner import BaseQueryRunner, register
+from redash.utils import json_dumps
def reduce_item(reduced_item, key, value):
"""From https://github.com/vinay20045/json-to-csv"""
# Reduction Condition 1
- if isinstance(value, list):
+ if type(value) is list:
for i, sub_item in enumerate(value):
reduce_item(reduced_item, "{}.{}".format(key, i), sub_item)
# Reduction Condition 2
- elif isinstance(value, dict):
+ elif type(value) is dict:
sub_keys = value.keys()
for sub_key in sub_keys:
reduce_item(reduced_item, "{}.{}".format(key, sub_key), value[sub_key])
@@ -80,7 +81,8 @@ def run_dgraph_query_raw(self, query):
client_stub.close()
def run_query(self, query, user):
- data = None
+
+ json_data = None
error = None
try:
@@ -104,14 +106,18 @@ def run_query(self, query, user):
header = list(set(header))
- columns = [{"name": c, "friendly_name": c, "type": "string"} for c in header]
+ columns = [
+ {"name": c, "friendly_name": c, "type": "string"} for c in header
+ ]
# finally, assemble both the columns and data
data = {"columns": columns, "rows": processed_data}
+
+ json_data = json_dumps(data)
except Exception as e:
error = e
- return data, error
+ return json_data, error
def get_schema(self, get_stats=False):
"""Queries Dgraph for all the predicates, their types, their tokenizers, etc.
diff --git a/redash/query_runner/drill.py b/redash/query_runner/drill.py
index a011e8590c..e843e68384 100644
--- a/redash/query_runner/drill.py
+++ b/redash/query_runner/drill.py
@@ -1,18 +1,19 @@
-import logging
import os
+import logging
import re
from dateutil import parser
from redash.query_runner import (
- TYPE_BOOLEAN,
+ BaseHTTPQueryRunner,
+ register,
TYPE_DATETIME,
- TYPE_FLOAT,
TYPE_INTEGER,
- BaseHTTPQueryRunner,
+ TYPE_FLOAT,
+ TYPE_BOOLEAN,
guess_type,
- register,
)
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
@@ -50,7 +51,9 @@ def parse_response(data):
types = {}
for c in cols:
- columns.append({"name": c, "type": guess_type(first_row[c]), "friendly_name": c})
+ columns.append(
+ {"name": c, "type": guess_type(first_row[c]), "friendly_name": c}
+ )
for col in columns:
types[col["name"]] = col["type"]
@@ -93,13 +96,18 @@ def run_query(self, query, user):
payload = {"queryType": "SQL", "query": query}
- response, error = self.get_response(drill_url, http_method="post", json=payload)
+ response, error = self.get_response(
+ drill_url, http_method="post", json=payload
+ )
if error is not None:
return None, error
- return parse_response(response.json()), None
+ results = parse_response(response.json())
+
+ return json_dumps(results), None
def get_schema(self, get_stats=False):
+
query = """
SELECT DISTINCT
TABLE_SCHEMA,
@@ -127,7 +135,9 @@ def get_schema(self, get_stats=False):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
schema = {}
diff --git a/redash/query_runner/druid.py b/redash/query_runner/druid.py
index b20a01953d..0790d5e1e6 100644
--- a/redash/query_runner/druid.py
+++ b/redash/query_runner/druid.py
@@ -5,13 +5,9 @@
except ImportError:
enabled = False
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from redash.query_runner import BaseQueryRunner, register
+from redash.query_runner import TYPE_STRING, TYPE_INTEGER, TYPE_BOOLEAN
+from redash.utils import json_dumps, json_loads
TYPES_MAP = {1: TYPE_STRING, 2: TYPE_INTEGER, 3: TYPE_BOOLEAN}
@@ -53,15 +49,21 @@ def run_query(self, query, user):
try:
cursor.execute(query)
- columns = self.fetch_columns([(i[0], TYPES_MAP.get(i[1], None)) for i in cursor.description])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
+ columns = self.fetch_columns(
+ [(i[0], TYPES_MAP.get(i[1], None)) for i in cursor.description]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row)) for row in cursor
+ ]
data = {"columns": columns, "rows": rows}
error = None
+ json_data = json_dumps(data)
+ print(json_data)
finally:
connection.close()
- return data, error
+ return json_data, error
def get_schema(self, get_stats=False):
query = """
@@ -75,9 +77,10 @@ def get_schema(self, get_stats=False):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
schema = {}
+ results = json_loads(results)
for row in results["rows"]:
table_name = "{}.{}".format(row["TABLE_SCHEMA"], row["TABLE_NAME"])
diff --git a/redash/query_runner/dynamodb_sql.py b/redash/query_runner/dynamodb_sql.py
new file mode 100644
index 0000000000..fa99c0ee12
--- /dev/null
+++ b/redash/query_runner/dynamodb_sql.py
@@ -0,0 +1,150 @@
+import logging
+import sys
+
+from redash.query_runner import *
+from redash.utils import json_dumps
+
+logger = logging.getLogger(__name__)
+
+try:
+ from dql import Engine, FragmentEngine
+ from dynamo3 import DynamoDBError
+ from pyparsing import ParseException
+
+ enabled = True
+except ImportError as e:
+ enabled = False
+
+types_map = {
+ "UNICODE": TYPE_INTEGER,
+ "TINYINT": TYPE_INTEGER,
+ "SMALLINT": TYPE_INTEGER,
+ "INT": TYPE_INTEGER,
+ "DOUBLE": TYPE_FLOAT,
+ "DECIMAL": TYPE_FLOAT,
+ "FLOAT": TYPE_FLOAT,
+ "REAL": TYPE_FLOAT,
+ "BOOLEAN": TYPE_BOOLEAN,
+ "TIMESTAMP": TYPE_DATETIME,
+ "DATE": TYPE_DATETIME,
+ "CHAR": TYPE_STRING,
+ "STRING": TYPE_STRING,
+ "VARCHAR": TYPE_STRING,
+}
+
+
+class DynamoDBSQL(BaseSQLQueryRunner):
+ should_annotate_query = False
+
+ @classmethod
+ def configuration_schema(cls):
+ return {
+ "type": "object",
+ "properties": {
+ "region": {"type": "string", "default": "us-east-1"},
+ "access_key": {"type": "string"},
+ "secret_key": {"type": "string"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ },
+ "required": ["access_key", "secret_key"],
+ "secret": ["secret_key"],
+ }
+
+ def test_connection(self):
+ engine = self._connect()
+ list(engine.connection.list_tables())
+
+ @classmethod
+ def type(cls):
+ return "dynamodb_sql"
+
+ @classmethod
+ def name(cls):
+ return "DynamoDB (with DQL)"
+
+ def _connect(self):
+ engine = FragmentEngine()
+ config = self.configuration.to_dict()
+
+ if not config.get("region"):
+ config["region"] = "us-east-1"
+
+ if config.get("host") == "":
+ config["host"] = None
+
+ engine.connect(**config)
+
+ return engine
+
+ def _get_tables(self, schema):
+ engine = self._connect()
+
+ # We can't use describe_all because sometimes a user might give List permission
+ # for * (all tables), but describe permission only for some of them.
+ tables = engine.connection.list_tables()
+ for table_name in tables:
+ try:
+ table = engine.describe(table_name, True)
+ schema[table.name] = {
+ "name": table.name,
+ "columns": list(table.attrs.keys()),
+ }
+ except DynamoDBError:
+ pass
+
+ def run_query(self, query, user):
+ engine = None
+ try:
+ engine = self._connect()
+
+ if not query.endswith(";"):
+ query = query + ";"
+
+ result = engine.execute(query)
+
+ columns = []
+ rows = []
+
+ # When running a count query it returns the value as a string, in which case
+ # we transform it into a dictionary to be the same as regular queries.
+ if isinstance(result, str):
+ # when count < scanned_count, dql returns a string with number of rows scanned
+ value = result.split(" (")[0]
+ if value:
+ value = int(value)
+ result = [{"value": value}]
+
+ for item in result:
+ if not columns:
+ for k, v in item.items():
+ columns.append(
+ {
+ "name": k,
+ "friendly_name": k,
+ "type": types_map.get(str(type(v)).upper(), None),
+ }
+ )
+ rows.append(item)
+
+ data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
+ error = None
+ except ParseException as e:
+ error = "Error parsing query at line {} (column {}):\n{}".format(
+ e.lineno, e.column, e.line
+ )
+ json_data = None
+ except (KeyboardInterrupt, JobTimeoutException):
+ if engine and engine.connection:
+ engine.connection.cancel()
+ raise
+
+ return json_data, error
+
+
+register(DynamoDBSQL)
diff --git a/redash/query_runner/e6data.py b/redash/query_runner/e6data.py
deleted file mode 100644
index 0087c22e1a..0000000000
--- a/redash/query_runner/e6data.py
+++ /dev/null
@@ -1,152 +0,0 @@
-import logging
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-
-try:
- from e6data_python_connector import Connection
-
- enabled = True
-
-except ImportError:
- enabled = False
-
-
-logger = logging.getLogger(__name__)
-
-E6DATA_TYPES_MAPPING = {
- "INT": TYPE_INTEGER,
- "BYTE": TYPE_INTEGER,
- "INTEGER": TYPE_INTEGER,
- "LONG": TYPE_INTEGER,
- "SHORT": TYPE_INTEGER,
- "FLOAT": TYPE_FLOAT,
- "DOUBLE": TYPE_FLOAT,
- "STRING": TYPE_STRING,
- "DATETIME": TYPE_DATETIME,
- "BINARY": TYPE_INTEGER,
- "ARRAY": TYPE_STRING,
- "MAP": TYPE_STRING,
- "STRUCT": TYPE_STRING,
- "UNION_TYPE": TYPE_STRING,
- "DECIMAL_TYPE": TYPE_FLOAT,
- "DATE": TYPE_DATE,
- "INT96": TYPE_INTEGER,
- "BOOLEAN": TYPE_BOOLEAN,
- "CHAR": TYPE_STRING,
-}
-
-
-class e6data(BaseQueryRunner):
- limit_query = " LIMIT 1000"
-
- should_annotate_query = False
-
- def __init__(self, configuration):
- super().__init__(configuration)
- self.connection = Connection(
- host=self.configuration.get("host"),
- port=self.configuration.get("port"),
- username=self.configuration.get("username"),
- database=self.configuration.get("database"),
- password=self.configuration.get("password"),
- )
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "host": {"type": "string"},
- "port": {"type": "number"},
- "username": {"type": "string"},
- "password": {"type": "string"},
- "catalog": {"type": "string"},
- "database": {"type": "string"},
- },
- "order": [
- "host",
- "port",
- "username",
- "password",
- "catalog",
- "database",
- ],
- "required": ["host", "port", "username", "password", "catalog", "database"],
- "secret": ["password"],
- }
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def type(cls):
- return "e6data"
-
- def run_query(self, query, user):
- cursor = None
- try:
- cursor = self.connection.cursor(catalog_name=self.configuration.get("catalog"))
- cursor.execute(query)
- results = cursor.fetchall()
- description = cursor.description
- columns = []
- for c in description:
- column_name, column_type = c[0], E6DATA_TYPES_MAPPING.get(c[1], None)
- columns.append({"name": column_name, "type": column_type})
- rows = [dict(zip([c["name"] for c in columns], r)) for r in results]
- data = {"columns": columns, "rows": rows}
- error = None
-
- except Exception as error:
- logger.debug(error)
- data = None
- finally:
- if cursor is not None:
- cursor.clear()
- cursor.close()
-
- return data, error
-
- def test_connection(self):
- self.noop_query = "SELECT 1"
-
- data, error = self.run_query(self.noop_query, None)
-
- if error is not None:
- raise Exception(error)
-
- def get_schema(self, get_stats=False):
- tables = self.connection.get_tables(self.configuration.get("catalog"), self.configuration.get("database"))
-
- schema = list()
-
- for table_name in tables:
- columns = self.connection.get_columns(
- self.configuration.get("catalog"),
- self.configuration.get("database"),
- table_name,
- )
- columns_with_type = []
-
- for column in columns:
- redash_type = E6DATA_TYPES_MAPPING.get(column["fieldType"], None)
- columns_with_type.append({"name": column["fieldName"], "type": redash_type})
-
- table_schema = {"name": table_name, "columns": columns_with_type}
-
- schema.append(table_schema)
-
- return schema
-
-
-register(e6data)
diff --git a/redash/query_runner/elasticsearch.py b/redash/query_runner/elasticsearch.py
index b3cc560bbc..5658b5fb50 100644
--- a/redash/query_runner/elasticsearch.py
+++ b/redash/query_runner/elasticsearch.py
@@ -1,22 +1,14 @@
import logging
-import urllib.error
-import urllib.parse
+import sys
import urllib.request
+import urllib.parse
+import urllib.error
import requests
from requests.auth import HTTPBasicAuth
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- JobTimeoutException,
- register,
-)
-from redash.utils import json_loads
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
try:
import http.client as http_client
@@ -52,7 +44,6 @@
class BaseElasticSearch(BaseQueryRunner):
should_annotate_query = False
DEBUG_ENABLED = False
- deprecated = True
@classmethod
def configuration_schema(cls):
@@ -65,6 +56,12 @@ def configuration_schema(cls):
"type": "string",
"title": "Basic Auth Password",
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"order": ["server", "basic_auth_user", "basic_auth_password"],
"secret": ["basic_auth_password"],
@@ -91,8 +88,8 @@ def __init__(self, configuration):
logger.setLevel(logging.DEBUG)
- self.server_url = self.configuration.get("server", "")
- if self.server_url and self.server_url[-1] == "/":
+ self.server_url = self.configuration["server"]
+ if self.server_url[-1] == "/":
self.server_url = self.server_url[:-1]
basic_auth_user = self.configuration.get("basic_auth_user", None)
@@ -111,7 +108,9 @@ def _get_mappings(self, url):
mappings = r.json()
except requests.HTTPError as e:
logger.exception(e)
- error = "Failed to execute query. Return Code: {0} Reason: {1}".format(r.status_code, r.text)
+ error = "Failed to execute query. Return Code: {0} Reason: {1}".format(
+ r.status_code, r.text
+ )
mappings = None
except requests.exceptions.RequestException as e:
logger.exception(e)
@@ -129,17 +128,19 @@ def _get_query_mappings(self, url):
for index_name in mappings_data:
index_mappings = mappings_data[index_name]
for m in index_mappings.get("mappings", {}):
- if not isinstance(index_mappings["mappings"][m], dict):
- continue
if "properties" not in index_mappings["mappings"][m]:
continue
for property_name in index_mappings["mappings"][m]["properties"]:
- property_data = index_mappings["mappings"][m]["properties"][property_name]
+ property_data = index_mappings["mappings"][m]["properties"][
+ property_name
+ ]
if property_name not in mappings:
property_type = property_data.get("type", None)
if property_type:
if property_type in ELASTICSEARCH_TYPES_MAPPING:
- mappings[property_name] = ELASTICSEARCH_TYPES_MAPPING[property_type]
+ mappings[property_name] = ELASTICSEARCH_TYPES_MAPPING[
+ property_type
+ ]
else:
mappings[property_name] = TYPE_STRING
# raise Exception("Unknown property type: {0}".format(property_type))
@@ -148,7 +149,8 @@ def _get_query_mappings(self, url):
def get_schema(self, *args, **kwargs):
def parse_doc(doc, path=None):
- """Recursively parse a doc type dictionary"""
+ """Recursively parse a doc type dictionary
+ """
path = path or []
result = []
for field, description in doc["properties"].items():
@@ -177,8 +179,12 @@ def parse_doc(doc, path=None):
schema[name]["columns"] = sorted(set(columns))
return list(schema.values())
- def _parse_results(self, mappings, result_fields, raw_result, result_columns, result_rows): # noqa: C901
- def add_column_if_needed(mappings, column_name, friendly_name, result_columns, result_columns_index):
+ def _parse_results(
+ self, mappings, result_fields, raw_result, result_columns, result_rows
+ ):
+ def add_column_if_needed(
+ mappings, column_name, friendly_name, result_columns, result_columns_index
+ ):
if friendly_name not in result_columns_index:
result_columns.append(
{
@@ -200,10 +206,14 @@ def collect_value(mappings, row, key, value, type):
return
mappings[key] = type
- add_column_if_needed(mappings, key, key, result_columns, result_columns_index)
+ add_column_if_needed(
+ mappings, key, key, result_columns, result_columns_index
+ )
row[key] = value
- def collect_aggregations(mappings, rows, parent_key, data, row, result_columns, result_columns_index):
+ def collect_aggregations(
+ mappings, rows, parent_key, data, row, result_columns, result_columns_index
+ ):
if isinstance(data, dict):
for key, value in data.items():
val = collect_aggregations(
@@ -264,7 +274,9 @@ def collect_aggregations(mappings, rows, parent_key, data, row, result_columns,
"string",
)
else:
- collect_value(mappings, result_row, parent_key, value["key"], "string")
+ collect_value(
+ mappings, result_row, parent_key, value["key"], "string"
+ )
return None
@@ -284,7 +296,9 @@ def collect_aggregations(mappings, rows, parent_key, data, row, result_columns,
elif "aggregations" in raw_result:
if result_fields:
for field in result_fields:
- add_column_if_needed(mappings, field, field, result_columns, result_columns_index)
+ add_column_if_needed(
+ mappings, field, field, result_columns, result_columns_index
+ )
for key, data in raw_result["aggregations"].items():
collect_aggregations(
@@ -302,7 +316,9 @@ def collect_aggregations(mappings, rows, parent_key, data, row, result_columns,
elif "hits" in raw_result and "hits" in raw_result["hits"]:
if result_fields:
for field in result_fields:
- add_column_if_needed(mappings, field, field, result_columns, result_columns_index)
+ add_column_if_needed(
+ mappings, field, field, result_columns, result_columns_index
+ )
for h in raw_result["hits"]["hits"]:
row = {}
@@ -312,22 +328,36 @@ def collect_aggregations(mappings, rows, parent_key, data, row, result_columns,
if result_fields and column not in result_fields_index:
continue
- add_column_if_needed(mappings, column, column, result_columns, result_columns_index)
+ add_column_if_needed(
+ mappings, column, column, result_columns, result_columns_index
+ )
value = h[column_name][column]
- row[column] = value[0] if isinstance(value, list) and len(value) == 1 else value
+ row[column] = (
+ value[0]
+ if isinstance(value, list) and len(value) == 1
+ else value
+ )
result_rows.append(row)
else:
- raise Exception("Redash failed to parse the results it got from Elasticsearch.")
+ raise Exception(
+ "Redash failed to parse the results it got from Elasticsearch."
+ )
def test_connection(self):
try:
- r = requests.get("{0}/_cluster/health".format(self.server_url), auth=self.auth)
+ r = requests.get(
+ "{0}/_cluster/health".format(self.server_url), auth=self.auth
+ )
r.raise_for_status()
except requests.HTTPError as e:
logger.exception(e)
- raise Exception("Failed to execute query. Return Code: {0} Reason: {1}".format(r.status_code, r.text))
+ raise Exception(
+ "Failed to execute query. Return Code: {0} Reason: {1}".format(
+ r.status_code, r.text
+ )
+ )
except requests.exceptions.RequestException as e:
logger.exception(e)
raise Exception("Connection refused")
@@ -338,14 +368,18 @@ class Kibana(BaseElasticSearch):
def enabled(cls):
return True
- def _execute_simple_query(self, url, auth, _from, mappings, result_fields, result_columns, result_rows):
+ def _execute_simple_query(
+ self, url, auth, _from, mappings, result_fields, result_columns, result_rows
+ ):
url += "&from={0}".format(_from)
r = requests.get(url, auth=self.auth)
r.raise_for_status()
raw_result = r.json()
- self._parse_results(mappings, result_fields, raw_result, result_columns, result_rows)
+ self._parse_results(
+ mappings, result_fields, raw_result, result_columns, result_rows
+ )
total = raw_result["hits"]["total"]
result_size = len(raw_result["hits"]["hits"])
@@ -392,7 +426,7 @@ def run_query(self, query, user):
_from = 0
while True:
query_size = size if limit >= (_from + size) else (limit - _from)
- self._execute_simple_query(
+ total = self._execute_simple_query(
url + "&size={0}".format(query_size),
self.auth,
_from,
@@ -408,18 +442,19 @@ def run_query(self, query, user):
# TODO: Handle complete ElasticSearch queries (JSON based sent over HTTP POST)
raise Exception("Advanced queries are not supported")
- data = {"columns": result_columns, "rows": result_rows}
+ json_data = json_dumps({"columns": result_columns, "rows": result_rows})
except requests.HTTPError as e:
logger.exception(e)
- r = e.response
- error = "Failed to execute query. Return Code: {0} Reason: {1}".format(r.status_code, r.text)
- data = None
+ error = "Failed to execute query. Return Code: {0} Reason: {1}".format(
+ r.status_code, r.text
+ )
+ json_data = None
except requests.exceptions.RequestException as e:
logger.exception(e)
error = "Connection refused"
- data = None
+ json_data = None
- return data, error
+ return json_data, error
class ElasticSearch(BaseElasticSearch):
@@ -460,22 +495,26 @@ def run_query(self, query, user):
result_columns = []
result_rows = []
- self._parse_results(mappings, result_fields, r.json(), result_columns, result_rows)
+ self._parse_results(
+ mappings, result_fields, r.json(), result_columns, result_rows
+ )
- data = {"columns": result_columns, "rows": result_rows}
- except (KeyboardInterrupt, JobTimeoutException) as e:
+ json_data = json_dumps({"columns": result_columns, "rows": result_rows})
+ except (KeyboardInterrupt, JobTimeoutException):
logger.exception(e)
raise
except requests.HTTPError as e:
logger.exception(e)
- error = "Failed to execute query. Return Code: {0} Reason: {1}".format(r.status_code, r.text)
- data = None
+ error = "Failed to execute query. Return Code: {0} Reason: {1}".format(
+ r.status_code, r.text
+ )
+ json_data = None
except requests.exceptions.RequestException as e:
logger.exception(e)
error = "Connection refused"
- data = None
+ json_data = None
- return data, error
+ return json_data, error
register(Kibana)
diff --git a/redash/query_runner/elasticsearch2.py b/redash/query_runner/elasticsearch2.py
deleted file mode 100644
index 3570d10b65..0000000000
--- a/redash/query_runner/elasticsearch2.py
+++ /dev/null
@@ -1,308 +0,0 @@
-import json
-import logging
-from typing import Optional, Tuple
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseHTTPQueryRunner,
- register,
-)
-
-logger = logging.getLogger(__name__)
-
-ELASTICSEARCH_TYPES_MAPPING = {
- "integer": TYPE_INTEGER,
- "long": TYPE_INTEGER,
- "float": TYPE_FLOAT,
- "double": TYPE_FLOAT,
- "boolean": TYPE_BOOLEAN,
- "string": TYPE_STRING,
- "date": TYPE_DATE,
- "object": TYPE_STRING,
-}
-
-
-TYPES_MAP = {
- str: TYPE_STRING,
- int: TYPE_INTEGER,
- float: TYPE_FLOAT,
- bool: TYPE_BOOLEAN,
-}
-
-
-class ElasticSearch2(BaseHTTPQueryRunner):
- should_annotate_query = False
-
- @classmethod
- def name(cls):
- return "Elasticsearch"
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.syntax = "json"
-
- def get_response(self, url, auth=None, http_method="get", **kwargs):
- url = "{}{}".format(self.configuration["server"], url)
- headers = kwargs.pop("headers", {})
- headers["Accept"] = "application/json"
- return super().get_response(url, auth, http_method, headers=headers, **kwargs)
-
- def test_connection(self):
- _, error = self.get_response("/_cluster/health")
- if error is not None:
- raise Exception(error)
-
- def run_query(self, query, user):
- query, url, result_fields = self._build_query(query)
- response, error = self.get_response(url, http_method="post", json=query)
- query_results = response.json()
- data = self._parse_results(result_fields, query_results)
- error = None
- return data, error
-
- def _build_query(self, query: str) -> Tuple[dict, str, Optional[list]]:
- query = json.loads(query)
- index_name = query.pop("index", "")
- result_fields = query.pop("result_fields", None)
- url = "/{}/_search".format(index_name)
- return query, url, result_fields
-
- @classmethod
- def _parse_mappings(cls, mappings_data: dict):
- mappings = {}
-
- def _parse_properties(prefix: str, properties: dict):
- for property_name, property_data in properties.items():
- if property_name not in mappings:
- property_type = property_data.get("type", None)
- nested_properties = property_data.get("properties", None)
- if property_type:
- mappings[index_name][prefix + property_name] = ELASTICSEARCH_TYPES_MAPPING.get(
- property_type, TYPE_STRING
- )
- elif nested_properties:
- new_prefix = prefix + property_name + "."
- _parse_properties(new_prefix, nested_properties)
-
- for index_name in mappings_data:
- mappings[index_name] = {}
- index_mappings = mappings_data[index_name]
- try:
- for m in index_mappings.get("mappings", {}):
- _parse_properties("", index_mappings["mappings"][m]["properties"])
- except KeyError:
- _parse_properties("", index_mappings["mappings"]["properties"])
-
- return mappings
-
- def get_mappings(self):
- response, error = self.get_response("/_mappings")
- return self._parse_mappings(response.json())
-
- def get_schema(self, *args, **kwargs):
- schema = {}
- for name, columns in self.get_mappings().items():
- schema[name] = {"name": name, "columns": list(columns.keys())}
- return list(schema.values())
-
- @classmethod
- def _parse_results(cls, result_fields, raw_result): # noqa: C901
- result_columns = []
- result_rows = []
- result_columns_index = {c["name"]: c for c in result_columns}
- result_fields_index = {}
-
- def add_column_if_needed(column_name, value=None):
- if column_name not in result_columns_index:
- result_columns.append(
- {
- "name": column_name,
- "friendly_name": column_name,
- "type": TYPES_MAP.get(type(value), TYPE_STRING),
- }
- )
- result_columns_index[column_name] = result_columns[-1]
-
- def get_row(rows, row):
- if row is None:
- row = {}
- rows.append(row)
- return row
-
- def collect_value(row, key, value):
- if result_fields and key not in result_fields_index:
- return
-
- add_column_if_needed(key, value)
- row[key] = value
-
- def parse_bucket_to_row(data, row, agg_key):
- sub_agg_key = ""
- for key, item in data.items():
- if key == "key_as_string":
- continue
- if key == "key":
- if "key_as_string" in data:
- collect_value(row, agg_key, data["key_as_string"])
- else:
- collect_value(row, agg_key, data["key"])
- continue
-
- if isinstance(item, (str, int, float)):
- collect_value(row, agg_key + "." + key, item)
- elif isinstance(item, dict):
- if "buckets" not in item:
- for sub_key, sub_item in item.items():
- collect_value(
- row,
- agg_key + "." + key + "." + sub_key,
- sub_item,
- )
- else:
- sub_agg_key = key
-
- return sub_agg_key
-
- def parse_buckets_list(rows, parent_key, data, row, depth):
- if len(rows) > 0 and depth == 0:
- row = rows.pop()
-
- for value in data:
- row = row.copy()
- sub_agg_key = parse_bucket_to_row(value, row, parent_key)
-
- if sub_agg_key == "":
- rows.append(row)
- else:
- depth += 1
- parse_buckets_list(rows, sub_agg_key, value[sub_agg_key]["buckets"], row, depth)
-
- def collect_aggregations(rows, parent_key, data, row, depth):
- row = get_row(rows, row)
- parse_bucket_to_row(data, row, parent_key)
-
- if "buckets" in data:
- parse_buckets_list(rows, parent_key, data["buckets"], row, depth)
-
- return None
-
- def get_flatten_results(dd, separator=".", prefix=""):
- if isinstance(dd, dict):
- return {
- prefix + separator + k if prefix else k: v
- for kk, vv in dd.items()
- for k, v in get_flatten_results(vv, separator, kk).items()
- }
- elif isinstance(dd, list) and len(dd) == 1:
- return {prefix: dd[0]}
- else:
- return {prefix: dd}
-
- if result_fields:
- for r in result_fields:
- result_fields_index[r] = None
-
- if "error" in raw_result:
- error = raw_result["error"]
- if len(error) > 10240:
- error = error[:10240] + "... continues"
-
- raise Exception(error)
- elif "aggregations" in raw_result:
- for key, data in raw_result["aggregations"].items():
- collect_aggregations(result_rows, key, data, None, 0)
-
- elif "hits" in raw_result and "hits" in raw_result["hits"]:
- for h in raw_result["hits"]["hits"]:
- row = {}
-
- fields_parameter_name = "_source" if "_source" in h else "fields"
- for column in h[fields_parameter_name]:
- if result_fields and column not in result_fields_index:
- continue
-
- unested_results = get_flatten_results({column: h[fields_parameter_name][column]})
-
- for column_name, value in unested_results.items():
- add_column_if_needed(column_name, value=value)
- row[column_name] = value
-
- result_rows.append(row)
- else:
- raise Exception("Redash failed to parse the results it got from Elasticsearch.")
-
- return {"columns": result_columns, "rows": result_rows}
-
-
-class OpenDistroSQLElasticSearch(ElasticSearch2):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.syntax = "sql"
-
- def _build_query(self, query: str) -> Tuple[dict, str, Optional[list]]:
- sql_query = {"query": query}
- sql_query_url = "/_opendistro/_sql"
- return sql_query, sql_query_url, None
-
- @classmethod
- def name(cls):
- return "Open Distro SQL Elasticsearch"
-
- @classmethod
- def type(cls):
- return "elasticsearch2_OpenDistroSQLElasticSearch"
-
-
-class XPackSQLElasticSearch(ElasticSearch2):
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.syntax = "sql"
-
- def _build_query(self, query: str) -> Tuple[dict, str, Optional[list]]:
- sql_query = {"query": query}
- sql_query_url = "/_xpack/sql"
- return sql_query, sql_query_url, None
-
- @classmethod
- def _parse_results(cls, result_fields, raw_result):
- error = raw_result.get("error")
- if error:
- raise Exception(error)
-
- rv = {
- "columns": [
- {
- "name": c["name"],
- "friendly_name": c["name"],
- "type": ELASTICSEARCH_TYPES_MAPPING.get(c["type"], "string"),
- }
- for c in raw_result["columns"]
- ],
- "rows": [],
- }
- query_results_rows = raw_result["rows"]
-
- for query_results_row in query_results_rows:
- result_row = dict()
- for column, column_value in zip(rv["columns"], query_results_row):
- result_row[column["name"]] = column_value
- rv["rows"].append(result_row)
-
- return rv
-
- @classmethod
- def name(cls):
- return "X-Pack SQL Elasticsearch"
-
- @classmethod
- def type(cls):
- return "elasticsearch2_XPackSQLElasticSearch"
-
-
-register(ElasticSearch2)
-register(OpenDistroSQLElasticSearch)
-register(XPackSQLElasticSearch)
diff --git a/redash/query_runner/exasol.py b/redash/query_runner/exasol.py
index a5fdd7df13..5bdbae5fc5 100644
--- a/redash/query_runner/exasol.py
+++ b/redash/query_runner/exasol.py
@@ -1,14 +1,7 @@
import datetime
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
def _exasol_type_mapper(val, data_type):
@@ -102,19 +95,21 @@ def run_query(self, query, user):
try:
statement = connection.execute(query)
columns = [
- {"name": n, "friendly_name": n, "type": _type_mapper(t)} for (n, t) in statement.columns().items()
+ {"name": n, "friendly_name": n, "type": _type_mapper(t)}
+ for (n, t) in statement.columns().items()
]
cnames = statement.column_names()
rows = [dict(zip(cnames, row)) for row in statement]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
finally:
if statement is not None:
statement.close()
connection.close()
- return data, error
+ return json_data, error
def get_schema(self, get_stats=False):
query = """
@@ -131,7 +126,7 @@ def get_schema(self, get_stats=False):
statement = connection.execute(query)
result = {}
- for schema, table_name, column in statement:
+ for (schema, table_name, column) in statement:
table_name_with_schema = "%s.%s" % (schema, table_name)
if table_name_with_schema not in result:
diff --git a/redash/query_runner/excel.py b/redash/query_runner/excel.py
deleted file mode 100644
index 488632e022..0000000000
--- a/redash/query_runner/excel.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import logging
-
-import yaml
-
-from redash.query_runner import BaseQueryRunner, NotSupported, register
-from redash.utils.requests_session import (
- UnacceptableAddressException,
- requests_or_advocate,
-)
-
-logger = logging.getLogger(__name__)
-
-try:
- import numpy as np
- import openpyxl # noqa: F401
- import pandas as pd
- import xlrd # noqa: F401
-
- enabled = True
-except ImportError:
- enabled = False
-
-
-class Excel(BaseQueryRunner):
- should_annotate_query = False
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {},
- }
-
- def __init__(self, configuration):
- super(Excel, self).__init__(configuration)
- self.syntax = "yaml"
-
- def test_connection(self):
- pass
-
- def run_query(self, query, user):
- path = ""
- ua = ""
- args = {}
- try:
- args = yaml.safe_load(query)
- path = args["url"]
- args.pop("url", None)
- ua = args["user-agent"]
- args.pop("user-agent", None)
-
- except Exception:
- pass
-
- try:
- response = requests_or_advocate.get(url=path, headers={"User-agent": ua})
- workbook = pd.read_excel(response.content, **args)
-
- df = workbook.copy()
- data = {"columns": [], "rows": []}
- conversions = [
- {
- "pandas_type": np.integer,
- "redash_type": "integer",
- },
- {
- "pandas_type": np.inexact,
- "redash_type": "float",
- },
- {
- "pandas_type": np.datetime64,
- "redash_type": "datetime",
- "to_redash": lambda x: x.strftime("%Y-%m-%d %H:%M:%S"),
- },
- {"pandas_type": np.bool_, "redash_type": "boolean"},
- {"pandas_type": np.object_, "redash_type": "string"},
- ]
- labels = []
- for dtype, label in zip(df.dtypes, df.columns):
- for conversion in conversions:
- if issubclass(dtype.type, conversion["pandas_type"]):
- data["columns"].append(
- {"name": label, "friendly_name": label, "type": conversion["redash_type"]}
- )
- labels.append(label)
- func = conversion.get("to_redash")
- if func:
- df[label] = df[label].apply(func)
- break
- data["rows"] = df[labels].replace({np.nan: None}).to_dict(orient="records")
-
- error = None
- except KeyboardInterrupt:
- error = "Query cancelled by user."
- data = None
- except UnacceptableAddressException:
- error = "Can't query private addresses."
- data = None
- except Exception as e:
- error = "Error reading {0}. {1}".format(path, str(e))
- data = None
-
- return data, error
-
- def get_schema(self):
- raise NotSupported()
-
-
-register(Excel)
diff --git a/redash/query_runner/files/rds-combined-ca-bundle.pem b/redash/query_runner/files/rds-combined-ca-bundle.pem
index de68d41a0f..fe486180f2 100644
--- a/redash/query_runner/files/rds-combined-ca-bundle.pem
+++ b/redash/query_runner/files/rds-combined-ca-bundle.pem
@@ -1,50 +1,530 @@
-----BEGIN CERTIFICATE-----
-MIIEEjCCAvqgAwIBAgIJAM2ZN/+nPi27MA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD
+MIID9DCCAtygAwIBAgIBQjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUwOTExMzFaFw0y
+MDAzMDUwOTExMzFaMIGKMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEbMBkGA1UEAwwSQW1hem9uIFJE
+UyBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuD8nrZ8V
+u+VA8yVlUipCZIKPTDcOILYpUe8Tct0YeQQr0uyl018StdBsa3CjBgvwpDRq1HgF
+Ji2N3+39+shCNspQeE6aYU+BHXhKhIIStt3r7gl/4NqYiDDMWKHxHq0nsGDFfArf
+AOcjZdJagOMqb3fF46flc8k2E7THTm9Sz4L7RY1WdABMuurpICLFE3oHcGdapOb9
+T53pQR+xpHW9atkcf3pf7gbO0rlKVSIoUenBlZipUlp1VZl/OD/E+TtRhDDNdI2J
+P/DSMM3aEsq6ZQkfbz/Ilml+Lx3tJYXUDmp+ZjzMPLk/+3beT8EhrwtcG3VPpvwp
+BIOqsqVVTvw/CwIDAQABo2MwYTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zAdBgNVHQ4EFgQUTgLurD72FchM7Sz1BcGPnIQISYMwHwYDVR0jBBgwFoAU
+TgLurD72FchM7Sz1BcGPnIQISYMwDQYJKoZIhvcNAQEFBQADggEBAHZcgIio8pAm
+MjHD5cl6wKjXxScXKtXygWH2BoDMYBJF9yfyKO2jEFxYKbHePpnXB1R04zJSWAw5
+2EUuDI1pSBh9BA82/5PkuNlNeSTB3dXDD2PEPdzVWbSKvUB8ZdooV+2vngL0Zm4r
+47QPyd18yPHrRIbtBtHR/6CwKevLZ394zgExqhnekYKIqqEX41xsUV0Gm6x4vpjf
+2u6O/+YE2U+qyyxHE5Wd5oqde0oo9UUpFETJPVb6Q2cEeQib8PBAyi0i6KnF+kIV
+A9dY7IHSubtCK/i8wxMVqfd5GtbA8mmpeJFwnDvm9rBEsHybl08qlax9syEwsUYr
+/40NawZfTUU=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEATCCAumgAwIBAgIBRDANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzMDZaFw0y
+MDAzMDUyMjAzMDZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1hem9uIFJE
+UyBhcC1ub3J0aGVhc3QtMSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAMmM2B4PfTXCZjbZMWiDPyxvk/eeNwIRJAhfzesiGUiLozX6CRy3rwC1ZOPV
+AcQf0LB+O8wY88C/cV+d4Q2nBDmnk+Vx7o2MyMh343r5rR3Na+4izd89tkQVt0WW
+vO21KRH5i8EuBjinboOwAwu6IJ+HyiQiM0VjgjrmEr/YzFPL8MgHD/YUHehqjACn
+C0+B7/gu7W4qJzBL2DOf7ub2qszGtwPE+qQzkCRDwE1A4AJmVE++/FLH2Zx78Egg
+fV1sUxPtYgjGH76VyyO6GNKM6rAUMD/q5mnPASQVIXgKbupr618bnH+SWHFjBqZq
+HvDGPMtiiWII41EmGUypyt5AbysCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIG
+A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFIiKM0Q6n1K4EmLxs3ZXxINbwEwR
+MB8GA1UdIwQYMBaAFE4C7qw+9hXITO0s9QXBj5yECEmDMA0GCSqGSIb3DQEBBQUA
+A4IBAQBezGbE9Rw/k2e25iGjj5n8r+M3dlye8ORfCE/dijHtxqAKasXHgKX8I9Tw
+JkBiGWiuzqn7gO5MJ0nMMro1+gq29qjZnYX1pDHPgsRjUX8R+juRhgJ3JSHijRbf
+4qNJrnwga7pj94MhcLq9u0f6dxH6dXbyMv21T4TZMTmcFduf1KgaiVx1PEyJjC6r
+M+Ru+A0eM+jJ7uCjUoZKcpX8xkj4nmSnz9NMPog3wdOSB9cAW7XIc5mHa656wr7I
+WJxVcYNHTXIjCcng2zMKd1aCcl2KSFfy56sRfT7J5Wp69QSr+jq8KM55gw8uqAwi
+VPrXn2899T1rcTtFYFP16WXjGuc0
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEATCCAumgAwIBAgIBRTANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzMTlaFw0y
+MDAzMDUyMjAzMTlaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1hem9uIFJE
+UyBhcC1zb3V0aGVhc3QtMSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBANaXElmSEYt/UtxHFsARFhSUahTf1KNJzR0Dmay6hqOXQuRVbKRwPd19u5vx
+DdF1sLT7D69IK3VDnUiQScaCv2Dpu9foZt+rLx+cpx1qiQd1UHrvqq8xPzQOqCdC
+RFStq6yVYZ69yfpfoI67AjclMOjl2Vph3ftVnqP0IgVKZdzeC7fd+umGgR9xY0Qr
+Ubhd/lWdsbNvzK3f1TPWcfIKQnpvSt85PIEDJir6/nuJUKMtmJRwTymJf0i+JZ4x
+7dJa341p2kHKcHMgOPW7nJQklGBA70ytjUV6/qebS3yIugr/28mwReflg3TJzVDl
+EOvi6pqbqNbkMuEwGDCmEQIVqgkCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIG
+A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFAu93/4k5xbWOsgdCdn+/KdiRuit
+MB8GA1UdIwQYMBaAFE4C7qw+9hXITO0s9QXBj5yECEmDMA0GCSqGSIb3DQEBBQUA
+A4IBAQBlcjSyscpPjf5+MgzMuAsCxByqUt+WFspwcMCpwdaBeHOPSQrXNqX2Sk6P
+kth6oCivA64trWo8tFMvPYlUA1FYVD5WpN0kCK+P5pD4KHlaDsXhuhClJzp/OP8t
+pOyUr5109RHLxqoKB5J5m1XA7rgcFjnMxwBSWFe3/4uMk/+4T53YfCVXuc6QV3i7
+I/2LAJwFf//pTtt6fZenYfCsahnr2nvrNRNyAxcfvGZ/4Opn/mJtR6R/AjvQZHiR
+bkRNKF2GW0ueK5W4FkZVZVhhX9xh1Aj2Ollb+lbOqADaVj+AT3PoJPZ3MPQHKCXm
+xwG0LOLlRr/TfD6li1AfOVTAJXv9
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEATCCAumgAwIBAgIBRjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzMjRaFw0y
+MDAzMDUyMjAzMjRaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1hem9uIFJE
+UyBhcC1zb3V0aGVhc3QtMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAJqBAJutz69hFOh3BtLHZTbwE8eejGGKayn9hu98YMDPzWzGXWCmW+ZYWELA
+cY3cNWNF8K4FqKXFr2ssorBYim1UtYFX8yhydT2hMD5zgQ2sCGUpuidijuPA6zaq
+Z3tdhVR94f0q8mpwpv2zqR9PcqaGDx2VR1x773FupRPRo7mEW1vC3IptHCQlP/zE
+7jQiLl28bDIH2567xg7e7E9WnZToRnhlYdTaDaJsHTzi5mwILi4cihSok7Shv/ME
+hnukvxeSPUpaVtFaBhfBqq055ePq9I+Ns4KGreTKMhU0O9fkkaBaBmPaFgmeX/XO
+n2AX7gMouo3mtv34iDTZ0h6YCGkCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIG
+A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFIlQnY0KHYWn1jYumSdJYfwj/Nfw
+MB8GA1UdIwQYMBaAFE4C7qw+9hXITO0s9QXBj5yECEmDMA0GCSqGSIb3DQEBBQUA
+A4IBAQA0wVU6/l41cTzHc4azc4CDYY2Wd90DFWiH9C/mw0SgToYfCJ/5Cfi0NT/Y
+PRnk3GchychCJgoPA/k9d0//IhYEAIiIDjyFVgjbTkKV3sh4RbdldKVOUB9kumz/
+ZpShplsGt3z4QQiVnKfrAgqxWDjR0I0pQKkxXa6Sjkicos9LQxVtJ0XA4ieG1E7z
+zJr+6t80wmzxvkInSaWP3xNJK9azVRTrgQZQlvkbpDbExl4mNTG66VD3bAp6t3Wa
+B49//uDdfZmPkqqbX+hsxp160OH0rxJppwO3Bh869PkDnaPEd/Pxw7PawC+li0gi
+NRV8iCEx85aFxcyOhqn0WZOasxee
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/zCCAuegAwIBAgIBRzANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzMzFaFw0y
+MDAzMDUyMjAzMzFaMIGSMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEjMCEGA1UEAwwaQW1hem9uIFJE
+UyBldS1jZW50cmFsLTEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDFtP2dhSLuaPOI4ZrrPWsK4OY9ocQBp3yApH1KJYmI9wpQKZG/KCH2E6Oo7JAw
+QORU519r033T+FO2Z7pFPlmz1yrxGXyHpJs8ySx3Yo5S8ncDCdZJCLmtPiq/hahg
+5/0ffexMFUCQaYicFZsrJ/cStdxUV+tSw2JQLD7UxS9J97LQWUPyyG+ZrjYVTVq+
+zudnFmNSe4QoecXMhAFTGJFQXxP7nhSL9Ao5FGgdXy7/JWeWdQIAj8ku6cBDKPa6
+Y6kP+ak+In+Lye8z9qsCD/afUozfWjPR2aA4JoIZVF8dNRShIMo8l0XfgfM2q0+n
+ApZWZ+BjhIO5XuoUgHS3D2YFAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNV
+HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBRm4GsWIA/M6q+tK8WGHWDGh2gcyTAf
+BgNVHSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQUFAAOC
+AQEAHpMmeVQNqcxgfQdbDIi5UIy+E7zZykmtAygN1XQrvga9nXTis4kOTN6g5/+g
+HCx7jIXeNJzAbvg8XFqBN84Quqgpl/tQkbpco9Jh1HDs558D5NnZQxNqH5qXQ3Mm
+uPgCw0pYcPOa7bhs07i+MdVwPBsX27CFDtsgAIru8HvKxY1oTZrWnyIRo93tt/pk
+WuItVMVHjaQZVfTCow0aDUbte6Vlw82KjUFq+n2NMSCJDiDKsDDHT6BJc4AJHIq3
+/4Z52MSC9KMr0yAaaoWfW/yMEj9LliQauAgwVjArF4q78rxpfKTG9Rfd8U1BZANP
+7FrFMN0ThjfA1IvmOYcgskY5bQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBSDANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzMzVaFw0y
+MDAzMDUyMjAzMzVaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyBldS13ZXN0LTEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx
+PdbqQ0HKRj79Pmocxvjc+P6i4Ux24kgFIl+ckiir1vzkmesc3a58gjrMlCksEObt
+Yihs5IhzEq1ePT0gbfS9GYFp34Uj/MtPwlrfCBWG4d2TcrsKRHr1/EXUYhWqmdrb
+RhX8XqoRhVkbF/auzFSBhTzcGGvZpQ2KIaxRcQfcXlMVhj/pxxAjh8U4F350Fb0h
+nX1jw4/KvEreBL0Xb2lnlGTkwVxaKGSgXEnOgIyOFdOQc61vdome0+eeZsP4jqeR
+TGYJA9izJsRbe2YJxHuazD+548hsPlM3vFzKKEVURCha466rAaYAHy3rKur3HYQx
+Yt+SoKcEz9PXuSGj96ejAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBTebg//h2oeXbZjQ4uuoiuLYzuiPDAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQUFAAOCAQEA
+TikPaGeZasTPw+4RBemlsyPAjtFFQLo7ddaFdORLgdEysVf8aBqndvbA6MT/v4lj
+GtEtUdF59ZcbWOrVm+fBZ2h/jYJ59dYF/xzb09nyRbdMSzB9+mkSsnOMqluq5y8o
+DY/PfP2vGhEg/2ZncRC7nlQU1Dm8F4lFWEiQ2fi7O1cW852Vmbq61RIfcYsH/9Ma
+kpgk10VZ75b8m3UhmpZ/2uRY+JEHImH5WpcTJ7wNiPNJsciZMznGtrgOnPzYco8L
+cDleOASIZifNMQi9PKOJKvi0ITz0B/imr8KBsW0YjZVJ54HMa7W1lwugSM7aMAs+
+E3Sd5lS+SHwWaOCHwhOEVA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBSTANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzNDBaFw0y
+MDAzMDUyMjAzNDBaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyBzYS1lYXN0LTEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCU
+X4OBnQ5xA6TLJAiFEI6l7bUWjoVJBa/VbMdCCSs2i2dOKmqUaXu2ix2zcPILj3lZ
+GMk3d/2zvTK/cKhcFrewHUBamTeVHdEmynhMQamqNmkM4ptYzFcvEUw1TGxHT4pV
+Q6gSN7+/AJewQvyHexHo8D0+LDN0/Wa9mRm4ixCYH2CyYYJNKaZt9+EZfNu+PPS4
+8iB0TWH0DgQkbWMBfCRgolLLitAZklZ4dvdlEBS7evN1/7ttBxUK6SvkeeSx3zBl
+ww3BlXqc3bvTQL0A+RRysaVyFbvtp9domFaDKZCpMmDFAN/ntx215xmQdrSt+K3F
+cXdGQYHx5q410CAclGnbAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBT6iVWnm/uakS+tEX2mzIfw+8JL0zAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQUFAAOCAQEA
+FmDD+QuDklXn2EgShwQxV13+txPRuVdOSrutHhoCgMwFWCMtPPtBAKs6KPY7Guvw
+DpJoZSehDiOfsgMirjOWjvfkeWSNvKfjWTVneX7pZD9W5WPnsDBvTbCGezm+v87z
+b+ZM2ZMo98m/wkMcIEAgdSKilR2fuw8rLkAjhYFfs0A7tDgZ9noKwgHvoE4dsrI0
+KZYco6DlP/brASfHTPa2puBLN9McK3v+h0JaSqqm5Ro2Bh56tZkQh8AWy/miuDuK
+3+hNEVdxosxlkM1TPa1DGj0EzzK0yoeerXuH2HX7LlCrrxf6/wdKnjR12PMrLQ4A
+pCqkcWw894z6bV9MAvKe6A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBQzANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMTU0MDRaFw0y
+MDAzMDUyMTU0MDRaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyB1cy1lYXN0LTEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDI
+UIuwh8NusKHk1SqPXcP7OqxY3S/M2ZyQWD3w7Bfihpyyy/fc1w0/suIpX3kbMhAV
+2ESwged2/2zSx4pVnjp/493r4luhSqQYzru78TuPt9bhJIJ51WXunZW2SWkisSaf
+USYUzVN9ezR/bjXTumSUQaLIouJt3OHLX49s+3NAbUyOI8EdvgBQWD68H1epsC0n
+CI5s+pIktyOZ59c4DCDLQcXErQ+tNbDC++oct1ANd/q8p9URonYwGCGOBy7sbCYq
+9eVHh1Iy2M+SNXddVOGw5EuruvHoCIQyOz5Lz4zSuZA9dRbrfztNOpezCNYu6NKM
+n+hzcvdiyxv77uNm8EaxAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBQSQG3TmMe6Sa3KufaPBa72v4QFDzAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQUFAAOCAQEA
+L/mOZfB3187xTmjOHMqN2G2oSKHBKiQLM9uv8+97qT+XR+TVsBT6b3yoPpMAGhHA
+Pc7nxAF5gPpuzatx0OTLPcmYucFmfqT/1qA5WlgCnMNtczyNMH97lKFTNV7Njtek
+jWEzAEQSyEWrkNpNlC4j6kMYyPzVXQeXUeZTgJ9FNnVZqmvfjip2N22tawMjrCn5
+7KN/zN65EwY2oO9XsaTwwWmBu3NrDdMbzJnbxoWcFWj4RBwanR1XjQOVNhDwmCOl
+/1Et13b8CPyj69PC8BOVU6cfTSx8WUVy0qvYOKHNY9Bqa5BDnIL3IVmUkeTlM1mt
+enRpyBj+Bk9rh/ICdiRKmA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBSjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzNDVaFw0y
+MDAzMDUyMjAzNDVaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyB1cy13ZXN0LTEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDE
+Dhw+uw/ycaiIhhyu2pXFRimq0DlB8cNtIe8hdqndH8TV/TFrljNgR8QdzOgZtZ9C
+zzQ2GRpInN/qJF6slEd6wO+6TaDBQkPY+07TXNt52POFUhdVkhJXHpE2BS7Xn6J7
+7RFAOeG1IZmc2DDt+sR1BgXzUqHslQGfFYNS0/MBO4P+ya6W7IhruB1qfa4HiYQS
+dbe4MvGWnv0UzwAqdR7OF8+8/5c58YXZIXCO9riYF2ql6KNSL5cyDPcYK5VK0+Q9
+VI6vuJHSMYcF7wLePw8jtBktqAFE/wbdZiIHhZvNyiNWPPNTGUmQbaJ+TzQEHDs5
+8en+/W7JKnPyBOkxxENbAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBS0nw/tFR9bCjgqWTPJkyy4oOD8bzAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQUFAAOCAQEA
+CXGAY3feAak6lHdqj6+YWjy6yyUnLK37bRxZDsyDVXrPRQaXRzPTzx79jvDwEb/H
+Q/bdQ7zQRWqJcbivQlwhuPJ4kWPUZgSt3JUUuqkMsDzsvj/bwIjlrEFDOdHGh0mi
+eVIngFEjUXjMh+5aHPEF9BlQnB8LfVtKj18e15UDTXFa+xJPFxUR7wDzCfo4WI1m
+sUMG4q1FkGAZgsoyFPZfF8IVvgCuGdR8z30VWKklFxttlK0eGLlPAyIO0CQxPQlo
+saNJrHf4tLOgZIWk+LpDhNd9Et5EzvJ3aURUsKY4pISPPF5WdvM9OE59bERwUErd
+nuOuQWQeeadMceZnauRzJQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBSzANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTAyMDUyMjAzNTBaFw0y
+MDAzMDUyMjAzNTBaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyB1cy13ZXN0LTIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDM
+H58SR48U6jyERC1vYTnub34smf5EQVXyzaTmspWGWGzT31NLNZGSDFaa7yef9kdO
+mzJsgebR5tXq6LdwlIoWkKYQ7ycUaadtVKVYdI40QcI3cHn0qLFlg2iBXmWp/B+i
+Z34VuVlCh31Uj5WmhaBoz8t/GRqh1V/aCsf3Wc6jCezH3QfuCjBpzxdOOHN6Ie2v
+xX09O5qmZTvMoRBAvPkxdaPg/Mi7fxueWTbEVk78kuFbF1jHYw8U1BLILIAhcqlq
+x4u8nl73t3O3l/soNUcIwUDK0/S+Kfqhwn9yQyPlhb4Wy3pfnZLJdkyHldktnQav
+9TB9u7KH5Lk0aAYslMLxAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBT8roM4lRnlFHWMPWRz0zkwFZog1jAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQUFAAOCAQEA
+JwrxwgwmPtcdaU7O7WDdYa4hprpOMamI49NDzmE0s10oGrqmLwZygcWU0jT+fJ+Y
+pJe1w0CVfKaeLYNsOBVW3X4ZPmffYfWBheZiaiEflq/P6t7/Eg81gaKYnZ/x1Dfa
+sUYkzPvCkXe9wEz5zdUTOCptDt89rBR9CstL9vE7WYUgiVVmBJffWbHQLtfjv6OF
+NMb0QME981kGRzc2WhgP71YS2hHd1kXtsoYP1yTu4vThSKsoN4bkiHsaC1cRkLoy
+0fFA4wpB3WloMEvCDaUvvH1LZlBXTNlwi9KtcwD4tDxkkBt4tQczKLGpQ/nF/W9n
+8YDWk3IIc1sd0bkZqoau2Q==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEATCCAumgAwIBAgIBTDANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNTExMDYwMDA1NDZaFw0y
+MDAzMDUwMDA1NDZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1hem9uIFJE
+UyBhcC1ub3J0aGVhc3QtMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKSwd+RVUzTRH0FgnbwoTK8TMm/zMT4+2BvALpAUe6YXbkisg2goycWuuWLg
+jOpFBB3GtyvXZnkqi7MkDWUmj1a2kf8l2oLyoaZ+Hm9x/sV+IJzOqPvj1XVUGjP6
+yYYnPJmUYqvZeI7fEkIGdFkP2m4/sgsSGsFvpD9FK1bL1Kx2UDpYX0kHTtr18Zm/
+1oN6irqWALSmXMDydb8hE0FB2A1VFyeKE6PnoDj/Y5cPHwPPdEi6/3gkDkSaOG30
+rWeQfL3pOcKqzbHaWTxMphd0DSL/quZ64Nr+Ly65Q5PRcTrtr55ekOUziuqXwk+o
+9QpACMwcJ7ROqOznZTqTzSFVXFECAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIG
+A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFM6Nox/QWbhzWVvzoJ/y0kGpNPK+
+MB8GA1UdIwQYMBaAFE4C7qw+9hXITO0s9QXBj5yECEmDMA0GCSqGSIb3DQEBBQUA
+A4IBAQCTkWBqNvyRf3Y/W21DwFx3oT/AIWrHt0BdGZO34tavummXemTH9LZ/mqv9
+aljt6ZuDtf5DEQjdsAwXMsyo03ffnP7doWm8iaF1+Mui77ot0TmTsP/deyGwukvJ
+tkxX8bZjDh+EaNauWKr+CYnniNxCQLfFtXYJsfOdVBzK3xNL+Z3ucOQRhr2helWc
+CDQgwfhP1+3pRVKqHvWCPC4R3fT7RZHuRmZ38kndv476GxRntejh+ePffif78bFI
+3rIZCPBGobrrUMycafSbyXteoGca/kA+/IqrAPlk0pWQ4aEL0yTWN2h2dnjoD7oX
+byIuL/g9AGRh97+ssn7D6bDRPTbW
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/TCCAuWgAwIBAgIBTTANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNjA1MDMyMTI5MjJaFw0y
+MDAzMDUyMTI5MjJaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UEAwwYQW1hem9uIFJE
+UyBhcC1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+06eWGLE0TeqL9kyWOLkS8q0fXO97z+xyBV3DKSB2lg2GkgBz3B98MkmkeB0SZy3G
+Ce4uCpCPbFKiFEdiUclOlhZsrBuCeaimxLM3Ig2wuenElO/7TqgaYHYUbT3d+VQW
+GUbLn5GRZJZe1OAClYdOWm7A1CKpuo+cVV1vxbY2nGUQSJPpVn2sT9gnwvjdE60U
+JGYU/RLCTm8zmZBvlWaNIeKDnreIc4rKn6gUnJ2cQn1ryCVleEeyc3xjYDSrjgdn
+FLYGcp9mphqVT0byeQMOk0c7RHpxrCSA0V5V6/CreFV2LteK50qcDQzDSM18vWP/
+p09FoN8O7QrtOeZJzH/lmwIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T
+AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU2i83QHuEl/d0keXF+69HNJph7cMwHwYD
+VR0jBBgwFoAUTgLurD72FchM7Sz1BcGPnIQISYMwDQYJKoZIhvcNAQELBQADggEB
+ACqnH2VjApoDqoSQOky52QBwsGaj+xWYHW5Gm7EvCqvQuhWMkeBuD6YJmMvNyA9G
+I2lh6/o+sUk/RIsbYbxPRdhNPTOgDR9zsNRw6qxaHztq/CEC+mxDCLa3O1hHBaDV
+BmB3nCZb93BvO0EQSEk7aytKq/f+sjyxqOcs385gintdHGU9uM7gTZHnU9vByJsm
+/TL07Miq67X0NlhIoo3jAk+xHaeKJdxdKATQp0448P5cY20q4b8aMk1twcNaMvCP
+dG4M5doaoUA8OQ/0ukLLae/LBxLeTw04q1/a2SyFaVUX2Twbb1S3xVWwLA8vsyGr
+igXx7B5GgP+IHb6DTjPJAi0=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBTjANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNjA4MTExOTU4NDVaFw0y
+MDAzMDUxOTU4NDVaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyB1cy1lYXN0LTIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCp
+WnnUX7wM0zzstccX+4iXKJa9GR0a2PpvB1paEX4QRCgfhEdQWDaSqyrWNgdVCKkt
+1aQkWu5j6VAC2XIG7kKoonm1ZdBVyBLqW5lXNywlaiU9yhJkwo8BR+/OqgE+PLt/
+EO1mlN0PQudja/XkExCXTO29TG2j7F/O7hox6vTyHNHc0H88zS21uPuBE+jivViS
+yzj/BkyoQ85hnkues3f9R6gCGdc+J51JbZnmgzUkvXjAEuKhAm9JksVOxcOKUYe5
+ERhn0U9zjzpfbAITIkul97VVa5IxskFFTHIPJbvRKHJkiF6wTJww/tc9wm+fSCJ1
++DbQTGZgkQ3bJrqRN29/AgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBSAHQzUYYZbepwKEMvGdHp8wzHnfDAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQsFAAOCAQEA
+MbaEzSYZ+aZeTBxf8yi0ta8K4RdwEJsEmP6IhFFQHYUtva2Cynl4Q9tZg3RMsybT
+9mlnSQQlbN/wqIIXbkrcgFcHoXG9Odm/bDtUwwwDaiEhXVfeQom3G77QHOWMTCGK
+qadwuh5msrb17JdXZoXr4PYHDKP7j0ONfAyFNER2+uecblHfRSpVq5UeF3L6ZJb8
+fSw/GtAV6an+/0r+Qm+PiI2H5XuZ4GmRJYnGMhqWhBYrY7p3jtVnKcsh39wgfUnW
+AvZEZG/yhFyAZW0Essa39LiL5VSq14Y1DOj0wgnhSY/9WHxaAo1HB1T9OeZknYbD
+fl/EGSZ0TEvZkENrXcPlVA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/zCCAuegAwIBAgIBTzANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNjA5MTUwMDEwMTFaFw0y
+MDAzMDUwMDEwMTFaMIGSMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEjMCEGA1UEAwwaQW1hem9uIFJE
+UyBjYS1jZW50cmFsLTEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQCZYI/iQ6DrS3ny3t1EwX1wAD+3LMgh7Fd01EW5LIuaK2kYIIQpsVKhxLCit/V5
+AGc/1qiJS1Qz9ODLTh0Na6bZW6EakRzuHJLe32KJtoFYPC7Z09UqzXrpA/XL+1hM
+P0ZmCWsU7Nn/EmvfBp9zX3dZp6P6ATrvDuYaVFr+SA7aT3FXpBroqBS1fyzUPs+W
+c6zTR6+yc4zkHX0XQxC5RH6xjgpeRkoOajA/sNo7AQF7KlWmKHbdVF44cvvAhRKZ
+XaoVs/C4GjkaAEPTCbopYdhzg+KLx9eB2BQnYLRrIOQZtRfbQI2Nbj7p3VsRuOW1
+tlcks2w1Gb0YC6w6SuIMFkl1AgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNV
+HRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBToYWxE1lawl6Ks6NsvpbHQ3GKEtzAf
+BgNVHSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQsFAAOC
+AQEAG/8tQ0ooi3hoQpa5EJz0/E5VYBsAz3YxA2HoIonn0jJyG16bzB4yZt4vNQMA
+KsNlQ1uwDWYL1nz63axieUUFIxqxl1KmwfhsmLgZ0Hd2mnTPIl2Hw3uj5+wdgGBg
+agnAZ0bajsBYgD2VGQbqjdk2Qn7Fjy3LEWIvGZx4KyZ99OJ2QxB7JOPdauURAtWA
+DKYkP4LLJxtj07DSzG8kuRWb9B47uqUD+eKDIyjfjbnzGtd9HqqzYFau7EX3HVD9
+9Qhnjl7bTZ6YfAEZ3nH2t3Vc0z76XfGh47rd0pNRhMV+xpok75asKf/lNh5mcUrr
+VKwflyMkQpSbDCmcdJ90N2xEXQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBUDANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNjEwMTAxNzQ0NDJaFw0y
+MDAzMDUxNzQ0NDJaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyBldS13ZXN0LTIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDO
+cttLJfubB4XMMIGWNfJISkIdCMGJyOzLiMJaiWB5GYoXKhEl7YGotpy0qklwW3BQ
+a0fmVdcCLX+dIuVQ9iFK+ZcK7zwm7HtdDTCHOCKeOh2IcnU4c/VIokFi6Gn8udM6
+N/Zi5M5OGpVwLVALQU7Yctsn3c95el6MdVx6mJiIPVu7tCVZn88Z2koBQ2gq9P4O
+Sb249SHFqOb03lYDsaqy1NDsznEOhaRBw7DPJFpvmw1lA3/Y6qrExRI06H2VYR2i
+7qxwDV50N58fs10n7Ye1IOxTVJsgEA7X6EkRRXqYaM39Z76R894548WHfwXWjUsi
+MEX0RS0/t1GmnUQjvevDAgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBQBxmcuRSxERYCtNnSr5xNfySokHjAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQsFAAOCAQEA
+UyCUQjsF3nUAABjfEZmpksTuUo07aT3KGYt+EMMFdejnBQ0+2lJJFGtT+CDAk1SD
+RSgfEBon5vvKEtlnTf9a3pv8WXOAkhfxnryr9FH6NiB8obISHNQNPHn0ljT2/T+I
+Y6ytfRvKHa0cu3V0NXbJm2B4KEOt4QCDiFxUIX9z6eB4Kditwu05OgQh6KcogOiP
+JesWxBMXXGoDC1rIYTFO7szwDyOHlCcVXJDNsTJhc32oDWYdeIbW7o/5I+aQsrXZ
+C96HykZcgWzz6sElrQxUaT3IoMw/5nmw4uWKKnZnxgI9bY4fpQwMeBZ96iHfFxvH
+mqfEEuC7uUoPofXdBp2ObQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/DCCAuSgAwIBAgIBUTANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNzA4MjUyMTM5MjZaFw0y
+MDAzMDUyMTM5MjZaMIGPMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJE
+UyBldS13ZXN0LTMgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC+
+xmlEC/3a4cJH+UPwXCE02lC7Zq5NHd0dn6peMeLN8agb6jW4VfSY0NydjRj2DJZ8
+K7wV6sub5NUGT1NuFmvSmdbNR2T59KX0p2dVvxmXHHtIpQ9Y8Aq3ZfhmC5q5Bqgw
+tMA1xayDi7HmoPX3R8kk9ktAZQf6lDeksCvok8idjTu9tiSpDiMwds5BjMsWfyjZ
+d13PTGGNHYVdP692BSyXzSP1Vj84nJKnciW8tAqwIiadreJt5oXyrCXi8ekUMs80
+cUTuGm3aA3Q7PB5ljJMPqz0eVddaiIvmTJ9O3Ez3Du/HpImyMzXjkFaf+oNXf/Hx
+/EW5jCRR6vEiXJcDRDS7AgMBAAGjZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMB
+Af8ECDAGAQH/AgEAMB0GA1UdDgQWBBRZ9mRtS5fHk3ZKhG20Oack4cAqMTAfBgNV
+HSMEGDAWgBROAu6sPvYVyEztLPUFwY+chAhJgzANBgkqhkiG9w0BAQsFAAOCAQEA
+F/u/9L6ExQwD73F/bhCw7PWcwwqsK1mypIdrjdIsu0JSgwWwGCXmrIspA3n3Dqxq
+sMhAJD88s9Em7337t+naar2VyLO63MGwjj+vA4mtvQRKq8ScIpiEc7xN6g8HUMsd
+gPG9lBGfNjuAZsrGJflrko4HyuSM7zHExMjXLH+CXcv/m3lWOZwnIvlVMa4x0Tz0
+A4fklaawryngzeEjuW6zOiYCzjZtPlP8Fw0SpzppJ8VpQfrZ751RDo4yudmPqoPK
+5EUe36L8U+oYBXnC5TlYs9bpVv9o5wJQI5qA9oQE2eFWxF1E0AyZ4V5sgGUBStaX
+BjDDWul0wSo7rt1Tq7XpnA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEATCCAumgAwIBAgIBTjANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNzEyMDEwMDU1NDJaFw0y
+MDAzMDUwMDU1NDJaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzElMCMGA1UEAwwcQW1hem9uIFJE
+UyBhcC1ub3J0aGVhc3QtMyBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAMZtQNnm/XT19mTa10ftHLzg5UhajoI65JHv4TQNdGXdsv+CQdGYU49BJ9Eu
+3bYgiEtTzR2lQe9zGMvtuJobLhOWuavzp7IixoIQcHkFHN6wJ1CvqrxgvJfBq6Hy
+EuCDCiU+PPDLUNA6XM6Qx3IpHd1wrJkjRB80dhmMSpxmRmx849uFafhN+P1QybsM
+TI0o48VON2+vj+mNuQTyLMMP8D4odSQHjaoG+zyJfJGZeAyqQyoOUOFEyQaHC3TT
+3IDSNCQlpxb9LerbCoKu79WFBBq3CS5cYpg8/fsnV2CniRBFFUumBt5z4dhw9RJU
+qlUXXO1ZyzpGd+c5v6FtrfXtnIUCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIG
+A1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFETv7ELNplYy/xTeIOInl6nzeiHg
+MB8GA1UdIwQYMBaAFE4C7qw+9hXITO0s9QXBj5yECEmDMA0GCSqGSIb3DQEBBQUA
+A4IBAQCpKxOQcd0tEKb3OtsOY8q/MPwTyustGk2Rt7t9G68idADp8IytB7M0SDRo
+wWZqynEq7orQVKdVOanhEWksNDzGp0+FPAf/KpVvdYCd7ru3+iI+V4ZEp2JFdjuZ
+Zz0PIjS6AgsZqE5Ri1J+NmfmjGZCPhsHnGZiBaenX6K5VRwwwmLN6xtoqrrfR5zL
+QfBeeZNJG6KiM3R/DxJ5rAa6Fz+acrhJ60L7HprhB7SFtj1RCijau3+ZwiGmUOMr
+yKlMv+VgmzSw7o4Hbxy1WVrA6zQsTHHSGf+vkQn2PHvnFMUEu/ZLbTDYFNmTLK91
+K6o4nMsEvhBKgo4z7H1EqqxXhvN2
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEBDCCAuygAwIBAgIBTTANBgkqhkiG9w0BAQUFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xNzEyMDYyMjQyMjdaFw0y
+MDAzMDQyMjQyMjdaMIGXMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEoMCYGA1UEAwwfQW1hem9uIFJE
+UyBwcmV2aWV3LXVzLWVhc3QtMiBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMw0E8k8URanS0c/i1S7wzFf5+XC9H2bm+4pENdElGP5s9rVCybrzJaw
+6zZgVLpOFnS9mJ+sDHIMUexPjj0X4+r7wZ4+hPfy7Rmrgbt23IQwr+PIBxsKAVjj
+iaQ3bSm5WQ79an5elfQqEDdZ13ckUcLBJDA8bUDthI8m7gnteGtx0M1D0VS5PDs9
+cf96QlBia9Lx3VcNo3cc0PzP30E4j3h/Ywlb0jXUgB6oVlTxK70BjD3kZa+2xlea
+vKmm4NqGVhPY7BWd4XNdbSYsPDeZ9HxHNWXZxoHcQ7vSU8RKYVPtoBK/zIp3eWOi
+gzZlm5vYPvlkYh2pshttPPVyhZqlEZ8CAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEG
+MBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFI93K+FRhste6w3MiD+IK3Tc
+g/BsMB8GA1UdIwQYMBaAFE4C7qw+9hXITO0s9QXBj5yECEmDMA0GCSqGSIb3DQEB
+BQUAA4IBAQAs4RsC8MJVOvrlRi5sgKC9LJ4BvSrrbR5V8CdIEwlPqrVOSsU5t7Py
+j8CHoPUY/ya1azlBSO62BqdZxipFuAR06NdxNG2Gy0fGl71N2udxokwEPW+IEZ81
+G6JeX8HNFjnna8ehimz1VJDDW7qborhg3dCAgEWkgv5PDR9/zoUu6bbmHPV77zbx
+Gq7Sybz5OiagC7Nj9N1WgjNXUEmlfY2DHXnJmIVgUGEVrBgu5tGcIU/bQCRznH1N
+JsBH0SalneCbSzMBhQdnzL+L5KOERibWAZvS6ebmomTBwa03kgo/T0DfEccgobTs
+rV6T9/8Vg9T18vEeqURL+LOGs7+lIKmN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/TCCAuWgAwIBAgIBUjANBgkqhkiG9w0BAQsFADCBijELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxIjAgBgNVBAoM
+GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
+GzAZBgNVBAMMEkFtYXpvbiBSRFMgUm9vdCBDQTAeFw0xODA5MjgxNzM0NTJaFw0y
+MDAzMDUxNzM0NTJaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3Rv
+bjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
+cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UEAwwYQW1hem9uIFJE
+UyBldS1ub3J0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
+wvHfpoixHNy1jvcq/WNhXDHlsFVbEOX7mp01YQeK0wWqlpFvjs2HFJ1sRnnmyhdT
+sv4VQuXnQw2V2iFAO2HveDi8pcJ+eIXY+wloSVBytgYLTMcNpn5LmqIeyGO+Lr6p
+KUr78I4uE0mnabxyILA96CYrYtgwpLCtpEXSdSJPwOSK9nX9++molxLcJ5v4fiPS
+j46PETsbFoFdXXwYCdiJKpzO4zUAkKzzvzbF7cXg9R4noJuytjEKbluxugDHdnwl
+SctGZ3moju2I0OpPbJKUI3wHsUMtY5v15X74MOED5lbtaW5+/6JIERggve0b23Ni
+4nlYSt0Bb3z3Zwc83twCUwIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0T
+AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU4stOy1OAFRyvZCSKNfCiPRD+rPowHwYD
+VR0jBBgwFoAUTgLurD72FchM7Sz1BcGPnIQISYMwDQYJKoZIhvcNAQELBQADggEB
+AHpRIlKh1fqbMHl0+VnJ/52XQy1F5gM2hnw3lYkOLsDyzj9W4V6D1v2EDgYW+ZVH
+0wWqo8m0jS6CDn14W2HqNlyXyHpJK3eh3088zxvJgKqzKS4ghNzafN7axwYIwRN6
+9rrhRWy9MaFHaSPKtgiuTxw9fOekqyJdO+OYpBVEp7KEEyEG9/W5xZcU64zGb6UT
+8/g4+5t+HlT0nYBMvt8HW7w2XbFBetfKKK4WaoPKloOMN+RLO/JgJ6pVWvxM8nhC
+PbVtr43OI1sQAXYk0an7aUDgXT98vGwovWNHI6lFCMGRG+WXhauLtKRsIr4hR1LV
+fES7Q9MWPzPYHQoKELF9Jhk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEBzCCAu+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT
+MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
+DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
+MSUwIwYDVQQDDBxBbWF6b24gUkRTIGFwLWVhc3QtMSBSb290IENBMB4XDTE5MDIx
+NzAyNDcwMFoXDTIyMDYwMTEyMDAwMFowgY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+DApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSIwIAYDVQQKDBlBbWF6b24g
+V2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMSAwHgYDVQQD
+DBdBbWF6b24gUkRTIGFwLWVhc3QtMSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOcJAUofyJuBuPr5ISHi/Ha5ed8h3eGdzn4MBp6rytPOg9NVGRQs
+O93fNGCIKsUT6gPuk+1f1ncMTV8Y0Fdf4aqGWme+Khm3ZOP3V1IiGnVq0U2xiOmn
+SQ4Q7LoeQC4lC6zpoCHVJyDjZ4pAknQQfsXb77Togdt/tK5ahev0D+Q3gCwAoBoO
+DHKJ6t820qPi63AeGbJrsfNjLKiXlFPDUj4BGir4dUzjEeH7/hx37na1XG/3EcxP
+399cT5k7sY/CR9kctMlUyEEUNQOmhi/ly1Lgtihm3QfjL6K9aGLFNwX35Bkh9aL2
+F058u+n8DP/dPeKUAcJKiQZUmzuen5n57x8CAwEAAaNmMGQwDgYDVR0PAQH/BAQD
+AgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFFlqgF4FQlb9yP6c+Q3E
+O3tXv+zOMB8GA1UdIwQYMBaAFK9T6sY/PBZVbnHcNcQXf58P4OuPMA0GCSqGSIb3
+DQEBCwUAA4IBAQDeXiS3v1z4jWAo1UvVyKDeHjtrtEH1Rida1eOXauFuEQa5tuOk
+E53Os4haZCW4mOlKjigWs4LN+uLIAe1aFXGo92nGIqyJISHJ1L+bopx/JmIbHMCZ
+0lTNJfR12yBma5VQy7vzeFku/SisKwX0Lov1oHD4MVhJoHbUJYkmAjxorcIHORvh
+I3Vj5XrgDWtLDPL8/Id/roul/L+WX5ir+PGScKBfQIIN2lWdZoqdsx8YWqhm/ikL
+C6qNieSwcvWL7C03ri0DefTQMY54r5wP33QU5hJ71JoaZI3YTeT0Nf+NRL4hM++w
+Q0veeNzBQXg1f/JxfeA39IDIX1kiCf71tGlT
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEEDCCAvigAwIBAgIJAJF3HxEqKM4lMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD
VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi
MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h
-em9uIFJEUzEmMCQGA1UEAwwdQW1hem9uIFJEUyBhZi1zb3V0aC0xIFJvb3QgQ0Ew
-HhcNMTkxMDI4MTgwNTU4WhcNMjQxMDI2MTgwNTU4WjCBlTELMAkGA1UEBhMCVVMx
-EDAOBgNVBAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoM
-GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
-JjAkBgNVBAMMHUFtYXpvbiBSRFMgYWYtc291dGgtMSBSb290IENBMIIBIjANBgkq
-hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwR2351uPMZaJk2gMGT+1sk8HE9MQh2rc
-/sCnbxGn2p1c7Oi9aBbd/GiFijeJb2BXvHU+TOq3d3Jjqepq8tapXVt4ojbTJNyC
-J5E7r7KjTktKdLxtBE1MK25aY+IRJjtdU6vG3KiPKUT1naO3xs3yt0F76WVuFivd
-9OHv2a+KHvPkRUWIxpmAHuMY9SIIMmEZtVE7YZGx5ah0iO4JzItHcbVR0y0PBH55
-arpFBddpIVHCacp1FUPxSEWkOpI7q0AaU4xfX0fe1BV5HZYRKpBOIp1TtZWvJD+X
-jGUtL1BEsT5vN5g9MkqdtYrC+3SNpAk4VtpvJrdjraI/hhvfeXNnAwIDAQABo2Mw
-YTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUEEi/
-WWMcBJsoGXg+EZwkQ0MscZQwHwYDVR0jBBgwFoAUEEi/WWMcBJsoGXg+EZwkQ0Ms
-cZQwDQYJKoZIhvcNAQELBQADggEBAGDZ5js5Pc/gC58LJrwMPXFhJDBS8QuDm23C
-FFUdlqucskwOS3907ErK1ZkmVJCIqFLArHqskFXMAkRZ2PNR7RjWLqBs+0znG5yH
-hRKb4DXzhUFQ18UBRcvT6V6zN97HTRsEEaNhM/7k8YLe7P8vfNZ28VIoJIGGgv9D
-wQBBvkxQ71oOmAG0AwaGD0ORGUfbYry9Dz4a4IcUsZyRWRMADixgrFv6VuETp26s
-/+z+iqNaGWlELBKh3iQCT6Y/1UnkPLO42bxrCSyOvshdkYN58Q2gMTE1SVTqyo8G
-Lw8lLAz9bnvUSgHzB3jRrSx6ggF/WRMRYlR++y6LXP4SAsSAaC0=
+em9uIFJEUzElMCMGA1UEAwwcQW1hem9uIFJEUyBhcC1lYXN0LTEgUm9vdCBDQTAe
+Fw0xOTAyMTcwMjQ2MTFaFw0yNDAyMTYwMjQ2MTFaMIGUMQswCQYDVQQGEwJVUzEQ
+MA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEiMCAGA1UECgwZ
+QW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEl
+MCMGA1UEAwwcQW1hem9uIFJEUyBhcC1lYXN0LTEgUm9vdCBDQTCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAOCVr1Yj5IW4XWa9QOLGJDSz4pqIM6BAbqQp
+gYvzIO4Lv8c8dEnuuuCY8M/zOrJ1iQJ3cDiKGa32HVBVcH+nUdXzw4Jq5jw0hsb6
+/WW2RD2aUe4jCkRD5wNzmeHM4gTgtMZnXNVHpELgKR4wVhSHEfWFTiMsZi35y8mj
+PL98Mz/m/nMnB/59EjMvcJMrsUljHO6B9BMEcvNkwvre9xza0BQWKyiVRcbOpoj1
+w4BPtYYZ+dW2QKw9AmYXwAmCLeATsxrHIJ/IbzS7obxv2QN2Eh4pJ3ghRCFv1XM9
+XVkm13oiCjj7jsxAwF7o+VggPl/GG+/Gwk+TLuaTFNAtROpPxL8CAwEAAaNjMGEw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFK9T6sY/
+PBZVbnHcNcQXf58P4OuPMB8GA1UdIwQYMBaAFK9T6sY/PBZVbnHcNcQXf58P4OuP
+MA0GCSqGSIb3DQEBCwUAA4IBAQBBY+KATaT7ndYT3Ky0VWaiwNfyl1u3aDxr+MKP
+VeDhtOhlob5u0E+edOXUvEXd4A+ntS+U0HmwvtMXtQbQ2EJbsNRqZnS8KG9YB2Yc
+Q99auphW3wMjwHRtflLO5h14aa9SspqJJgcM1R7Z3pAYeq6bpBDxZSGrYtWI64q4
+h4i67qWAGDFcXSTW1kJ00GMlBCIGTeYiu8LYutdsDWzYKkeezJRjx9VR4w7A7e1G
+WmY4aUg/8aPxCioY2zEQKNl55Ghg6Dwy+6BxaV6RlV9r9EaSCai11p1bgS568WQn
+4WNQK36EGe37l2SOpDB6STrq57/rjREvmq803Ylg/Gf6qqzK
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIEEjCCAvqgAwIBAgIJAJYM4LxvTZA6MA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD
-VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi
-MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h
-em9uIFJEUzEmMCQGA1UEAwwdQW1hem9uIFJEUyBldS1zb3V0aC0xIFJvb3QgQ0Ew
-HhcNMTkxMDMwMjAyMDM2WhcNMjQxMDI4MjAyMDM2WjCBlTELMAkGA1UEBhMCVVMx
-EDAOBgNVBAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoM
-GUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMx
-JjAkBgNVBAMMHUFtYXpvbiBSRFMgZXUtc291dGgtMSBSb290IENBMIIBIjANBgkq
-hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqM921jXCXeqpRNCS9CBPOe5N7gMaEt+D
-s5uR3riZbqzRlHGiF1jZihkXfHAIQewDwy+Yz+Oec1aEZCQMhUHxZJPusuX0cJfj
-b+UluFqHIijL2TfXJ3D0PVLLoNTQJZ8+GAPECyojAaNuoHbdVqxhOcznMsXIXVFq
-yVLKDGvyKkJjai/iSPDrQMXufg3kWt0ISjNLvsG5IFXgP4gttsM8i0yvRd4QcHoo
-DjvH7V3cS+CQqW5SnDrGnHToB0RLskE1ET+oNOfeN9PWOxQprMOX/zmJhnJQlTqD
-QP7jcf7SddxrKFjuziFiouskJJyNDsMjt1Lf60+oHZhed2ogTeifGwIDAQABo2Mw
-YTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUFBAF
-cgJe/BBuZiGeZ8STfpkgRYQwHwYDVR0jBBgwFoAUFBAFcgJe/BBuZiGeZ8STfpkg
-RYQwDQYJKoZIhvcNAQELBQADggEBAKAYUtlvDuX2UpZW9i1QgsjFuy/ErbW0dLHU
-e/IcFtju2z6RLZ+uF+5A8Kme7IKG1hgt8s+w9TRVQS/7ukQzoK3TaN6XKXRosjtc
-o9Rm4gYWM8bmglzY1TPNaiI4HC7546hSwJhubjN0bXCuj/0sHD6w2DkiGuwKNAef
-yTu5vZhPkeNyXLykxkzz7bNp2/PtMBnzIp+WpS7uUDmWyScGPohKMq5PqvL59z+L
-ZI3CYeMZrJ5VpXUg3fNNIz/83N3G0sk7wr0ohs/kHTP7xPOYB0zD7Ku4HA0Q9Swf
-WX0qr6UQgTPMjfYDLffI7aEId0gxKw1eGYc6Cq5JAZ3ipi/cBFc=
+MIIECTCCAvGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT
+MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
+DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
+MSYwJAYDVQQDDB1BbWF6b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQTAeFw0xOTA1
+MTAyMTU4NDNaFw0yNTA2MDExMjAwMDBaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9u
+IFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UE
+AwwYQW1hem9uIFJEUyBtZS1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAudOYPZH+ihJAo6hNYMB5izPVBe3TYhnZm8+X3IoaaYiKtsp1
+JJhkTT0CEejYIQ58Fh4QrMUyWvU8qsdK3diNyQRoYLbctsBPgxBR1u07eUJDv38/
+C1JlqgHmMnMi4y68Iy7ymv50QgAMuaBqgEBRI1R6Lfbyrb2YvH5txjJyTVMwuCfd
+YPAtZVouRz0JxmnfsHyxjE+So56uOKTDuw++Ho4HhZ7Qveej7XB8b+PIPuroknd3
+FQB5RVbXRvt5ZcVD4F2fbEdBniF7FAF4dEiofVCQGQ2nynT7dZdEIPfPdH3n7ZmE
+lAOmwHQ6G83OsiHRBLnbp+QZRgOsjkHJxT20bQIDAQABo2YwZDAOBgNVHQ8BAf8E
+BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUOEVDM7VomRH4HVdA
+QvIMNq2tXOcwHwYDVR0jBBgwFoAU54cfDjgwBx4ycBH8+/r8WXdaiqYwDQYJKoZI
+hvcNAQELBQADggEBAHhvMssj+Th8IpNePU6RH0BiL6o9c437R3Q4IEJeFdYL+nZz
+PW/rELDPvLRUNMfKM+KzduLZ+l29HahxefejYPXtvXBlq/E/9czFDD4fWXg+zVou
+uDXhyrV4kNmP4S0eqsAP/jQHPOZAMFA4yVwO9hlqmePhyDnszCh9c1PfJSBh49+b
+4w7i/L3VBOMt8j3EKYvqz0gVfpeqhJwL4Hey8UbVfJRFJMJzfNHpePqtDRAY7yjV
+PYquRaV2ab/E+/7VFkWMM4tazYz/qsYA2jSH+4xDHvYk8LnsbcrF9iuidQmEc5sb
+FgcWaSKG4DJjcI5k7AJLWcXyTDt21Ci43LE+I9Q=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEEjCCAvqgAwIBAgIJANew34ehz5l8MA0GCSqGSIb3DQEBCwUAMIGVMQswCQYD
@@ -71,28 +551,28 @@ aTW6R05681Z0mvkRdb+cdXtKOSuDZPoe2wJJIaz3IlNQNSrB5TImMYgmt6iAsFhv
3vfTSTKrZDNTJn4ybG6pq1zWExoXsktZPylJly6R3RBwV6nwqBM=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIEBjCCAu6gAwIBAgIJAMc0ZzaSUK51MA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
-VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi
-MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h
-em9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJEUyBSb290IDIwMTkgQ0EwHhcNMTkw
-ODIyMTcwODUwWhcNMjQwODIyMTcwODUwWjCBjzELMAkGA1UEBhMCVVMxEDAOBgNV
-BAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoMGUFtYXpv
-biBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxIDAeBgNV
-BAMMF0FtYXpvbiBSRFMgUm9vdCAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEArXnF/E6/Qh+ku3hQTSKPMhQQlCpoWvnIthzX6MK3p5a0eXKZ
-oWIjYcNNG6UwJjp4fUXl6glp53Jobn+tWNX88dNH2n8DVbppSwScVE2LpuL+94vY
-0EYE/XxN7svKea8YvlrqkUBKyxLxTjh+U/KrGOaHxz9v0l6ZNlDbuaZw3qIWdD/I
-6aNbGeRUVtpM6P+bWIoxVl/caQylQS6CEYUk+CpVyJSkopwJlzXT07tMoDL5WgX9
-O08KVgDNz9qP/IGtAcRduRcNioH3E9v981QO1zt/Gpb2f8NqAjUUCUZzOnij6mx9
-McZ+9cWX88CRzR0vQODWuZscgI08NvM69Fn2SQIDAQABo2MwYTAOBgNVHQ8BAf8E
-BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUc19g2LzLA5j0Kxc0LjZa
-pmD/vB8wHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJKoZIhvcN
-AQELBQADggEBAHAG7WTmyjzPRIM85rVj+fWHsLIvqpw6DObIjMWokpliCeMINZFV
-ynfgBKsf1ExwbvJNzYFXW6dihnguDG9VMPpi2up/ctQTN8tm9nDKOy08uNZoofMc
-NUZxKCEkVKZv+IL4oHoeayt8egtv3ujJM6V14AstMQ6SwvwvA93EP/Ug2e4WAXHu
-cbI1NAbUgVDqp+DRdfvZkgYKryjTWd/0+1fS8X1bBZVWzl7eirNVnHbSH2ZDpNuY
-0SBd8dj5F6ld3t58ydZbrTHze7JJOd8ijySAp4/kiu9UfZWuTPABzDa/DSdz9Dk/
-zPW4CXXvhLmE02TA9/HeCw3KEHIwicNuEfw=
+MIIEETCCAvmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT
+MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
+DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
+MSUwIwYDVQQDDBxBbWF6b24gUkRTIEJldGEgUm9vdCAyMDE5IENBMB4XDTE5MDgy
+MDE3MTAwN1oXDTI0MDgxOTE3MzgyNlowgZkxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
+DApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSIwIAYDVQQKDBlBbWF6b24g
+V2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMSowKAYDVQQD
+DCFBbWF6b24gUkRTIEJldGEgdXMtZWFzdC0xIDIwMTkgQ0EwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQDTNCOlotQcLP8TP82U2+nk0bExVuuMVOgFeVMx
+vbUHZQeIj9ikjk+jm6eTDnnkhoZcmJiJgRy+5Jt69QcRbb3y3SAU7VoHgtraVbxF
+QDh7JEHI9tqEEVOA5OvRrDRcyeEYBoTDgh76ROco2lR+/9uCvGtHVrMCtG7BP7ZB
+sSVNAr1IIRZZqKLv2skKT/7mzZR2ivcw9UeBBTUf8xsfiYVBvMGoEsXEycjYdf6w
+WV+7XS7teNOc9UgsFNN+9AhIBc1jvee5E//72/4F8pAttAg/+mmPUyIKtekNJ4gj
+OAR2VAzGx1ybzWPwIgOudZFHXFduxvq4f1hIRPH0KbQ/gkRrAgMBAAGjZjBkMA4G
+A1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTkvpCD
+6C43rar9TtJoXr7q8dkrrjAfBgNVHSMEGDAWgBStoQwVpbGx87fxB3dEGDqKKnBT
+4TANBgkqhkiG9w0BAQsFAAOCAQEAJd9fOSkwB3uVdsS+puj6gCER8jqmhd3g/J5V
+Zjk9cKS8H0e8pq/tMxeJ8kpurPAzUk5RkCspGt2l0BSwmf3ahr8aJRviMX6AuW3/
+g8aKplTvq/WMNGKLXONa3Sq8591J+ce8gtOX/1rDKmFI4wQ/gUzOSYiT991m7QKS
+Fr6HMgFuz7RNJbb3Fy5cnurh8eYWA7mMv7laiLwTNsaro5qsqErD5uXuot6o9beT
+a+GiKinEur35tNxAr47ax4IRubuIzyfCrezjfKc5raVV2NURJDyKP0m0CCaffAxE
+qn2dNfYc3v1D8ypg3XjHlOzRo32RB04o8ALHMD9LSwsYDLpMag==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIEEDCCAvigAwIBAgIJAKFMXyltvuRdMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD
@@ -119,6 +599,30 @@ XR/UVxMJL0Q4iVpcRS1kaNCMfqS2smbLJeNdsan8pkw1dvPhcaVTb7CvjhJtjztF
YfDzAI5794qMlWxwilKMmUvDlPPOTen8NNHkLwWvyFCH7Doh
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
+MIIEFzCCAv+gAwIBAgICFSUwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT
+MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
+DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
+MSgwJgYDVQQDDB9BbWF6b24gUkRTIFByZXZpZXcgUm9vdCAyMDE5IENBMB4XDTE5
+MDgyMTIyMzk0N1oXDTI0MDgyMTIyMjk0OVowgZwxCzAJBgNVBAYTAlVTMRMwEQYD
+VQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSIwIAYDVQQKDBlBbWF6
+b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMS0wKwYD
+VQQDDCRBbWF6b24gUkRTIFByZXZpZXcgdXMtZWFzdC0yIDIwMTkgQ0EwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0dB/U7qRnSf05wOi7m10Pa2uPMTJv
+r6U/3Y17a5prq5Zr4++CnSUYarG51YuIf355dKs+7Lpzs782PIwCmLpzAHKWzix6
+pOaTQ+WZ0+vUMTxyqgqWbsBgSCyP7pVBiyqnmLC/L4az9XnscrbAX4pNaoJxsuQe
+mzBo6yofjQaAzCX69DuqxFkVTRQnVy7LCFkVaZtjNAftnAHJjVgQw7lIhdGZp9q9
+IafRt2gteihYfpn+EAQ/t/E4MnhrYs4CPLfS7BaYXBycEKC5Muj1l4GijNNQ0Efo
+xG8LSZz7SNgUvfVwiNTaqfLP3AtEAWiqxyMyh3VO+1HpCjT7uNBFtmF3AgMBAAGj
+ZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
+BBQtinkdrj+0B2+qdXngV2tgHnPIujAfBgNVHSMEGDAWgBRp0xqULkNh/w2ZVzEI
+o2RIY7O03TANBgkqhkiG9w0BAQsFAAOCAQEAtJdqbCxDeMc8VN1/RzCabw9BIL/z
+73Auh8eFTww/sup26yn8NWUkfbckeDYr1BrXa+rPyLfHpg06kwR8rBKyrs5mHwJx
+bvOzXD/5WTdgreB+2Fb7mXNvWhenYuji1MF+q1R2DXV3I05zWHteKX6Dajmx+Uuq
+Yq78oaCBSV48hMxWlp8fm40ANCL1+gzQ122xweMFN09FmNYFhwuW+Ao+Vv90ZfQG
+PYwTvN4n/gegw2TYcifGZC2PNX74q3DH03DXe5fvNgRW5plgz/7f+9mS+YHd5qa9
+tYTPUvoRbi169ou6jicsMKUKPORHWhiTpSCWR1FMMIbsAcsyrvtIsuaGCQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
MIIEFjCCAv6gAwIBAgIJAMzYZJ+R9NBVMA0GCSqGSIb3DQEBCwUAMIGXMQswCQYD
VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi
MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h
@@ -143,78 +647,6 @@ xMTldqWFsOF3bJIlvOY0c/1EFZXu3Ns6/oCP//Ap9vumldYMUZWmbK+gK33FPOXV
8BQ6jNC29icv7lLDpRPwjibJBXX+peDR5UK4FdYcswWEB1Tix5X8dYu6
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT
-MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
-DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
-MSYwJAYDVQQDDB1BbWF6b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQTAeFw0xOTEw
-MjgxODA2NTNaFw0yNDEwMjgxODA2NTNaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UE
-CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9u
-IFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UE
-AwwYQW1hem9uIFJEUyBhZi1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAvtV1OqmFa8zCVQSKOvPUJERLVFtd4rZmDpImc5rIoeBk7w/P
-9lcKUJjO8R/w1a2lJXx3oQ81tiY0Piw6TpT62YWVRMWrOw8+Vxq1dNaDSFp9I8d0
-UHillSSbOk6FOrPDp+R6AwbGFqUDebbN5LFFoDKbhNmH1BVS0a6YNKpGigLRqhka
-cClPslWtPqtjbaP3Jbxl26zWzLo7OtZl98dR225pq8aApNBwmtgA7Gh60HK/cX0t
-32W94n8D+GKSg6R4MKredVFqRTi9hCCNUu0sxYPoELuM+mHiqB5NPjtm92EzCWs+
-+vgWhMc6GxG+82QSWx1Vj8sgLqtE/vLrWddf5QIDAQABo2YwZDAOBgNVHQ8BAf8E
-BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUuLB4gYVJrSKJj/Gz
-pqc6yeA+RcAwHwYDVR0jBBgwFoAUEEi/WWMcBJsoGXg+EZwkQ0MscZQwDQYJKoZI
-hvcNAQELBQADggEBABauYOZxUhe9/RhzGJ8MsWCz8eKcyDVd4FCnY6Qh+9wcmYNT
-LtnD88LACtJKb/b81qYzcB0Em6+zVJ3Z9jznfr6buItE6es9wAoja22Xgv44BTHL
-rimbgMwpTt3uEMXDffaS0Ww6YWb3pSE0XYI2ISMWz+xRERRf+QqktSaL39zuiaW5
-tfZMre+YhohRa/F0ZQl3RCd6yFcLx4UoSPqQsUl97WhYzwAxZZfwvLJXOc4ATt3u
-VlCUylNDkaZztDJc/yN5XQoK9W5nOt2cLu513MGYKbuarQr8f+gYU8S+qOyuSRSP
-NRITzwCRVnsJE+2JmcRInn/NcanB7uOGqTvJ9+c=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT
-MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
-DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
-MSYwJAYDVQQDDB1BbWF6b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQTAeFw0xOTEw
-MzAyMDIxMzBaFw0yNDEwMzAyMDIxMzBaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UE
-CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9u
-IFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UE
-AwwYQW1hem9uIFJEUyBldS1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAtEyjYcajx6xImJn8Vz1zjdmL4ANPgQXwF7+tF7xccmNAZETb
-bzb3I9i5fZlmrRaVznX+9biXVaGxYzIUIR3huQ3Q283KsDYnVuGa3mk690vhvJbB
-QIPgKa5mVwJppnuJm78KqaSpi0vxyCPe3h8h6LLFawVyWrYNZ4okli1/U582eef8
-RzJp/Ear3KgHOLIiCdPDF0rjOdCG1MOlDLixVnPn9IYOciqO+VivXBg+jtfc5J+L
-AaPm0/Yx4uELt1tkbWkm4BvTU/gBOODnYziITZM0l6Fgwvbwgq5duAtKW+h031lC
-37rEvrclqcp4wrsUYcLAWX79ZyKIlRxcAdvEhQIDAQABo2YwZDAOBgNVHQ8BAf8E
-BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU7zPyc0azQxnBCe7D
-b9KAadH1QSEwHwYDVR0jBBgwFoAUFBAFcgJe/BBuZiGeZ8STfpkgRYQwDQYJKoZI
-hvcNAQELBQADggEBAFGaNiYxg7yC/xauXPlaqLCtwbm2dKyK9nIFbF/7be8mk7Q3
-MOA0of1vGHPLVQLr6bJJpD9MAbUcm4cPAwWaxwcNpxOjYOFDaq10PCK4eRAxZWwF
-NJRIRmGsl8NEsMNTMCy8X+Kyw5EzH4vWFl5Uf2bGKOeFg0zt43jWQVOX6C+aL3Cd
-pRS5MhmYpxMG8irrNOxf4NVFE2zpJOCm3bn0STLhkDcV/ww4zMzObTJhiIb5wSWn
-EXKKWhUXuRt7A2y1KJtXpTbSRHQxE++69Go1tWhXtRiULCJtf7wF2Ksm0RR/AdXT
-1uR1vKyH5KBJPX3ppYkQDukoHTFR0CpB+G84NLo=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZUxCzAJBgNVBAYTAlVT
-MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
-DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
-MSYwJAYDVQQDDB1BbWF6b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQTAeFw0xOTA1
-MTAyMTU4NDNaFw0yNTA2MDExMjAwMDBaMIGQMQswCQYDVQQGEwJVUzETMBEGA1UE
-CAwKV2FzaGluZ3RvbjEQMA4GA1UEBwwHU2VhdHRsZTEiMCAGA1UECgwZQW1hem9u
-IFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzEhMB8GA1UE
-AwwYQW1hem9uIFJEUyBtZS1zb3V0aC0xIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAudOYPZH+ihJAo6hNYMB5izPVBe3TYhnZm8+X3IoaaYiKtsp1
-JJhkTT0CEejYIQ58Fh4QrMUyWvU8qsdK3diNyQRoYLbctsBPgxBR1u07eUJDv38/
-C1JlqgHmMnMi4y68Iy7ymv50QgAMuaBqgEBRI1R6Lfbyrb2YvH5txjJyTVMwuCfd
-YPAtZVouRz0JxmnfsHyxjE+So56uOKTDuw++Ho4HhZ7Qveej7XB8b+PIPuroknd3
-FQB5RVbXRvt5ZcVD4F2fbEdBniF7FAF4dEiofVCQGQ2nynT7dZdEIPfPdH3n7ZmE
-lAOmwHQ6G83OsiHRBLnbp+QZRgOsjkHJxT20bQIDAQABo2YwZDAOBgNVHQ8BAf8E
-BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUOEVDM7VomRH4HVdA
-QvIMNq2tXOcwHwYDVR0jBBgwFoAU54cfDjgwBx4ycBH8+/r8WXdaiqYwDQYJKoZI
-hvcNAQELBQADggEBAHhvMssj+Th8IpNePU6RH0BiL6o9c437R3Q4IEJeFdYL+nZz
-PW/rELDPvLRUNMfKM+KzduLZ+l29HahxefejYPXtvXBlq/E/9czFDD4fWXg+zVou
-uDXhyrV4kNmP4S0eqsAP/jQHPOZAMFA4yVwO9hlqmePhyDnszCh9c1PfJSBh49+b
-4w7i/L3VBOMt8j3EKYvqz0gVfpeqhJwL4Hey8UbVfJRFJMJzfNHpePqtDRAY7yjV
-PYquRaV2ab/E+/7VFkWMM4tazYz/qsYA2jSH+4xDHvYk8LnsbcrF9iuidQmEc5sb
-FgcWaSKG4DJjcI5k7AJLWcXyTDt21Ci43LE+I9Q=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
MIIECDCCAvCgAwIBAgICVIYwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT
MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
@@ -239,6 +671,30 @@ iOghbQQyAEe03MWCyDGtSmDfr0qEk+CHN+6hPiaL8qKt4s+V9P7DeK4iW08ny8Ox
AVS7u0OK/5+jKMAMrKwpYrBydOjTUTHScocyNw==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
+MIIEBjCCAu6gAwIBAgIJAMc0ZzaSUK51MA0GCSqGSIb3DQEBCwUAMIGPMQswCQYD
+VQQGEwJVUzEQMA4GA1UEBwwHU2VhdHRsZTETMBEGA1UECAwKV2FzaGluZ3RvbjEi
+MCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEGA1UECwwKQW1h
+em9uIFJEUzEgMB4GA1UEAwwXQW1hem9uIFJEUyBSb290IDIwMTkgQ0EwHhcNMTkw
+ODIyMTcwODUwWhcNMjQwODIyMTcwODUwWjCBjzELMAkGA1UEBhMCVVMxEDAOBgNV
+BAcMB1NlYXR0bGUxEzARBgNVBAgMCldhc2hpbmd0b24xIjAgBgNVBAoMGUFtYXpv
+biBXZWIgU2VydmljZXMsIEluYy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxIDAeBgNV
+BAMMF0FtYXpvbiBSRFMgUm9vdCAyMDE5IENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEArXnF/E6/Qh+ku3hQTSKPMhQQlCpoWvnIthzX6MK3p5a0eXKZ
+oWIjYcNNG6UwJjp4fUXl6glp53Jobn+tWNX88dNH2n8DVbppSwScVE2LpuL+94vY
+0EYE/XxN7svKea8YvlrqkUBKyxLxTjh+U/KrGOaHxz9v0l6ZNlDbuaZw3qIWdD/I
+6aNbGeRUVtpM6P+bWIoxVl/caQylQS6CEYUk+CpVyJSkopwJlzXT07tMoDL5WgX9
+O08KVgDNz9qP/IGtAcRduRcNioH3E9v981QO1zt/Gpb2f8NqAjUUCUZzOnij6mx9
+McZ+9cWX88CRzR0vQODWuZscgI08NvM69Fn2SQIDAQABo2MwYTAOBgNVHQ8BAf8E
+BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUc19g2LzLA5j0Kxc0LjZa
+pmD/vB8wHwYDVR0jBBgwFoAUc19g2LzLA5j0Kxc0LjZapmD/vB8wDQYJKoZIhvcN
+AQELBQADggEBAHAG7WTmyjzPRIM85rVj+fWHsLIvqpw6DObIjMWokpliCeMINZFV
+ynfgBKsf1ExwbvJNzYFXW6dihnguDG9VMPpi2up/ctQTN8tm9nDKOy08uNZoofMc
+NUZxKCEkVKZv+IL4oHoeayt8egtv3ujJM6V14AstMQ6SwvwvA93EP/Ug2e4WAXHu
+cbI1NAbUgVDqp+DRdfvZkgYKryjTWd/0+1fS8X1bBZVWzl7eirNVnHbSH2ZDpNuY
+0SBd8dj5F6ld3t58ydZbrTHze7JJOd8ijySAp4/kiu9UfZWuTPABzDa/DSdz9Dk/
+zPW4CXXvhLmE02TA9/HeCw3KEHIwicNuEfw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
MIIEBzCCAu+gAwIBAgICQ2QwDQYJKoZIhvcNAQELBQAwgY8xCzAJBgNVBAYTAlVT
MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
@@ -622,2407 +1078,3 @@ E1LaAUCmCZBVi9fIe0H2r9whIh4uLWZA41oMnJx/MOmo3XyMfQoWcqaSFlMqfZM4
h2XBHKxQ1Y4HgAn0jACP2QSPEmuoQEIa57bEKEcZsBR8SDY6ZdTd2HLRIApcCOSF
MRM8CKLeF658I0XgF8D5EsYoKPsA+74Z+jDH
-----END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEETCCAvmgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT
-MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
-DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
-MSUwIwYDVQQDDBxBbWF6b24gUkRTIEJldGEgUm9vdCAyMDE5IENBMB4XDTE5MDgy
-MDE3MTAwN1oXDTI0MDgxOTE3MzgyNlowgZkxCzAJBgNVBAYTAlVTMRMwEQYDVQQI
-DApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSIwIAYDVQQKDBlBbWF6b24g
-V2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMSowKAYDVQQD
-DCFBbWF6b24gUkRTIEJldGEgdXMtZWFzdC0xIDIwMTkgQ0EwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQDTNCOlotQcLP8TP82U2+nk0bExVuuMVOgFeVMx
-vbUHZQeIj9ikjk+jm6eTDnnkhoZcmJiJgRy+5Jt69QcRbb3y3SAU7VoHgtraVbxF
-QDh7JEHI9tqEEVOA5OvRrDRcyeEYBoTDgh76ROco2lR+/9uCvGtHVrMCtG7BP7ZB
-sSVNAr1IIRZZqKLv2skKT/7mzZR2ivcw9UeBBTUf8xsfiYVBvMGoEsXEycjYdf6w
-WV+7XS7teNOc9UgsFNN+9AhIBc1jvee5E//72/4F8pAttAg/+mmPUyIKtekNJ4gj
-OAR2VAzGx1ybzWPwIgOudZFHXFduxvq4f1hIRPH0KbQ/gkRrAgMBAAGjZjBkMA4G
-A1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTkvpCD
-6C43rar9TtJoXr7q8dkrrjAfBgNVHSMEGDAWgBStoQwVpbGx87fxB3dEGDqKKnBT
-4TANBgkqhkiG9w0BAQsFAAOCAQEAJd9fOSkwB3uVdsS+puj6gCER8jqmhd3g/J5V
-Zjk9cKS8H0e8pq/tMxeJ8kpurPAzUk5RkCspGt2l0BSwmf3ahr8aJRviMX6AuW3/
-g8aKplTvq/WMNGKLXONa3Sq8591J+ce8gtOX/1rDKmFI4wQ/gUzOSYiT991m7QKS
-Fr6HMgFuz7RNJbb3Fy5cnurh8eYWA7mMv7laiLwTNsaro5qsqErD5uXuot6o9beT
-a+GiKinEur35tNxAr47ax4IRubuIzyfCrezjfKc5raVV2NURJDyKP0m0CCaffAxE
-qn2dNfYc3v1D8ypg3XjHlOzRo32RB04o8ALHMD9LSwsYDLpMag==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEFzCCAv+gAwIBAgICFSUwDQYJKoZIhvcNAQELBQAwgZcxCzAJBgNVBAYTAlVT
-MRAwDgYDVQQHDAdTZWF0dGxlMRMwEQYDVQQIDApXYXNoaW5ndG9uMSIwIAYDVQQK
-DBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRT
-MSgwJgYDVQQDDB9BbWF6b24gUkRTIFByZXZpZXcgUm9vdCAyMDE5IENBMB4XDTE5
-MDgyMTIyMzk0N1oXDTI0MDgyMTIyMjk0OVowgZwxCzAJBgNVBAYTAlVTMRMwEQYD
-VQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMSIwIAYDVQQKDBlBbWF6
-b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMS0wKwYD
-VQQDDCRBbWF6b24gUkRTIFByZXZpZXcgdXMtZWFzdC0yIDIwMTkgQ0EwggEiMA0G
-CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD0dB/U7qRnSf05wOi7m10Pa2uPMTJv
-r6U/3Y17a5prq5Zr4++CnSUYarG51YuIf355dKs+7Lpzs782PIwCmLpzAHKWzix6
-pOaTQ+WZ0+vUMTxyqgqWbsBgSCyP7pVBiyqnmLC/L4az9XnscrbAX4pNaoJxsuQe
-mzBo6yofjQaAzCX69DuqxFkVTRQnVy7LCFkVaZtjNAftnAHJjVgQw7lIhdGZp9q9
-IafRt2gteihYfpn+EAQ/t/E4MnhrYs4CPLfS7BaYXBycEKC5Muj1l4GijNNQ0Efo
-xG8LSZz7SNgUvfVwiNTaqfLP3AtEAWiqxyMyh3VO+1HpCjT7uNBFtmF3AgMBAAGj
-ZjBkMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQW
-BBQtinkdrj+0B2+qdXngV2tgHnPIujAfBgNVHSMEGDAWgBRp0xqULkNh/w2ZVzEI
-o2RIY7O03TANBgkqhkiG9w0BAQsFAAOCAQEAtJdqbCxDeMc8VN1/RzCabw9BIL/z
-73Auh8eFTww/sup26yn8NWUkfbckeDYr1BrXa+rPyLfHpg06kwR8rBKyrs5mHwJx
-bvOzXD/5WTdgreB+2Fb7mXNvWhenYuji1MF+q1R2DXV3I05zWHteKX6Dajmx+Uuq
-Yq78oaCBSV48hMxWlp8fm40ANCL1+gzQ122xweMFN09FmNYFhwuW+Ao+Vv90ZfQG
-PYwTvN4n/gegw2TYcifGZC2PNX74q3DH03DXe5fvNgRW5plgz/7f+9mS+YHd5qa9
-tYTPUvoRbi169ou6jicsMKUKPORHWhiTpSCWR1FMMIbsAcsyrvtIsuaGCQ==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/jCCAuagAwIBAgIQdOCSuA9psBpQd8EI368/0DANBgkqhkiG9w0BAQsFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIHNhLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTE5MTgwNjI2WhgPMjA2MTA1MTkxOTA2MjZaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgc2EtZWFzdC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN6ftL6w8v3dB2yW
-LjCxSP1D7ZsOTeLZOSCz1Zv0Gkd0XLhil5MdHOHBvwH/DrXqFU2oGzCRuAy+aZis
-DardJU6ChyIQIciXCO37f0K23edhtpXuruTLLwUwzeEPdcnLPCX+sWEn9Y5FPnVm
-pCd6J8edH2IfSGoa9LdErkpuESXdidLym/w0tWG/O2By4TabkNSmpdrCL00cqI+c
-prA8Bx1jX8/9sY0gpAovtuFaRN+Ivg3PAnWuhqiSYyQ5nC2qDparOWuDiOhpY56E
-EgmTvjwqMMjNtExfYx6Rv2Ndu50TriiNKEZBzEtkekwXInTupmYTvc7U83P/959V
-UiQ+WSMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU4uYHdH0+
-bUeh81Eq2l5/RJbW+vswDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB
-AQBhxcExJ+w74bvDknrPZDRgTeMLYgbVJjx2ExH7/Ac5FZZWcpUpFwWMIJJxtewI
-AnhryzM3tQYYd4CG9O+Iu0+h/VVfW7e4O3joWVkxNMb820kQSEwvZfA78aItGwOY
-WSaFNVRyloVicZRNJSyb1UL9EiJ9ldhxm4LTT0ax+4ontI7zTx6n6h8Sr6r/UOvX
-d9T5aUUENWeo6M9jGupHNn3BobtL7BZm2oS8wX8IVYj4tl0q5T89zDi2x0MxbsIV
-5ZjwqBQ5JWKv7ASGPb+z286RjPA9R2knF4lJVZrYuNV90rHvI/ECyt/JrDqeljGL
-BLl1W/UsvZo6ldLIpoMbbrb5
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEBDCCAuygAwIBAgIQUfVbqapkLYpUqcLajpTJWzANBgkqhkiG9w0BAQsFADCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIG1lLWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV
-BAcMB1NlYXR0bGUwIBcNMjIwNTA2MjMyMDA5WhgPMjA2MjA1MDcwMDIwMDlaMIGa
-MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j
-LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt
-YXpvbiBSRFMgbWUtY2VudHJhbC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJIeovu3
-ewI9FVitXMQzvkh34aQ6WyI4NO3YepfJaePiv3cnyFGYHN2S1cR3UQcLWgypP5va
-j6bfroqwGbCbZZcb+6cyOB4ceKO9Ws1UkcaGHnNDcy5gXR7aCW2OGTUfinUuhd2d
-5bOGgV7JsPbpw0bwJ156+MwfOK40OLCWVbzy8B1kITs4RUPNa/ZJnvIbiMu9rdj4
-8y7GSFJLnKCjlOFUkNI5LcaYvI1+ybuNgphT3nuu5ZirvTswGakGUT/Q0J3dxP0J
-pDfg5Sj/2G4gXiaM0LppVOoU5yEwVewhQ250l0eQAqSrwPqAkdTg9ng360zqCFPE
-JPPcgI1tdGUgneECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
-/2AJVxWdZxc8eJgdpbwpW7b0f7IwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
-CwUAA4IBAQBYm63jTu2qYKJ94gKnqc+oUgqmb1mTXmgmp/lXDbxonjszJDOXFbri
-3CCO7xB2sg9bd5YWY8sGKHaWmENj3FZpCmoefbUx++8D7Mny95Cz8R32rNcwsPTl
-ebpd9A/Oaw5ug6M0x/cNr0qzF8Wk9Dx+nFEimp8RYQdKvLDfNFZHjPa1itnTiD8M
-TorAqj+VwnUGHOYBsT/0NY12tnwXdD+ATWfpEHdOXV+kTMqFFwDyhfgRVNpTc+os
-ygr8SwhnSCpJPB/EYl2S7r+tgAbJOkuwUvGT4pTqrzDQEhwE7swgepnHC87zhf6l
-qN6mVpSnQKQLm6Ob5TeCEFgcyElsF5bH
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjSgAwIBAgIRAOxu0I1QuMAhIeszB3fJIlkwCgYIKoZIzj0EAwMwgZYx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h
-em9uIFJEUyB1cy13ZXN0LTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTI0MjIwNjU5WhgPMjEyMTA1MjQyMzA2NTlaMIGWMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS
-RFMgdXMtd2VzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEz4bylRcGqqDWdP7gQIIoTHdBK6FNtKH1
-4SkEIXRXkYDmRvL9Bci1MuGrwuvrka5TDj4b7e+csY0llEzHpKfq6nJPFljoYYP9
-uqHFkv77nOpJJ633KOr8IxmeHW5RXgrZo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G
-A1UdDgQWBBQQikVz8wmjd9eDFRXzBIU8OseiGzAOBgNVHQ8BAf8EBAMCAYYwCgYI
-KoZIzj0EAwMDaAAwZQIwf06Mcrpw1O0EBLBBrp84m37NYtOkE/0Z0O+C7D41wnXi
-EQdn6PXUVgdD23Gj82SrAjEAklhKs+liO1PtN15yeZR1Io98nFve+lLptaLakZcH
-+hfFuUtCqMbaI8CdvJlKnPqT
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCTCCA/GgAwIBAgIRALyWMTyCebLZOGcZZQmkmfcwDQYJKoZIhvcNAQEMBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI0MjAyODAzWhgPMjEyMTA1MjQyMTI4MDNa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTMgUm9vdCBDQSBSU0E0MDk2IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
-wGFiyDyCrGqgdn4fXG12cxKAAfVvhMea1mw5h9CVRoavkPqhzQpAitSOuMB9DeiP
-wQyqcsiGl/cTEau4L+AUBG8b9v26RlY48exUYBXj8CieYntOT9iNw5WtdYJa3kF/
-JxgI+HDMzE9cmHDs5DOO3S0uwZVyra/xE1ymfSlpOeUIOTpHRJv97CBUEpaZMUW5
-Sr6GruuOwFVpO5FX3A/jQlcS+UN4GjSRgDUJuqg6RRQldEZGCVCCmodbByvI2fGm
-reGpsPJD54KkmAX08nOR8e5hkGoHxq0m2DLD4SrOFmt65vG47qnuwplWJjtk9B3Z
-9wDoopwZLBOtlkPIkUllWm1P8EuHC1IKOA+wSP6XdT7cy8S77wgyHzR0ynxv7q/l
-vlZtH30wnNqFI0y9FeogD0TGMCHcnGqfBSicJXPy9T4fU6f0r1HwqKwPp2GArwe7
-dnqLTj2D7M9MyVtFjEs6gfGWXmu1y5uDrf+CszurE8Cycoma+OfjjuVQgWOCy7Nd
-jJswPxAroTzVfpgoxXza4ShUY10woZu0/J+HmNmqK7lh4NS75q1tz75in8uTZDkV
-be7GK+SEusTrRgcf3tlgPjSTWG3veNzFDF2Vn1GLJXmuZfhdlVQDBNXW4MNREExS
-dG57kJjICpT+r8X+si+5j51gRzkSnMYs7VHulpxfcwECAwEAAaNCMEAwDwYDVR0T
-AQH/BAUwAwEB/zAdBgNVHQ4EFgQU4JWOpDBmUBuWKvGPZelw87ezhL8wDgYDVR0P
-AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQBRNLMql7itvXSEFQRAnyOjivHz
-l5IlWVQjAbOUr6ogZcwvK6YpxNAFW5zQr8F+fdkiypLz1kk5irx9TIpff0BWC9hQ
-/odMPO8Gxn8+COlSvc+dLsF2Dax3Hvz0zLeKMo+cYisJOzpdR/eKd0/AmFdkvQoM
-AOK9n0yYvVJU2IrSgeJBiiCarpKSeAktEVQ4rvyacQGr+QAPkkjRwm+5LHZKK43W
-nNnggRli9N/27qYtc5bgr3AaQEhEXMI4RxPRXCLsod0ehMGWyRRK728a+6PMMJAJ
-WHOU0x7LCEMPP/bvpLj3BdvSGqNor4ZtyXEbwREry1uzsgODeRRns5acPwTM6ff+
-CmxO2NZ0OktIUSYRmf6H/ZFlZrIhV8uWaIwEJDz71qvj7buhQ+RFDZ9CNL64C0X6
-mf0zJGEpddjANHaaVky+F4gYMtEy2K2Lcm4JGTdyIzUoIe+atzCnRp0QeIcuWtF+
-s8AjDYCVFNypcMmqbRmNpITSnOoCHSRuVkY3gutVoYyMLbp8Jm9SJnCIlEWTA6Rm
-wADOMGZJVn5/XRTRuetVOB3KlQDjs9OO01XN5NzGSZO2KT9ngAUfh9Eqhf1iRWSP
-nZlRbQ2NRCuY/oJ5N59mLGxnNJSE7giEKEBRhTQ/XEPIUYAUPD5fca0arKRJwbol
-l9Se1Hsq0ZU5f+OZKQ==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGATCCA+mgAwIBAgIRAK7vlRrGVEePJpW1VHMXdlIwDQYJKoZIhvcNAQEMBQAw
-gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo
-QW1hem9uIFJEUyBhZi1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMTA1MTkxOTI4NDNaGA8yMTIxMDUxOTIwMjg0M1owgZgx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h
-em9uIFJEUyBhZi1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMZiHOQC6x4o
-eC7vVOMCGiN5EuLqPYHdceFPm4h5k/ZejXTf7kryk6aoKZKsDIYihkaZwXVS7Y/y
-7Ig1F1ABi2jD+CYprj7WxXbhpysmN+CKG7YC3uE4jSvfvUnpzionkQbjJsRJcrPO
-cZJM4FVaVp3mlHHtvnM+K3T+ni4a38nAd8xrv1na4+B8ZzZwWZXarfg8lJoGskSn
-ou+3rbGQ0r+XlUP03zWujHoNlVK85qUIQvDfTB7n3O4s1XNGvkfv3GNBhYRWJYlB
-4p8T+PFN8wG+UOByp1gV7BD64RnpuZ8V3dRAlO6YVAmINyG5UGrPzkIbLtErUNHO
-4iSp4UqYvztDqJWWHR/rA84ef+I9RVwwZ8FQbjKq96OTnPrsr63A5mXTC9dXKtbw
-XNJPQY//FEdyM3K8sqM0IdCzxCA1MXZ8+QapWVjwyTjUwFvL69HYky9H8eAER59K
-5I7u/CWWeCy2R1SYUBINc3xxLr0CGGukcWPEZW2aPo5ibW5kepU1P/pzdMTaTfao
-F42jSFXbc7gplLcSqUgWwzBnn35HLTbiZOFBPKf6vRRu8aRX9atgHw/EjCebi2xP
-xIYr5Ub8u0QVHIqcnF1/hVzO/Xz0chj3E6VF/yTXnsakm+W1aM2QkZbFGpga+LMy
-mFCtdPrELjea2CfxgibaJX1Q4rdEpc8DAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFDSaycEyuspo/NOuzlzblui8KotFMA4GA1UdDwEB/wQEAwIB
-hjANBgkqhkiG9w0BAQwFAAOCAgEAbosemjeTRsL9o4v0KadBUNS3V7gdAH+X4vH2
-Ee1Jc91VOGLdd/s1L9UX6bhe37b9WjUD69ur657wDW0RzxMYgQdZ27SUl0tEgGGp
-cCmVs1ky3zEN+Hwnhkz+OTmIg1ufq0W2hJgJiluAx2r1ib1GB+YI3Mo3rXSaBYUk
-bgQuujYPctf0PA153RkeICE5GI3OaJ7u6j0caYEixBS3PDHt2MJWexITvXGwHWwc
-CcrC05RIrTUNOJaetQw8smVKYOfRImEzLLPZ5kf/H3Cbj8BNAFNsa10wgvlPuGOW
-XLXqzNXzrG4V3sjQU5YtisDMagwYaN3a6bBf1wFwFIHQoAPIgt8q5zaQ9WI+SBns
-Il6rd4zfvjq/BPmt0uI7rVg/cgbaEg/JDL2neuM9CJAzmKxYxLQuHSX2i3Fy4Y1B
-cnxnRQETCRZNPGd00ADyxPKVoYBC45/t+yVusArFt+2SVLEGiFBr23eG2CEZu+HS
-nDEgIfQ4V3YOTUNa86wvbAss1gbbnT/v1XCnNGClEWCWNCSRjwV2ZmQ/IVTmNHPo
-7axTTBBJbKJbKzFndCnuxnDXyytdYRgFU7Ly3sa27WS2KFyFEDebLFRHQEfoYqCu
-IupSqBSbXsR3U10OTjc9z6EPo1nuV6bdz+gEDthmxKa1NI+Qb1kvyliXQHL2lfhr
-5zT5+Bs=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/zCCA+egAwIBAgIRAOLV6zZcL4IV2xmEneN1GwswDQYJKoZIhvcNAQEMBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyB1cy13ZXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUxOTE5MDg1OFoYDzIxMjEwNTE5MjAwODU4WjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIHVzLXdlc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC7koAKGXXlLixN
-fVjhuqvz0WxDeTQfhthPK60ekRpftkfE5QtnYGzeovaUAiS58MYVzqnnTACDwcJs
-IGTFE6Wd7sB6r8eI/3CwI1pyJfxepubiQNVAQG0zJETOVkoYKe/5KnteKtnEER3X
-tCBRdV/rfbxEDG9ZAsYfMl6zzhEWKF88G6xhs2+VZpDqwJNNALvQuzmTx8BNbl5W
-RUWGq9CQ9GK9GPF570YPCuURW7kl35skofudE9bhURNz51pNoNtk2Z3aEeRx3ouT
-ifFJlzh+xGJRHqBG7nt5NhX8xbg+vw4xHCeq1aAe6aVFJ3Uf9E2HzLB4SfIT9bRp
-P7c9c0ySGt+3n+KLSHFf/iQ3E4nft75JdPjeSt0dnyChi1sEKDi0tnWGiXaIg+J+
-r1ZtcHiyYpCB7l29QYMAdD0TjfDwwPayLmq//c20cPmnSzw271VwqjUT0jYdrNAm
-gV+JfW9t4ixtE3xF2jaUh/NzL3bAmN5v8+9k/aqPXlU1BgE3uPwMCjrfn7V0I7I1
-WLpHyd9jF3U/Ysci6H6i8YKgaPiOfySimQiDu1idmPld659qerutUSemQWmPD3bE
-dcjZolmzS9U0Ujq/jDF1YayN3G3xvry1qWkTci0qMRMu2dZu30Herugh9vsdTYkf
-00EqngPbqtIVLDrDjEQLqPcb8QvWFQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBQBqg8Za/L0YMHURGExHfvPyfLbOTAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQEMBQADggIBACAGPMa1QL7P/FIO7jEtMelJ0hQlQepKnGtbKz4r
-Xq1bUX1jnLvnAieR9KZmeQVuKi3g3CDU6b0mDgygS+FL1KDDcGRCSPh238Ou8KcG
-HIxtt3CMwMHMa9gmdcMlR5fJF9vhR0C56KM2zvyelUY51B/HJqHwGvWuexryXUKa
-wq1/iK2/d9mNeOcjDvEIj0RCMI8dFQCJv3PRCTC36XS36Tzr6F47TcTw1c3mgKcs
-xpcwt7ezrXMUunzHS4qWAA5OGdzhYlcv+P5GW7iAA7TDNrBF+3W4a/6s9v2nQAnX
-UvXd9ul0ob71377UhZbJ6SOMY56+I9cJOOfF5QvaL83Sz29Ij1EKYw/s8TYdVqAq
-+dCyQZBkMSnDFLVe3J1KH2SUSfm3O98jdPORQrUlORQVYCHPls19l2F6lCmU7ICK
-hRt8EVSpXm4sAIA7zcnR2nU00UH8YmMQLnx5ok9YGhuh3Ehk6QlTQLJux6LYLskd
-9YHOLGW/t6knVtV78DgPqDeEx/Wu/5A8R0q7HunpWxr8LCPBK6hksZnOoUhhb8IP
-vl46Ve5Tv/FlkyYr1RTVjETmg7lb16a8J0At14iLtpZWmwmuv4agss/1iBVMXfFk
-+ZGtx5vytWU5XJmsfKA51KLsMQnhrLxb3X3zC+JRCyJoyc8++F3YEcRi2pkRYE3q
-Hing
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/zCCAuegAwIBAgIRAI+asxQA/MB1cGyyrC0MPpkwDQYJKoZIhvcNAQELBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyBjYS13ZXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIzMDkxMzIwMjEzNFoYDzIwNjMwOTEzMjEyMTMzWjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGNhLXdlc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMHvQITTZcfl2O
-yfzRIAPKwzzlc8eXWdXef7VUsbezg3lm9RC+vArO4JuAzta/aLw1D94wPSRm9JXX
-NkP3obO6Ql80/0doooU6BAPceD0xmEWC4aCFT/5KWsD6Sy2/Rjwq3NKBTwzxLwYK
-GqVsBp8AdrzDTmdRETC+Dg2czEo32mTDAA1uMgqrz6xxeTYroj8NTSTp6jfE6C0n
-YgzYmVQCEIjHqI49j7k3jfT3P2skCVKGJwQzoZnerFacKzXsDB18uIqU7NaMc2cX
-kOd0gRqpyKOzAHU2m5/S4jw4UHdkoI3E7nkayuen8ZPKH2YqWtTXUrXGhSTT34nX
-yiFgu+vTAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFHzz1NTd
-TOm9zAv4d8l6XCFKSdJfMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
-AQEAodBvd0cvXQYhFBef2evnuI9XA+AC/Q9P1nYtbp5MPA4aFhy5v9rjW8wwJX14
-l+ltd2o3tz8PFDBZ1NX2ooiWVlZthQxKn1/xDVKsTXHbYUXItPQ3jI5IscB5IML8
-oCzAbkoLXsSPNOVFP5P4l4cZEMqHGRnBag7hLJZvmvzZSBnz+ioC2jpjVluF8kDX
-fQGNjqPECik68CqbSV0SaQ0cgEoYTDjwON5ZLBeS8sxR2abE/gsj4VFYl5w/uEBd
-w3Tt9uGfIy+wd2tNj6isGC6PcbPMjA31jd+ifs2yNzigqkcYTTWFtnvh4a8xiecm
-GHu2EgH0Jqzz500N7L3uQdPkdg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIRANxgyBbnxgTEOpDul2ZnC0UwDQYJKoZIhvcNAQELBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMyBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNjEwMTgxOTA3WhgPMjA2MTA2MTAxOTE5MDda
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTMgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-xnwSDAChrMkfk5TA4Dk8hKzStDlSlONzmd3fTG0Wqr5+x3EmFT6Ksiu/WIwEl9J2
-K98UI7vYyuZfCxUKb1iMPeBdVGqk0zb92GpURd+Iz/+K1ps9ZLeGBkzR8mBmAi1S
-OfpwKiTBzIv6E8twhEn4IUpHsdcuX/2Y78uESpJyM8O5CpkG0JaV9FNEbDkJeBUQ
-Ao2qqNcH4R0Qcr5pyeqA9Zto1RswgL06BQMI9dTpfwSP5VvkvcNUaLl7Zv5WzLQE
-JzORWePvdPzzvWEkY/3FPjxBypuYwssKaERW0fkPDmPtykktP9W/oJolKUFI6pXp
-y+Y6p6/AVdnQD2zZjW5FhQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBT+jEKs96LC+/X4BZkUYUkzPfXdqTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
-hvcNAQELBQADggEBAIGQqgqcQ6XSGkmNebzR6DhadTbfDmbYeN5N0Vuzv+Tdmufb
-tMGjdjnYMg4B+IVnTKQb+Ox3pL9gbX6KglGK8HupobmIRtwKVth+gYYz3m0SL/Nk
-haWPYzOm0x3tJm8jSdufJcEob4/ATce9JwseLl76pSWdl5A4lLjnhPPKudUDfH+1
-BLNUi3lxpp6GkC8aWUPtupnhZuXddolTLOuA3GwTZySI44NfaFRm+o83N1jp+EwD
-6e94M4cTRzjUv6J3MZmSbdtQP/Tk1uz2K4bQZGP0PZC3bVpqiesdE/xr+wbu8uHr
-cM1JXH0AmXf1yIkTgyWzmvt0k1/vgcw5ixAqvvE=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEATCCAumgAwIBAgIRAMhw98EQU18mIji+unM2YH8wDQYJKoZIhvcNAQELBQAw
-gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo
-QW1hem9uIFJEUyBhcC1zb3V0aC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMjA2MDYyMTQyMjJaGA8yMDYyMDYwNjIyNDIyMlowgZgx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h
-em9uIFJEUyBhcC1zb3V0aC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIeeRoLfTm+7
-vqm7ZlFSx+1/CGYHyYrOOryM4/Z3dqYVHFMgWTR7V3ziO8RZ6yUanrRcWVX3PZbF
-AfX0KFE8OgLsXEZIX8odSrq86+/Th5eZOchB2fDBsUB7GuN2rvFBbM8lTI9ivVOU
-lbuTnYyb55nOXN7TpmH2bK+z5c1y9RVC5iQsNAl6IJNvSN8VCqXh31eK5MlKB4DT
-+Y3OivCrSGsjM+UR59uZmwuFB1h+icE+U0p9Ct3Mjq3MzSX5tQb6ElTNGlfmyGpW
-Kh7GQ5XU1KaKNZXoJ37H53woNSlq56bpVrKI4uv7ATpdpFubOnSLtpsKlpLdR3sy
-Ws245200pC8CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUp0ki
-6+eWvsnBjQhMxwMW5pwn7DgwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUA
-A4IBAQB2V8lv0aqbYQpj/bmVv/83QfE4vOxKCJAHv7DQ35cJsTyBdF+8pBczzi3t
-3VNL5IUgW6WkyuUOWnE0eqAFOUVj0yTS1jSAtfl3vOOzGJZmWBbqm9BKEdu1D8O6
-sB8bnomwiab2tNDHPmUslpdDqdabbkWwNWzLJ97oGFZ7KNODMEPXWKWNxg33iHfS
-/nlmnrTVI3XgaNK9qLZiUrxu9Yz5gxi/1K+sG9/Dajd32ZxjRwDipOLiZbiXQrsd
-qzIMY4GcWf3g1gHL5mCTfk7dG22h/rhPyGV0svaDnsb+hOt6sv1McMN6Y3Ou0mtM
-/UaAXojREmJmTSCNvs2aBny3/2sy
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjSgAwIBAgIRAMnRxsKLYscJV8Qv5pWbL7swCgYIKoZIzj0EAwMwgZYx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h
-em9uIFJEUyBzYS1lYXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTE5MTgxNjAxWhgPMjEyMTA1MTkxOTE2MDFaMIGWMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS
-RFMgc2EtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEjFOCZgTNVKxLKhUxffiDEvTLFhrmIqdO
-dKqVdgDoELEzIHWDdC+19aDPitbCYtBVHl65ITu/9pn6mMUl5hhUNtfZuc6A+Iw1
-sBe0v0qI3y9Q9HdQYrGgeHDh8M5P7E2ho0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G
-A1UdDgQWBBS5L7/8M0TzoBZk39Ps7BkfTB4yJTAOBgNVHQ8BAf8EBAMCAYYwCgYI
-KoZIzj0EAwMDaAAwZQIwI43O0NtWKTgnVv9z0LO5UMZYgSve7GvGTwqktZYCMObE
-rUI4QerXM9D6JwLy09mqAjEAypfkdLyVWtaElVDUyHFkihAS1I1oUxaaDrynLNQK
-Ou/Ay+ns+J+GyvyDUjBpVVW1
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/jCCA+agAwIBAgIQR71Z8lTO5Sj+as2jB7IWXzANBgkqhkiG9w0BAQwFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIHVzLXdlc3QtMiBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTI0MjIwMzIwWhgPMjEyMTA1MjQyMzAzMjBaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgdXMtd2VzdC0yIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM977bHIs1WJijrS
-XQMfUOhmlJjr2v0K0UjPl52sE1TJ76H8umo1yR4T7Whkd9IwBHNGKXCJtJmMr9zp
-fB38eLTu+5ydUAXdFuZpRMKBWwPVe37AdJRKqn5beS8HQjd3JXAgGKUNNuE92iqF
-qi2fIqFMpnJXWo0FIW6s2Dl2zkORd7tH0DygcRi7lgVxCsw1BJQhFJon3y+IV8/F
-bnbUXSNSDUnDW2EhvWSD8L+t4eiXYsozhDAzhBvojpxhPH9OB7vqFYw5qxFx+G0t
-lSLX5iWi1jzzc3XyGnB6WInZDVbvnvJ4BGZ+dTRpOCvsoMIn9bz4EQTvu243c7aU
-HbS/kvnCASNt+zk7C6lbmaq0AGNztwNj85Opn2enFciWZVnnJ/4OeefUWQxD0EPp
-SjEd9Cn2IHzkBZrHCg+lWZJQBKbUVS0lLIMSsLQQ6WvR38jY7D2nxM1A93xWxwpt
-ZtQnYRCVXH6zt2OwDAFePInWwxUjR5t/wu3XxPgpSfrmTi3WYtr1wFypAJ811e/P
-yBtswWUQ6BNJQvy+KnOEeGfOwmtdDFYR+GOCfvCihzrKJrxOtHIieehR5Iw3cbXG
-sm4pDzfMUVvDDz6C2M6PRlJhhClbatHCjik9hxFYEsAlqtVVK9pxaz9i8hOqSFQq
-kJSQsgWw+oM/B2CyjcSqkSQEu8RLAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w
-HQYDVR0OBBYEFPmrdxpRRgu3IcaB5BTqlprcKdTsMA4GA1UdDwEB/wQEAwIBhjAN
-BgkqhkiG9w0BAQwFAAOCAgEAVdlxWjPvVKky3kn8ZizeM4D+EsLw9dWLau2UD/ls
-zwDCFoT6euagVeCknrn+YEl7g20CRYT9iaonGoMUPuMR/cdtPL1W/Rf40PSrGf9q
-QuxavWiHLEXOQTCtCaVZMokkvjuuLNDXyZnstgECuiZECTwhexUF4oiuhyGk9o01
-QMaiz4HX4lgk0ozALUvEzaNd9gWEwD2qe+rq9cQMTVq3IArUkvTIftZUaVUMzr0O
-ed1+zAsNa9nJhURJ/6anJPJjbQgb5qA1asFcp9UaMT1ku36U3gnR1T/BdgG2jX3X
-Um0UcaGNVPrH1ukInWW743pxWQb7/2sumEEMVh+jWbB18SAyLI4WIh4lkurdifzS
-IuTFp8TEx+MouISFhz/vJDWZ84tqoLVjkEcP6oDypq9lFoEzHDJv3V1CYcIgOusT
-k1jm9P7BXdTG7TYzUaTb9USb6bkqkD9EwJAOSs7DI94aE6rsSws2yAHavjAMfuMZ
-sDAZvkqS2Qg2Z2+CI6wUZn7mzkJXbZoqRjDvChDXEB1mIhzVXhiNW/CR5WKVDvlj
-9v1sdGByh2pbxcLQtVaq/5coM4ANgphoNz3pOYUPWHS+JUrIivBZ+JobjXcxr3SN
-9iDzcu5/FVVNbq7+KN/nvPMngT+gduEN5m+EBjm8GukJymFG0m6BENRA0QSDqZ7k
-zDY=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIRAK5EYG3iHserxMqgg+0EFjgwDQYJKoZIhvcNAQELBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI0MjAyMzE2WhgPMjA2MTA1MjQyMTIzMTZa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTMgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-s1L6TtB84LGraLHVC+rGPhLBW2P0oN/91Rq3AnYwqDOuTom7agANwEjvLq7dSRG/
-sIfZsSV/ABTgArZ5sCmLjHFZAo8Kd45yA9byx20RcYtAG8IZl+q1Cri+s0XefzyO
-U6mlfXZkVe6lzjlfXBkrlE/+5ifVbJK4dqOS1t9cWIpgKqv5fbE6Qbq4LVT+5/WM
-Vd2BOljuBMGMzdZubqFKFq4mzTuIYfnBm7SmHlZfTdfBYPP1ScNuhpjuzw4n3NCR
-EdU6dQv04Q6th4r7eiOCwbWI9LkmVbvBe3ylhH63lApC7MiiPYLlB13xBubVHVhV
-q1NHoNTi+zA3MN9HWicRxQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBSuxoqm0/wjNiZLvqv+JlQwsDvTPDAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
-hvcNAQELBQADggEBAFfTK/j5kv90uIbM8VaFdVbr/6weKTwehafT0pAk1bfLVX+7
-uf8oHgYiyKTTl0DFQicXejghXTeyzwoEkWSR8c6XkhD5vYG3oESqmt/RGvvoxz11
-rHHy7yHYu7RIUc3VQG60c4qxXv/1mWySGwVwJrnuyNT9KZXPevu3jVaWOVHEILaK
-HvzQ2YEcWBPmde/zEseO2QeeGF8FL45Q1d66wqIP4nNUd2pCjeTS5SpB0MMx7yi9
-ki1OH1pv8tOuIdimtZ7wkdB8+JSZoaJ81b8sRrydRwJyvB88rftuI3YB4WwGuONT
-ZezUPsmaoK69B0RChB0ofDpAaviF9V3xOWvVZfo=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGDzCCA/egAwIBAgIRAI0sMNG2XhaBMRN3zD7ZyoEwDQYJKoZIhvcNAQEMBQAw
-gZ8xCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE4MDYGA1UEAwwv
-QW1hem9uIFJEUyBQcmV2aWV3IHVzLWVhc3QtMiBSb290IENBIFJTQTQwOTYgRzEx
-EDAOBgNVBAcMB1NlYXR0bGUwIBcNMjEwNTE4MjA1NzUwWhgPMjEyMTA1MTgyMTU3
-NTBaMIGfMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
-cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExODA2BgNV
-BAMML0FtYXpvbiBSRFMgUHJldmlldyB1cy1lYXN0LTIgUm9vdCBDQSBSU0E0MDk2
-IEcxMRAwDgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
-CgKCAgEAh/otSiCu4Uw3hu7OJm0PKgLsLRqBmUS6jihcrkxfN2SHmp2zuRflkweU
-BhMkebzL+xnNvC8okzbgPWtUxSmDnIRhE8J7bvSKFlqs/tmEdiI/LMqe/YIKcdsI
-20UYmvyLIjtDaJIh598SHHlF9P8DB5jD8snJfhxWY+9AZRN+YVTltgQAAgayxkWp
-M1BbvxpOnz4CC00rE0eqkguXIUSuobb1vKqdKIenlYBNxm2AmtgvQfpsBIQ0SB+8
-8Zip8Ef5rtjSw5J3s2Rq0aYvZPfCVIsKYepIboVwXtD7E9J31UkB5onLBQlaHaA6
-XlH4srsMmrew5d2XejQGy/lGZ1nVWNsKO0x/Az2QzY5Kjd6AlXZ8kq6H68hscA5i
-OMbNlXzeEQsZH0YkId3+UsEns35AAjZv4qfFoLOu8vDotWhgVNT5DfdbIWZW3ZL8
-qbmra3JnCHuaTwXMnc25QeKgVq7/rG00YB69tCIDwcf1P+tFJWxvaGtV0g2NthtB
-a+Xo09eC0L53gfZZ3hZw1pa3SIF5dIZ6RFRUQ+lFOux3Q/I3u+rYstYw7Zxc4Zeo
-Y8JiedpQXEAnbw2ECHix/L6mVWgiWCiDzBnNLLdbmXjJRnafNSndSfFtHCnY1SiP
-aCrNpzwZIJejoV1zDlWAMO+gyS28EqzuIq3WJK/TFE7acHkdKIcCAwEAAaNCMEAw
-DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUrmV1YASnuudfmqAZP4sKGTvScaEw
-DgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQBGpEKeQoPvE85tN/25
-qHFkys9oHDl93DZ62EnOqAUKLd6v0JpCyEiop4nlrJe+4KrBYVBPyKOJDcIqE2Sp
-3cvgJXLhY4i46VM3Qxe8yuYF1ElqBpg3jJVj/sCQnYz9dwoAMWIJFaDWOvmU2E7M
-MRaKx+sPXFkIjiDA6Bv0m+VHef7aedSYIY7IDltEQHuXoqNacGrYo3I50R+fZs88
-/mB3e/V7967e99D6565yf9Lcjw4oQf2Hy7kl/6P9AuMz0LODnGITwh2TKk/Zo3RU
-Vgq25RDrT4xJK6nFHyjUF6+4cOBxVpimmFw/VP1zaXT8DN5r4HyJ9p4YuSK8ha5N
-2pJc/exvU8Nv2+vS/efcDZWyuEdZ7eh1IJWQZlOZKIAONfRDRTpeQHJ3zzv3QVYy
-t78pYp/eWBHyVIfEE8p2lFKD4279WYe+Uvdb8c4Jm4TJwqkSJV8ifID7Ub80Lsir
-lPAU3OCVTBeVRFPXT2zpC4PB4W6KBSuj6OOcEu2y/HgWcoi7Cnjvp0vFTUhDFdus
-Wz3ucmJjfVsrkEO6avDKu4SwdbVHsk30TVAwPd6srIdi9U6MOeOQSOSE4EsrrS7l
-SVmu2QIDUVFpm8QAHYplkyWIyGkupyl3ashH9mokQhixIU/Pzir0byePxHLHrwLu
-1axqeKpI0F5SBUPsaVNYY2uNFg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECDCCAvCgAwIBAgIQCREfzzVyDTMcNME+gWnTCTANBgkqhkiG9w0BAQsFADCB
-nDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTUwMwYDVQQDDCxB
-bWF6b24gUkRTIGFwLXNvdXRoZWFzdC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4G
-A1UEBwwHU2VhdHRsZTAgFw0yMTA1MjQyMDQyMzNaGA8yMDYxMDUyNDIxNDIzM1ow
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDL
-1MT6br3L/4Pq87DPXtcjlXN3cnbNk2YqRAZHJayStTz8VtsFcGPJOpk14geRVeVk
-e9uKFHRbcyr/RM4owrJTj5X4qcEuATYZbo6ou/rW2kYzuWFZpFp7lqm0vasV4Z9F
-fChlhwkNks0UbM3G+psCSMNSoF19ERunj7w2c4E62LwujkeYLvKGNepjnaH10TJL
-2krpERd+ZQ4jIpObtRcMH++bTrvklc+ei8W9lqrVOJL+89v2piN3Ecdd389uphst
-qQdb1BBVXbhUrtuGHgVf7zKqN1SkCoktoWxVuOprVWhSvr7akaWeq0UmlvbEsujU
-vADqxGMcJFyCzxx3CkJjAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O
-BBYEFFk8UJmlhoxFT3PP12PvhvazHjT4MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
-9w0BAQsFAAOCAQEAfFtr2lGoWVXmWAsIo2NYre7kzL8Xb9Tx7desKxCCz5HOOvIr
-8JMB1YK6A7IOvQsLJQ/f1UnKRh3X3mJZjKIywfrMSh0FiDf+rjcEzXxw2dGtUem4
-A+WMvIA3jwxnJ90OQj5rQ8bg3iPtE6eojzo9vWQGw/Vu48Dtw1DJo9210Lq/6hze
-hPhNkFh8fMXNT7Q1Wz/TJqJElyAQGNOXhyGpHKeb0jHMMhsy5UNoW5hLeMS5ffao
-TBFWEJ1gVfxIU9QRxSh+62m46JIg+dwDlWv8Aww14KgepspRbMqDuaM2cinoejv6
-t3dyOyHHrsOyv3ffZUKtQhQbQr+sUcL89lARsg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/zCCAuegAwIBAgIRAIJLTMpzGNxqHZ4t+c1MlCIwDQYJKoZIhvcNAQELBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyBhcC1lYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyNTIxMzAzM1oYDzIwNjEwNTI1MjIzMDMzWjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGFwLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtdHut0ZhJ9Nn2
-MpVafFcwHdoEzx06okmmhjJsNy4l9QYVeh0UUoek0SufRNMRF4d5ibzpgZol0Y92
-/qKWNe0jNxhEj6sXyHsHPeYtNBPuDMzThfbvsLK8z7pBP7vVyGPGuppqW/6m4ZBB
-lcc9fsf7xpZ689iSgoyjiT6J5wlVgmCx8hFYc/uvcRtfd8jAHvheug7QJ3zZmIye
-V4htOW+fRVWnBjf40Q+7uTv790UAqs0Zboj4Yil+hER0ibG62y1g71XcCyvcVpto
-2/XW7Y9NCgMNqQ7fGN3wR1gjtSYPd7DO32LTzYhutyvfbpAZjsAHnoObmoljcgXI
-QjfBcCFpAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFJI3aWLg
-CS5xqU5WYVaeT5s8lpO0MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
-AQEAUwATpJOcGVOs3hZAgJwznWOoTzOVJKfrqBum7lvkVH1vBwxBl9CahaKj3ZOt
-YYp2qJzhDUWludL164DL4ZjS6eRedLRviyy5cRy0581l1MxPWTThs27z+lCC14RL
-PJZNVYYdl7Jy9Q5NsQ0RBINUKYlRY6OqGDySWyuMPgno2GPbE8aynMdKP+f6G/uE
-YHOf08gFDqTsbyfa70ztgVEJaRooVf5JJq4UQtpDvVswW2reT96qi6tXPKHN5qp3
-3wI0I1Mp4ePmiBKku2dwYzPfrJK/pQlvu0Gu5lKOQ65QdotwLAAoaFqrf9za1yYs
-INUkHLWIxDds+4OHNYcerGp5Dw==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCTCCA/GgAwIBAgIRAIO6ldra1KZvNWJ0TA1ihXEwDQYJKoZIhvcNAQEMBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIxMjE0NTA1WhgPMjEyMTA1MjEyMjQ1MDVa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
-sDN52Si9pFSyZ1ruh3xAN0nVqEs960o2IK5CPu/ZfshFmzAwnx/MM8EHt/jMeZtj
-SM58LADAsNDL01ELpFZATjgZQ6xNAyXRXE7RiTRUvNkK7O3o2qAGbLnJq/UqF7Sw
-LRnB8V6hYOv+2EjVnohtGCn9SUFGZtYDjWXsLd4ML4Zpxv0a5LK7oEC7AHzbUR7R
-jsjkrXqSv7GE7bvhSOhMkmgxgj1F3J0b0jdQdtyyj109aO0ATUmIvf+Bzadg5AI2
-A9UA+TUcGeebhpHu8AP1Hf56XIlzPpaQv3ZJ4vzoLaVNUC7XKzAl1dlvCl7Klg/C
-84qmbD/tjZ6GHtzpLKgg7kQEV7mRoXq8X4wDX2AFPPQl2fv+Kbe+JODqm5ZjGegm
-uskABBi8IFv1hYx9jEulZPxC6uD/09W2+niFm3pirnlWS83BwVDTUBzF+CooUIMT
-jhWkIIZGDDgMJTzouBHfoSJtS1KpUZi99m2WyVs21MNKHeWAbs+zmI6TO5iiMC+T
-uB8spaOiHFO1573Fmeer4sy3YA6qVoqVl6jjTQqOdy3frAMbCkwH22/crV8YA+08
-hLeHXrMK+6XUvU+EtHAM3VzcrLbuYJUI2XJbzTj5g0Eb8I8JWsHvWHR5K7Z7gceR
-78AzxQmoGEfV6KABNWKsgoCQnfb1BidDJIe3BsI0A6UCAwEAAaNCMEAwDwYDVR0T
-AQH/BAUwAwEB/zAdBgNVHQ4EFgQUABp0MlB14MSHgAcuNSOhs3MOlUcwDgYDVR0P
-AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQCv4CIOBSQi/QR9NxdRgVAG/pAh
-tFJhV7OWb/wqwsNKFDtg6tTxwaahdCfWpGWId15OUe7G9LoPiKiwM9C92n0ZeHRz
-4ewbrQVo7Eu1JI1wf0rnZJISL72hVYKmlvaWaacHhWxvsbKLrB7vt6Cknxa+S993
-Kf8i2Psw8j5886gaxhiUtzMTBwoDWak8ZaK7m3Y6C6hXQk08+3pnIornVSFJ9dlS
-PAqt5UPwWmrEfF+0uIDORlT+cvrAwgSp7nUF1q8iasledycZ/BxFgQqzNwnkBDwQ
-Z/aM52ArGsTzfMhkZRz9HIEhz1/0mJw8gZtDVQroD8778h8zsx2SrIz7eWQ6uWsD
-QEeSWXpcheiUtEfzkDImjr2DLbwbA23c9LoexUD10nwohhoiQQg77LmvBVxeu7WU
-E63JqaYUlOLOzEmNJp85zekIgR8UTkO7Gc+5BD7P4noYscI7pPOL5rP7YLg15ZFi
-ega+G53NTckRXz4metsd8XFWloDjZJJq4FfD60VuxgXzoMNT9wpFTNSH42PR2s9L
-I1vcl3w8yNccs9se2utM2nLsItZ3J0m/+QSRiw9hbrTYTcM9sXki0DtH2kyIOwYf
-lOrGJDiYOIrXSQK36H0gQ+8omlrUTvUj4msvkXuQjlfgx6sgp2duOAfnGxE7uHnc
-UhnJzzoe6M+LfGHkVQ==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICuDCCAj2gAwIBAgIQSAG6j2WHtWUUuLGJTPb1nTAKBggqhkjOPQQDAzCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLW5vcnRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyMDE2MzgyNloYDzIxMjEwNTIwMTczODI2WjCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLW5vcnRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE2eqwU4FOzW8RV1W381Bd
-olhDOrqoMqzWli21oDUt7y8OnXM/lmAuOS6sr8Nt61BLVbONdbr+jgCYw75KabrK
-ZGg3siqvMOgabIKkKuXO14wtrGyGDt7dnKXg5ERGYOZlo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBS1Acp2WYxOcblv5ikZ3ZIbRCCW+zAOBgNVHQ8BAf8E
-BAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAJL84J08PBprxmsAKPTotBuVI3MyW1r8
-xQ0i8lgCQUf8GcmYjQ0jI4oZyv+TuYJAcwIxAP9Xpzq0Docxb+4N1qVhpiOfWt1O
-FnemFiy9m1l+wv6p3riQMPV7mBVpklmijkIv3Q==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIRALZLcqCVIJ25maDPE3sbPCIwDQYJKoZIhvcNAQELBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIxMjEzOTM5WhgPMjA2MTA1MjEyMjM5Mzla
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-ypKc+6FfGx6Gl6fQ78WYS29QoKgQiur58oxR3zltWeg5fqh9Z85K5S3UbRSTqWWu
-Xcfnkz0/FS07qHX+nWAGU27JiQb4YYqhjZNOAq8q0+ptFHJ6V7lyOqXBq5xOzO8f
-+0DlbJSsy7GEtJp7d7QCM3M5KVY9dENVZUKeJwa8PC5StvwPx4jcLeZRJC2rAVDG
-SW7NAInbATvr9ssSh03JqjXb+HDyywiqoQ7EVLtmtXWimX+0b3/2vhqcH5jgcKC9
-IGFydrjPbv4kwMrKnm6XlPZ9L0/3FMzanXPGd64LQVy51SI4d5Xymn0Mw2kMX8s6
-Nf05OsWcDzJ1n6/Q1qHSxQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBRmaIc8eNwGP7i6P7AJrNQuK6OpFzAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
-hvcNAQELBQADggEBAIBeHfGwz3S2zwIUIpqEEI5/sMySDeS+3nJR+woWAHeO0C8i
-BJdDh+kzzkP0JkWpr/4NWz84/IdYo1lqASd1Kopz9aT1+iROXaWr43CtbzjXb7/X
-Zv7eZZFC8/lS5SROq42pPWl4ekbR0w8XGQElmHYcWS41LBfKeHCUwv83ATF0XQ6I
-4t+9YSqZHzj4vvedrvcRInzmwWJaal9s7Z6GuwTGmnMsN3LkhZ+/GD6oW3pU/Pyh
-EtWqffjsLhfcdCs3gG8x9BbkcJPH5aPAVkPn4wc8wuXg6xxb9YGsQuY930GWTYRf
-schbgjsuqznW4HHakq4WNhs1UdTSTKkRdZz7FUQ=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEDzCCAvegAwIBAgIRAM2zAbhyckaqRim63b+Tib8wDQYJKoZIhvcNAQELBQAw
-gZ8xCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE4MDYGA1UEAwwv
-QW1hem9uIFJEUyBQcmV2aWV3IHVzLWVhc3QtMiBSb290IENBIFJTQTIwNDggRzEx
-EDAOBgNVBAcMB1NlYXR0bGUwIBcNMjEwNTE4MjA0OTQ1WhgPMjA2MTA1MTgyMTQ5
-NDVaMIGfMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNl
-cywgSW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExODA2BgNV
-BAMML0FtYXpvbiBSRFMgUHJldmlldyB1cy1lYXN0LTIgUm9vdCBDQSBSU0EyMDQ4
-IEcxMRAwDgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
-CgKCAQEA1ybjQMH1MkbvfKsWJaCTXeCSN1SG5UYid+Twe+TjuSqaXWonyp4WRR5z
-tlkqq+L2MWUeQQAX3S17ivo/t84mpZ3Rla0cx39SJtP3BiA2BwfUKRjhPwOjmk7j
-3zrcJjV5k1vSeLNOfFFSlwyDiVyLAE61lO6onBx+cRjelu0egMGq6WyFVidTdCmT
-Q9Zw3W6LTrnPvPmEyjHy2yCHzH3E50KSd/5k4MliV4QTujnxYexI2eR8F8YQC4m3
-DYjXt/MicbqA366SOoJA50JbgpuVv62+LSBu56FpzY12wubmDZsdn4lsfYKiWxUy
-uc83a2fRXsJZ1d3whxrl20VFtLFHFQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBRC0ytKmDYbfz0Bz0Psd4lRQV3aNTAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQELBQADggEBAGv8qZu4uaeoF6zsbumauz6ea6tdcWt+hGFuwGrb
-tRbI85ucAmVSX06x59DJClsb4MPhL1XmqO3RxVMIVVfRwRHWOsZQPnXm8OYQ2sny
-rYuFln1COOz1U/KflZjgJmxbn8x4lYiTPZRLarG0V/OsCmnLkQLPtEl/spMu8Un7
-r3K8SkbWN80gg17Q8EV5mnFwycUx9xsTAaFItuG0en9bGsMgMmy+ZsDmTRbL+lcX
-Fq8r4LT4QjrFz0shrzCwuuM4GmcYtBSxlacl+HxYEtAs5k10tmzRf6OYlY33tGf6
-1tkYvKryxDPF/EDgGp/LiBwx6ixYMBfISoYASt4V/ylAlHA=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtTCCAjqgAwIBAgIRAK9BSZU6nIe6jqfODmuVctYwCgYIKoZIzj0EAwMwgZkx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1h
-em9uIFJEUyBjYS1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTIxMjIxMzA5WhgPMjEyMTA1MjEyMzEzMDlaMIGZMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMjAwBgNVBAMMKUFtYXpv
-biBSRFMgY2EtY2VudHJhbC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEUkEERcgxneT5H+P+fERcbGmf
-bVx+M7rNWtgWUr6w+OBENebQA9ozTkeSg4c4M+qdYSObFqjxITdYxT1z/nHz1gyx
-OKAhLjWu+nkbRefqy3RwXaWT680uUaAP6ccnkZOMo0IwQDAPBgNVHRMBAf8EBTAD
-AQH/MB0GA1UdDgQWBBSN6fxlg0s5Wny08uRBYZcQ3TUoyzAOBgNVHQ8BAf8EBAMC
-AYYwCgYIKoZIzj0EAwMDaQAwZgIxAORaz+MBVoFBTmZ93j2G2vYTwA6T5hWzBWrx
-CrI54pKn5g6At56DBrkjrwZF5T1enAIxAJe/LZ9xpDkAdxDgGJFN8gZYLRWc0NRy
-Rb4hihy5vj9L+w9uKc9VfEBIFuhT7Z3ljg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIQB/57HSuaqUkLaasdjxUdPjANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB
-bWF6b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUxOTE3NDAzNFoYDzIwNjEwNTE5MTg0MDM0WjCBmDEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6
-b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtbkaoVsUS76o
-TgLFmcnaB8cswBk1M3Bf4IVRcwWT3a1HeJSnaJUqWHCJ+u3ip/zGVOYl0gN1MgBb
-MuQRIJiB95zGVcIa6HZtx00VezDTr3jgGWRHmRjNVCCHGmxOZWvJjsIE1xavT/1j
-QYV/ph4EZEIZ/qPq7e3rHohJaHDe23Z7QM9kbyqp2hANG2JtU/iUhCxqgqUHNozV
-Zd0l5K6KnltZQoBhhekKgyiHqdTrH8fWajYl5seD71bs0Axowb+Oh0rwmrws3Db2
-Dh+oc2PwREnjHeca9/1C6J2vhY+V0LGaJmnnIuOANrslx2+bgMlyhf9j0Bv8AwSi
-dSWsobOhNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQb7vJT
-VciLN72yJGhaRKLn6Krn2TAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBAAxEj8N9GslReAQnNOBpGl8SLgCMTejQ6AW/bapQvzxrZrfVOZOYwp/5oV0f
-9S1jcGysDM+DrmfUJNzWxq2Y586R94WtpH4UpJDGqZp+FuOVJL313te4609kopzO
-lDdmd+8z61+0Au93wB1rMiEfnIMkOEyt7D2eTFJfJRKNmnPrd8RjimRDlFgcLWJA
-3E8wca67Lz/G0eAeLhRHIXv429y8RRXDtKNNz0wA2RwURWIxyPjn1fHjA9SPDkeW
-E1Bq7gZj+tBnrqz+ra3yjZ2blss6Ds3/uRY6NYqseFTZWmQWT7FolZEnT9vMUitW
-I0VynUbShVpGf6946e0vgaaKw20=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/jCCAuagAwIBAgIQGyUVTaVjYJvWhroVEiHPpDANBgkqhkiG9w0BAQsFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIHVzLXdlc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTE5MTkwNDA2WhgPMjA2MTA1MTkyMDA0MDZaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgdXMtd2VzdC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANhyXpJ0t4nigRDZ
-EwNtFOem1rM1k8k5XmziHKDvDk831p7QsX9ZOxl/BT59Pu/P+6W6SvasIyKls1sW
-FJIjFF+6xRQcpoE5L5evMgN/JXahpKGeQJPOX9UEXVW5B8yi+/dyUitFT7YK5LZA
-MqWBN/LtHVPa8UmE88RCDLiKkqiv229tmwZtWT7nlMTTCqiAHMFcryZHx0pf9VPh
-x/iPV8p2gBJnuPwcz7z1kRKNmJ8/cWaY+9w4q7AYlAMaq/rzEqDaN2XXevdpsYAK
-TMMj2kji4x1oZO50+VPNfBl5ZgJc92qz1ocF95SAwMfOUsP8AIRZkf0CILJYlgzk
-/6u6qZECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm5jfcS9o
-+LwL517HpB6hG+PmpBswDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB
-AQAcQ6lsqxi63MtpGk9XK8mCxGRLCad51+MF6gcNz6i6PAqhPOoKCoFqdj4cEQTF
-F8dCfa3pvfJhxV6RIh+t5FCk/y6bWT8Ls/fYKVo6FhHj57bcemWsw/Z0XnROdVfK
-Yqbc7zvjCPmwPHEqYBhjU34NcY4UF9yPmlLOL8uO1JKXa3CAR0htIoW4Pbmo6sA4
-6P0co/clW+3zzsQ92yUCjYmRNeSbdXbPfz3K/RtFfZ8jMtriRGuO7KNxp8MqrUho
-HK8O0mlSUxGXBZMNicfo7qY8FD21GIPH9w5fp5oiAl7lqFzt3E3sCLD3IiVJmxbf
-fUwpGd1XZBBSdIxysRLM6j48
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrTCCAjOgAwIBAgIQU+PAILXGkpoTcpF200VD/jAKBggqhkjOPQQDAzCBljEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6
-b24gUkRTIGFwLWVhc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTAgFw0yMTA1MjUyMTQ1MTFaGA8yMTIxMDUyNTIyNDUxMVowgZYxCzAJBgNV
-BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD
-VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE
-UyBhcC1lYXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw
-djAQBgcqhkjOPQIBBgUrgQQAIgNiAAT3tFKE8Kw1sGQAvNLlLhd8OcGhlc7MiW/s
-NXm3pOiCT4vZpawKvHBzD76Kcv+ZZzHRxQEmG1/muDzZGlKR32h8AAj+NNO2Wy3d
-CKTtYMiVF6Z2zjtuSkZQdjuQbe4eQ7qjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
-VR0OBBYEFAiSQOp16Vv0Ohpvqcbd2j5RmhYNMA4GA1UdDwEB/wQEAwIBhjAKBggq
-hkjOPQQDAwNoADBlAjBVsi+5Ape0kOhMt/WFkANkslD4qXA5uqhrfAtH29Xzz2NV
-tR7akiA771OaIGB/6xsCMQCZt2egCtbX7J0WkuZ2KivTh66jecJr5DHvAP4X2xtS
-F/5pS+AUhcKTEGjI9jDH3ew=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICuDCCAj2gAwIBAgIQT5mGlavQzFHsB7hV6Mmy6TAKBggqhkjOPQQDAzCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLXNvdXRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyNDIwNTAxNVoYDzIxMjEwNTI0MjE1MDE1WjCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLXNvdXRoZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEcm4BBBjYK7clwm0HJRWS
-flt3iYwoJbIXiXn9c1y3E+Vb7bmuyKhS4eO8mwO4GefUcXObRfoHY2TZLhMJLVBQ
-7MN2xDc0RtZNj07BbGD3VAIFRTDX0mH9UNYd0JQM3t/Oo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBRrd5ITedfAwrGo4FA9UaDaGFK3rjAOBgNVHQ8BAf8E
-BAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAPBNqmVv1IIA3EZyQ6XuVf4gj79/DMO8
-bkicNS1EcBpUqbSuU4Zwt2BYc8c/t7KVOQIxAOHoWkoKZPiKyCxfMtJpCZySUG+n
-sXgB/LOyWE5BJcXUfm+T1ckeNoWeUUMOLmnJjg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIRAJcDeinvdNrDQBeJ8+t38WQwDQYJKoZIhvcNAQELBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtNCBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjIwNTI1MTY0OTE2WhgPMjA2MjA1MjUxNzQ5MTZa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTQgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-k8DBNkr9tMoIM0NHoFiO7cQfSX0cOMhEuk/CHt0fFx95IBytx7GHCnNzpM27O5z6
-x6iRhfNnx+B6CrGyCzOjxvPizneY+h+9zfvNz9jj7L1I2uYMuiNyOKR6FkHR46CT
-1CiArfVLLPaTqgD/rQjS0GL2sLHS/0dmYipzynnZcs613XT0rAWdYDYgxDq7r/Yi
-Xge5AkWQFkMUq3nOYDLCyGGfQqWKkwv6lZUHLCDKf+Y0Uvsrj8YGCI1O8mF0qPCQ
-lmlfaDvbuBu1AV+aabmkvyFj3b8KRIlNLEtQ4N8KGYR2Jdb82S4YUGIOAt4wuuFt
-1B7AUDLk3V/u+HTWiwfoLQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBSNpcjz6ArWBtAA+Gz6kyyZxrrgdDAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
-hvcNAQELBQADggEBAGJEd7UgOzHYIcQRSF7nSYyjLROyalaIV9AX4WXW/Cqlul1c
-MblP5etDZm7A/thliZIWAuyqv2bNicmS3xKvNy6/QYi1YgxZyy/qwJ3NdFl067W0
-t8nGo29B+EVK94IPjzFHWShuoktIgp+dmpijB7wkTIk8SmIoe9yuY4+hzgqk+bo4
-ms2SOXSN1DoQ75Xv+YmztbnZM8MuWhL1T7hA4AMorzTQLJ9Pof8SpSdMHeDsHp0R
-01jogNFkwy25nw7cL62nufSuH2fPYGWXyNDg+y42wKsKWYXLRgUQuDVEJ2OmTFMB
-T0Vf7VuNijfIA9hkN2d3K53m/9z5WjGPSdOjGhg=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/jCCAuagAwIBAgIQRiwspKyrO0xoxDgSkqLZczANBgkqhkiG9w0BAQsFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIHVzLXdlc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTI0MjE1OTAwWhgPMjA2MTA1MjQyMjU5MDBaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgdXMtd2VzdC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL53Jk3GsKiu+4bx
-jDfsevWbwPCNJ3H08Zp7GWhvI3Tgi39opfHYv2ku2BKFjK8N2L6RvNPSR8yplv5j
-Y0tK0U+XVNl8o0ibhqRDhbTuh6KL8CFINWYzAajuxFS+CF0U6c1Q3tXLBdALxA7l
-FlXJ71QrP06W31kRe7kvgrvO7qWU3/OzUf9qYw4LSiR1/VkvvRCTqcVNw09clw/M
-Jbw6FSgweN65M9j7zPbjGAXSHkXyxH1Erin2fa+B9PE4ZDgX9cp2C1DHewYJQL/g
-SepwwcudVNRN1ibKH7kpMrgPnaNIVNx5sXVsTjk6q2ZqYw3SVHegltJpLy/cZReP
-mlivF2kCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUmTcQd6o1
-CuS65MjBrMwQ9JJjmBwwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB
-AQAKSDSIzl956wVddPThf2VAzI8syw9ngSwsEHZvxVGHBvu5gg618rDyguVCYX9L
-4Kw/xJrk6S3qxOS2ZDyBcOpsrBskgahDFIunzoRP3a18ARQVq55LVgfwSDQiunch
-Bd05cnFGLoiLkR5rrkgYaP2ftn3gRBRaf0y0S3JXZ2XB3sMZxGxavYq9mfiEcwB0
-LMTMQ1NYzahIeG6Jm3LqRqR8HkzP/Ztq4dT2AtSLvFebbNMiWqeqT7OcYp94HTYT
-zqrtaVdUg9bwyAUCDgy0GV9RHDIdNAOInU/4LEETovrtuBU7Z1q4tcHXvN6Hd1H8
-gMb0mCG5I393qW5hFsA/diFb
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIRAPQAvihfjBg/JDbj6U64K98wDQYJKoZIhvcNAQELBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIwMTYyODQxWhgPMjA2MTA1MjAxNzI4NDFa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-vJ9lgyksCxkBlY40qOzI1TCj/Q0FVGuPL/Z1Mw2YN0l+41BDv0FHApjTUkIKOeIP
-nwDwpXTa3NjYbk3cOZ/fpH2rYJ++Fte6PNDGPgKppVCUh6x3jiVZ1L7wOgnTdK1Q
-Trw8440IDS5eLykRHvz8OmwvYDl0iIrt832V0QyOlHTGt6ZJ/aTQKl12Fy3QBLv7
-stClPzvHTrgWqVU6uidSYoDtzHbU7Vda7YH0wD9IUoMBf7Tu0rqcE4uH47s2XYkc
-SdLEoOg/Ngs7Y9B1y1GCyj3Ux7hnyvCoRTw014QyNB7dTatFMDvYlrRDGG14KeiU
-UL7Vo/+EejWI31eXNLw84wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBQkgTWFsNg6wA3HbbihDQ4vpt1E2zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
-hvcNAQELBQADggEBAGz1Asiw7hn5WYUj8RpOCzpE0h/oBZcnxP8wulzZ5Xd0YxWO
-0jYUcUk3tTQy1QvoY+Q5aCjg6vFv+oFBAxkib/SmZzp4xLisZIGlzpJQuAgRkwWA
-6BVMgRS+AaOMQ6wKPgz1x4v6T0cIELZEPq3piGxvvqkcLZKdCaeC3wCS6sxuafzZ
-4qA3zMwWuLOzRftgX2hQto7d/2YkRXga7jSvQl3id/EI+xrYoH6zIWgjdU1AUaNq
-NGT7DIo47vVMfnd9HFZNhREsd4GJE83I+JhTqIxiKPNxrKgESzyADmNPt0gXDnHo
-tbV1pMZz5HpJtjnP/qVZhEK5oB0tqlKPv9yx074=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICuTCCAj6gAwIBAgIRAKp1Rn3aL/g/6oiHVIXtCq8wCgYIKoZIzj0EAwMwgZsx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h
-em9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMTA1MjQyMDMyMTdaGA8yMTIxMDUyNDIxMzIxN1owgZsx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h
-em9uIFJEUyBhcC1ub3J0aGVhc3QtMyBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABGTYWPILeBJXfcL3Dz4z
-EWMUq78xB1HpjBwHoTURYfcMd5r96BTVG6yaUBWnAVCMeeD6yTG9a1eVGNhG14Hk
-ZAEjgLiNB7RRbEG5JZ/XV7W/vODh09WCst2y9SLKsdgeAaNCMEAwDwYDVR0TAQH/
-BAUwAwEB/zAdBgNVHQ4EFgQUoE0qZHmDCDB+Bnm8GUa/evpfPwgwDgYDVR0PAQH/
-BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYCMQCnil5MMwhY3qoXv0xvcKZGxGPaBV15
-0CCssCKn0oVtdJQfJQ3Jrf3RSaEyijXIJsoCMQC35iJi4cWoNX3N/qfgnHohW52O
-B5dg0DYMqy5cNZ40+UcAanRMyqNQ6P7fy3umGco=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtzCCAj2gAwIBAgIQPXnDTPegvJrI98qz8WxrMjAKBggqhkjOPQQDAzCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIEJldGEgdXMtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUxODIxNDAxMloYDzIxMjEwNTE4MjI0MDEyWjCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIEJldGEgdXMtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEI0sR7gwutK5AB46hM761
-gcLTGBIYlURSEoM1jcBwy56CL+3CJKZwLLyJ7qoOKfWbu5GsVLUTWS8MV6Nw33cx
-2KQD2svb694wi+Px2f4n9+XHkEFQw8BbiodDD7RZA70fo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBTQSioOvnVLEMXwNSDg+zgln/vAkjAOBgNVHQ8BAf8E
-BAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIxAMwu1hqm5Bc98uE/E0B5iMYbBQ4kpMxO
-tP8FTfz5UR37HUn26nXE0puj6S/Ffj4oJgIwXI7s2c26tFQeqzq6u3lrNJHp5jC9
-Uxlo/hEJOLoDj5jnpxo8dMAtCNoQPaHdfL0P
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/jCCA+agAwIBAgIQEM1pS+bWfBJeu/6j1yIIFzANBgkqhkiG9w0BAQwFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIGNhLXdlc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjMwOTE5MjIwMTM5WhgPMjEyMzA5MTkyMzAxMzlaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgY2Etd2VzdC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Pyp8p5z6HnlGB
-daOj78gZ3ABufxnBFiu5NdFiGoMrS+eY//xxr2iKbnynJAzjmn5A6VKMNxtbuYIZ
-WKAzDb/HrWlIYD2w7ZVBXpylfPhiz3jLNsl03WdPNnEruCcivhY2QMewEVtzjPU0
-ofdbZlO2KpF3biv1gjPuIuE7AUyQAbWnWTlrzETAVWLboJJRRqxASSkFUHNLXod7
-ow02FwlAhcnCp9gSe1SKRDrpvvEvYQBAFB7owfnoQzOGDdd87RGyYfyuW8aFI2Z0
-LHNvsA0dTafO4Rh986c72kDL7ijICQdr5OTgZR2OnuESLk1DSK4xYJ4fA6jb5dJ5
-+xsI6tCPykWCW98aO/pha35OsrVNifL/5cH5pdv/ecgQGdffJB+Vdj6f/ZMwR6s/
-Rm37cQ9l3tU8eu/qpzsFjLq1ZUzDaVDWgMW9t49+q/zjhdmbPOabZDao7nHXrVRw
-rwPHWCmEY4OmH6ikEKQW3AChFjOdSg4me/J0Jr5l5jKggLPHWbNLRO8qTTK6N8qk
-ui3aJDi+XQfsTPARXIw4UFErArNImTsoZVyqfX7I4shp0qZbEhP6kRAbfPljw5kW
-Yat7ZlXqDanjsreqbLTaOU10P0rC0/4Ctv5cLSKCrzRLWtpXxhKa2wJTQ74G6fAZ
-1oUA79qg3F8nyM+ZzDsfNI854+PNAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w
-HQYDVR0OBBYEFLRWiDabEQZNkzEPUCr1ZVJV6xpwMA4GA1UdDwEB/wQEAwIBhjAN
-BgkqhkiG9w0BAQwFAAOCAgEATkVVzkkGBjEtLGDtERi+fSpIV0MxwAsA4PAeBBmb
-myxo90jz6kWkKM1Wm4BkZM8/mq5VbxPef1kxHfb5CHksCL6SgG5KujfIvht+KT2a
-MRJB+III3CbcTy0HtwCX5AlPIbXWydhQFoJTW/OkpecUWoyFM6SqYeYZx1itJpxl
-sXshLjYOvw+QgvxRsDxqUfkcaC/N2yhu/30Zo2P8msJfAFry2UmA/TBrWOQKVQxl
-Ee/yWgp4U/bC/GZnjWnWDTwkRFGQtI4wjxbVuX6V4FTLCT7kIoHBhG+zOSduJRn3
-Axej7gkEXEVc/PAnwp/kSJ/b0/JONLWdjGUFkyiMn1yJlhJ2sg39vepBN5r6yVYU
-nJWoZAuupRpoIKfmC3/cZanXqYbYl4yxzX/PMB4kAACfdxGxLawjnnBjSzaWokXs
-YVh2TjWpUMwLOi0RB2mtPUjHdDLKtjOTZ1zHZnR/wVp9BmVI1BXYnz5PAqU5XqeD
-EmanyaAuFCeyol1EtbQhgtysThQ+vwYAXMm2iKzJxq0hik8wyG8X55FhnGEOGV3u
-xxq7odd3/8BXkc3dGdBPQtH+k5glaQyPnAsLVAIUvyzTmy58saL+nJnQY4mmRrwV
-1jJA7nnkaklI/L5fvfCg0W+TMinCOAGd+GQ4hK2SAsJLtcqiBgPf2wJHO8wiwUh9
-Luw=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjWgAwIBAgIQGKVv+5VuzEZEBzJ+bVfx2zAKBggqhkjOPQQDAzCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGFwLXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTE5MTc1MDU5WhgPMjEyMTA1MTkxODUwNTlaMIGXMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS
-RFMgYXAtc291dGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs
-ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABMqdLJ0tZF/DGFZTKZDrGRJZID8ivC2I
-JRCYTWweZKCKSCAzoiuGGHzJhr5RlLHQf/QgmFcgXsdmO2n3CggzhA4tOD9Ip7Lk
-P05eHd2UPInyPCHRgmGjGb0Z+RdQ6zkitKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
-BgNVHQ4EFgQUC1yhRgVqU5bR8cGzOUCIxRpl4EYwDgYDVR0PAQH/BAQDAgGGMAoG
-CCqGSM49BAMDA2cAMGQCMG0c/zLGECRPzGKJvYCkpFTCUvdP4J74YP0v/dPvKojL
-t/BrR1Tg4xlfhaib7hPc7wIwFvgqHes20CubQnZmswbTKLUrgSUW4/lcKFpouFd2
-t2/ewfi/0VhkeUW+IiHhOMdU
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCTCCA/GgAwIBAgIRAOXxJuyXVkbfhZCkS/dOpfEwDQYJKoZIhvcNAQEMBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI1MjE1OTEwWhgPMjEyMTA1MjUyMjU5MTBa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
-xiP4RDYm4tIS12hGgn1csfO8onQDmK5SZDswUpl0HIKXOUVVWkHNlINkVxbdqpqH
-FhbyZmNN6F/EWopotMDKe1B+NLrjNQf4zefv2vyKvPHJXhxoKmfyuTd5Wk8k1F7I
-lNwLQzznB+ElhrLIDJl9Ro8t31YBBNFRGAGEnxyACFGcdkjlsa52UwfYrwreEg2l
-gW5AzqHgjFfj9QRLydeU/n4bHm0F1adMsV7P3rVwilcUlqsENDwXnWyPEyv3sw6F
-wNemLEs1129mB77fwvySb+lLNGsnzr8w4wdioZ74co+T9z2ca+eUiP+EQccVw1Is
-D4Fh57IjPa6Wuc4mwiUYKkKY63+38aCfEWb0Qoi+zW+mE9nek6MOQ914cN12u5LX
-dBoYopphRO5YmubSN4xcBy405nIdSdbrAVWwxXnVVyjqjknmNeqQsPZaxAhdoKhV
-AqxNr8AUAdOAO6Sz3MslmcLlDXFihrEEOeUbpg/m1mSUUHGbu966ajTG1FuEHHwS
-7WB52yxoJo/tHvt9nAWnh3uH5BHmS8zn6s6CGweWKbX5yICnZ1QFR1e4pogxX39v
-XD6YcNOO+Vn+HY4nXmjgSYVC7l+eeP8eduMg1xJujzjrbmrXU+d+cBObgdTOAlpa
-JFHaGwYw1osAwPCo9cZ2f04yitBfj9aPFia8ASKldakCAwEAAaNCMEAwDwYDVR0T
-AQH/BAUwAwEB/zAdBgNVHQ4EFgQUqKS+ltlior0SyZKYAkJ/efv55towDgYDVR0P
-AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQAdElvp8bW4B+Cv+1WSN87dg6TN
-wGyIjJ14/QYURgyrZiYpUmZpj+/pJmprSWXu4KNyqHftmaidu7cdjL5nCAvAfnY5
-/6eDDbX4j8Gt9fb/6H9y0O0dn3mUPSEKG0crR+JRFAtPhn/2FNvst2P82yguWLv0
-pHjHVUVcq+HqDMtUIJsTPYjSh9Iy77Q6TOZKln9dyDOWJpCSkiUWQtMAKbCSlvzd
-zTs/ahqpT+zLfGR1SR+T3snZHgQnbnemmz/XtlKl52NxccARwfcEEKaCRQyGq/pR
-0PVZasyJS9JY4JfQs4YOdeOt4UMZ8BmW1+BQWGSkkb0QIRl8CszoKofucAlqdPcO
-IT/ZaMVhI580LFGWiQIizWFskX6lqbCyHqJB3LDl8gJISB5vNTHOHpvpMOMs5PYt
-cRl5Mrksx5MKMqG7y5R734nMlZxQIHjL5FOoOxTBp9KeWIL/Ib89T2QDaLw1SQ+w
-ihqWBJ4ZdrIMWYpP3WqM+MXWk7WAem+xsFJdR+MDgOOuobVQTy5dGBlPks/6gpjm
-rO9TjfQ36ppJ3b7LdKUPeRfnYmlR5RU4oyYJ//uLbClI443RZAgxaCXX/nyc12lr
-eVLUMNF2abLX4/VF63m2/Z9ACgMRfqGshPssn1NN33OonrotQoj4S3N9ZrjvzKt8
-iHcaqd60QKpfiH2A3A==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICuDCCAj2gAwIBAgIQPaVGRuu86nh/ylZVCLB0MzAKBggqhkjOPQQDAzCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLW5vcnRoZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyNTIyMDMxNloYDzIxMjEwNTI1MjMwMzE2WjCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLW5vcnRoZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEexNURoB9KE93MEtEAlJG
-obz4LS/pD2hc8Gczix1WhVvpJ8bN5zCDXaKdnDMCebetyRQsmQ2LYlfmCwpZwSDu
-0zowB11Pt3I5Avu2EEcuKTlKIDMBeZ1WWuOd3Tf7MEAMo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBSaYbZPBvFLikSAjpa8mRJvyArMxzAOBgNVHQ8BAf8E
-BAMCAYYwCgYIKoZIzj0EAwMDaQAwZgIxAOEJkuh3Zjb7Ih/zuNRd1RBqmIYcnyw0
-nwUZczKXry+9XebYj3VQxSRNadrarPWVqgIxAMg1dyGoDAYjY/L/9YElyMnvHltO
-PwpJShmqHvCLc/mXMgjjYb/akK7yGthvW6j/uQ==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCDCCA/CgAwIBAgIQChu3v5W1Doil3v6pgRIcVzANBgkqhkiG9w0BAQwFADCB
-nDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTUwMwYDVQQDDCxB
-bWF6b24gUkRTIEJldGEgdXMtZWFzdC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4G
-A1UEBwwHU2VhdHRsZTAgFw0yMTA1MTgyMTM0MTVaGA8yMTIxMDUxODIyMzQxNVow
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBCZXRhIHVzLWVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC1
-FUGQ5tf3OwpDR6hGBxhUcrkwKZhaXP+1St1lSOQvjG8wXT3RkKzRGMvb7Ee0kzqI
-mzKKe4ASIhtV3UUWdlNmP0EA3XKnif6N79MismTeGkDj75Yzp5A6tSvqByCgxIjK
-JqpJrch3Dszoyn8+XhwDxMZtkUa5nQVdJgPzJ6ltsQ8E4SWLyLtTu0S63jJDkqYY
-S7cQblk7y7fel+Vn+LS5dGTdRRhMvSzEnb6mkVBaVzRyVX90FNUED06e8q+gU8Ob
-htvQlf9/kRzHwRAdls2YBhH40ZeyhpUC7vdtPwlmIyvW5CZ/QiG0yglixnL6xahL
-pbmTuTSA/Oqz4UGQZv2WzHe1lD2gRHhtFX2poQZeNQX8wO9IcUhrH5XurW/G9Xwl
-Sat9CMPERQn4KC3HSkat4ir2xaEUrjfg6c4XsGyh2Pk/LZ0gLKum0dyWYpWP4JmM
-RQNjrInXPbMhzQObozCyFT7jYegS/3cppdyy+K1K7434wzQGLU1gYXDKFnXwkX8R
-bRKgx2pHNbH5lUddjnNt75+e8m83ygSq/ZNBUz2Ur6W2s0pl6aBjwaDES4VfWYlI
-jokcmrGvJNDfQWygb1k00eF2bzNeNCHwgWsuo3HSxVgc/WGsbcGrTlDKfz+g3ich
-bXUeUidPhRiv5UQIVCLIHpHuin3bj9lQO/0t6p+tAQIDAQABo0IwQDAPBgNVHRMB
-Af8EBTADAQH/MB0GA1UdDgQWBBSFmMBgm5IsRv3hLrvDPIhcPweXYTAOBgNVHQ8B
-Af8EBAMCAYYwDQYJKoZIhvcNAQEMBQADggIBAAa2EuozymOsQDJlEi7TqnyA2OhT
-GXPfYqCyMJVkfrqNgcnsNpCAiNEiZbb+8sIPXnT8Ay8hrwJYEObJ5b7MHXpLuyft
-z0Pu1oFLKnQxKjNxrIsCvaB4CRRdYjm1q7EqGhMGv76se9stOxkOqO9it31w/LoU
-ENDk7GLsSqsV1OzYLhaH8t+MaNP6rZTSNuPrHwbV3CtBFl2TAZ7iKgKOhdFz1Hh9
-Pez0lG+oKi4mHZ7ajov6PD0W7njn5KqzCAkJR6OYmlNVPjir+c/vUtEs0j+owsMl
-g7KE5g4ZpTRShyh5BjCFRK2tv0tkqafzNtxrKC5XNpEkqqVTCnLcKG+OplIEadtr
-C7UWf4HyhCiR+xIyxFyR05p3uY/QQU/5uza7GlK0J+U1sBUytx7BZ+Fo8KQfPPqV
-CqDCaYUksoJcnJE/KeoksyqNQys7sDGJhkd0NeUGDrFLKHSLhIwAMbEWnqGxvhli
-E7sP2E5rI/I9Y9zTbLIiI8pfeZlFF8DBdoP/Hzg8pqsiE/yiXSFTKByDwKzGwNqz
-F0VoFdIZcIbLdDbzlQitgGpJtvEL7HseB0WH7B2PMMD8KPJlYvPveO3/6OLzCsav
-+CAkvk47NQViKMsUTKOA0JDCW+u981YRozxa3K081snhSiSe83zIPBz1ikldXxO9
-6YYLNPRrj3mi9T/f
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjSgAwIBAgIRAMkvdFnVDb0mWWFiXqnKH68wCgYIKoZIzj0EAwMwgZYx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h
-em9uIFJEUyB1cy13ZXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTE5MTkxMzI0WhgPMjEyMTA1MTkyMDEzMjRaMIGWMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS
-RFMgdXMtd2VzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEy86DB+9th/0A5VcWqMSWDxIUblWTt/R0
-ao6Z2l3vf2YDF2wt1A2NIOGpfQ5+WAOJO/IQmnV9LhYo+kacB8sOnXdQa6biZZkR
-IyouUfikVQAKWEJnh1Cuo5YMM4E2sUt5o0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G
-A1UdDgQWBBQ8u3OnecANmG8OoT7KLWDuFzZwBTAOBgNVHQ8BAf8EBAMCAYYwCgYI
-KoZIzj0EAwMDaAAwZQIwQ817qkb7mWJFnieRAN+m9W3E0FLVKaV3zC5aYJUk2fcZ
-TaUx3oLp3jPLGvY5+wgeAjEA6wAicAki4ZiDfxvAIuYiIe1OS/7H5RA++R8BH6qG
-iRzUBM/FItFpnkus7u/eTkvo
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrzCCAjWgAwIBAgIQS/+Ryfgb/IOVEa1pWoe8oTAKBggqhkjOPQQDAzCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGFwLXNvdXRoLTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjIwNjA2MjE1NDQyWhgPMjEyMjA2MDYyMjU0NDJaMIGXMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS
-RFMgYXAtc291dGgtMiBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs
-ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDsX6fhdUWBQpYTdseBD/P3s96Dtw2Iw
-OrXKNToCnmX5nMkUGdRn9qKNiz1pw3EPzaPxShbYwQ7LYP09ENK/JN4QQjxMihxC
-jLFxS85nhBQQQGRCWikDAe38mD8fSvREQKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
-BgNVHQ4EFgQUIh1xZiseQYFjPYKJmGbruAgRH+AwDgYDVR0PAQH/BAQDAgGGMAoG
-CCqGSM49BAMDA2gAMGUCMFudS4zLy+UUGrtgNLtRMcu/DZ9BUzV4NdHxo0bkG44O
-thnjl4+wTKI6VbyAbj2rkgIxAOHps8NMITU5DpyiMnKTxV8ubb/WGHrLl0BjB8Lw
-ETVJk5DNuZvsIIcm7ykk6iL4Tw==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGBDCCA+ygAwIBAgIQDcEmNIAVrDpUw5cH5ynutDANBgkqhkiG9w0BAQwFADCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIG1lLWNlbnRyYWwtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV
-BAcMB1NlYXR0bGUwIBcNMjIwNTA3MDA0MDIzWhgPMjEyMjA1MDcwMTQwMjNaMIGa
-MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j
-LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt
-YXpvbiBSRFMgbWUtY2VudHJhbC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKvADk8t
-Fl9bFlU5sajLPPDSOUpPAkKs6iPlz+27o1GJC88THcOvf3x0nVAcu9WYe9Qaas+4
-j4a0vv51agqyODRD/SNi2HnqW7DbtLPAm6KBHe4twl28ItB/JD5g7u1oPAHFoXMS
-cH1CZEAs5RtlZGzJhcBXLFsHNv/7+SCLyZ7+2XFh9OrtgU4wMzkHoRNndhfwV5bu
-17bPTwuH+VxH37zXf1mQ/KjhuJos0C9dL0FpjYBAuyZTAWhZKs8dpSe4DI544z4w
-gkwUB4bC2nA1TBzsywEAHyNuZ/xRjNpWvx0ToWAA2iFJqC3VO3iKcnBplMvaUuMt
-jwzVSNBnKcoabXCZL2XDLt4YTZR8FSwz05IvsmwcPB7uNTBXq3T9sjejW8QQK3vT
-tzyfLq4jKmQE7PoS6cqYm+hEPm2hDaC/WP9bp3FdEJxZlPH26fq1b7BWYWhQ9pBA
-Nv9zTnzdR1xohTyOJBUFQ81ybEzabqXqVXUIANqIOaNcTB09/sLJ7+zuMhp3mwBu
-LtjfJv8PLuT1r63bU3seROhKA98b5KfzjvbvPSg3vws78JQyoYGbqNyDfyjVjg3U
-v//AdVuPie6PNtdrW3upZY4Qti5IjP9e3kimaJ+KAtTgMRG56W0WxD3SP7+YGGbG
-KhntDOkKsN39hLpn9UOafTIqFu7kIaueEy/NAgMBAAGjQjBAMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFHAems86dTwdZbLe8AaPy3kfIUVoMA4GA1UdDwEB/wQE
-AwIBhjANBgkqhkiG9w0BAQwFAAOCAgEAOBHpp0ICx81kmeoBcZTrMdJs2gnhcd85
-FoSCjXx9H5XE5rmN/lQcxxOgj8hr3uPuLdLHu+i6THAyzjrl2NA1FWiqpfeECGmy
-0jm7iZsYORgGQYp/VKnDrwnKNSqlZvOuRr0kfUexwFlr34Y4VmupvEOK/RdGsd3S
-+3hiemcHse9ST/sJLHx962AWMkN86UHPscJEe4+eT3f2Wyzg6La8ARwdWZSNS+WH
-ZfybrncMmuiXuUdHv9XspPsqhKgtHhcYeXOGUtrwQPLe3+VJZ0LVxhlTWr9951GZ
-GfmWwTV/9VsyKVaCFIXeQ6L+gjcKyEzYF8wpMtQlSc7FFqwgC4bKxvMBSaRy88Nr
-lV2+tJD/fr8zGUeBK44Emon0HKDBWGX+/Hq1ZIv0Da0S+j6LbA4fusWxtGfuGha+
-luhHgVInCpALIOamiBEdGhILkoTtx7JrYppt3/Raqg9gUNCOOYlCvGhqX7DXeEfL
-DGabooiY2FNWot6h04JE9nqGj5QqT8D6t/TL1nzxhRPzbcSDIHUd/b5R+a0bAA+7
-YTU6JqzEVCWKEIEynYmqikgLMGB/OzWsgyEL6822QW6hJAQ78XpbNeCzrICF4+GC
-7KShLnwuWoWpAb26268lvOEvCTFM47VC6jNQl97md+2SA9Ma81C9wflid2M83Wle
-cuLMVcQZceE=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIQAhAteLRCvizAElaWORFU2zANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB
-bWF6b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyMDE3MDkxNloYDzIwNjEwNTIwMTgwOTE2WjCBmDEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6
-b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+qg7JAcOVKjh
-N83SACnBFZPyB63EusfDr/0V9ZdL8lKcmZX9sv/CqoBo3N0EvBqHQqUUX6JvFb7F
-XrMUZ740kr28gSRALfXTFgNODjXeDsCtEkKRTkac/UM8xXHn+hR7UFRPHS3e0GzI
-iLiwQWDkr0Op74W8aM0CfaVKvh2bp4BI1jJbdDnQ9OKXpOxNHGUf0ZGb7TkNPkgI
-b2CBAc8J5o3H9lfw4uiyvl6Fz5JoP+A+zPELAioYBXDrbE7wJeqQDJrETWqR9VEK
-BXURCkVnHeaJy123MpAX2ozf4pqk0V0LOEOZRS29I+USF5DcWr7QIXR/w2I8ws1Q
-7ys+qbE+kQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQFJ16n
-1EcCMOIhoZs/F9sR+Jy++zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBAOc5nXbT3XTDEZsxX2iD15YrQvmL5m13B3ImZWpx/pqmObsgx3/dg75rF2nQ
-qS+Vl+f/HLh516pj2BPP/yWCq12TRYigGav8UH0qdT3CAClYy2o+zAzUJHm84oiB
-ud+6pFVGkbqpsY+QMpJUbZWu52KViBpJMYsUEy+9cnPSFRVuRAHjYynSiLk2ZEjb
-Wkdc4x0nOZR5tP0FgrX0Ve2KcjFwVQJVZLgOUqmFYQ/G0TIIGTNh9tcmR7yp+xJR
-A2tbPV2Z6m9Yxx4E8lLEPNuoeouJ/GR4CkMEmF8cLwM310t174o3lKKUXJ4Vs2HO
-Wj2uN6R9oI+jGLMSswTzCNV1vgc=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICuDCCAj6gAwIBAgIRAOocLeZWjYkG/EbHmscuy8gwCgYIKoZIzj0EAwMwgZsx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h
-em9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMTA1MjEyMTUwMDFaGA8yMTIxMDUyMTIyNTAwMVowgZsx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE0MDIGA1UEAwwrQW1h
-em9uIFJEUyBhcC1zb3V0aGVhc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABCEr3jq1KtRncnZfK5cq
-btY0nW6ZG3FMbh7XwBIR6Ca0f8llGZ4vJEC1pXgiM/4Dh045B9ZIzNrR54rYOIfa
-2NcYZ7mk06DjIQML64hbAxbQzOAuNzLPx268MrlL2uW2XaNCMEAwDwYDVR0TAQH/
-BAUwAwEB/zAdBgNVHQ4EFgQUln75pChychwN4RfHl+tOinMrfVowDgYDVR0PAQH/
-BAQDAgGGMAoGCCqGSM49BAMDA2gAMGUCMGiyPINRU1mwZ4Crw01vpuPvxZxb2IOr
-yX3RNlOIu4We1H+5dQk5tIvH8KGYFbWEpAIxAO9NZ6/j9osMhLgZ0yj0WVjb+uZx
-YlZR9fyFisY/jNfX7QhSk+nrc3SFLRUNtpXrng==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEBTCCAu2gAwIBAgIRAKiaRZatN8eiz9p0s0lu0rQwDQYJKoZIhvcNAQELBQAw
-gZoxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEzMDEGA1UEAwwq
-QW1hem9uIFJEUyBjYS1jZW50cmFsLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYD
-VQQHDAdTZWF0dGxlMCAXDTIxMDUyMTIyMDIzNVoYDzIwNjEwNTIxMjMwMjM1WjCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGNhLWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV
-BAcMB1NlYXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCygVMf
-qB865IR9qYRBRFHn4eAqGJOCFx+UbraQZmjr/mnRqSkY+nhbM7Pn/DWOrRnxoh+w
-q5F9ZxdZ5D5T1v6kljVwxyfFgHItyyyIL0YS7e2h7cRRscCM+75kMedAP7icb4YN
-LfWBqfKHbHIOqvvQK8T6+Emu/QlG2B5LvuErrop9K0KinhITekpVIO4HCN61cuOe
-CADBKF/5uUJHwS9pWw3uUbpGUwsLBuhJzCY/OpJlDqC8Y9aToi2Ivl5u3/Q/sKjr
-6AZb9lx4q3J2z7tJDrm5MHYwV74elGSXoeoG8nODUqjgklIWAPrt6lQ3WJpO2kug
-8RhCdSbWkcXHfX95AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
-FOIxhqTPkKVqKBZvMWtKewKWDvDBMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B
-AQsFAAOCAQEAqoItII89lOl4TKvg0I1EinxafZLXIheLcdGCxpjRxlZ9QMQUN3yb
-y/8uFKBL0otbQgJEoGhxm4h0tp54g28M6TN1U0332dwkjYxUNwvzrMaV5Na55I2Z
-1hq4GB3NMXW+PvdtsgVOZbEN+zOyOZ5MvJHEQVkT3YRnf6avsdntltcRzHJ16pJc
-Y8rR7yWwPXh1lPaPkxddrCtwayyGxNbNmRybjR48uHRhwu7v2WuAMdChL8H8bp89
-TQLMrMHgSbZfee9hKhO4Zebelf1/cslRSrhkG0ESq6G5MUINj6lMg2g6F0F7Xz2v
-ncD/vuRN5P+vT8th/oZ0Q2Gc68Pun0cn/g==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/zCCAuegAwIBAgIRAJYlnmkGRj4ju/2jBQsnXJYwDQYJKoZIhvcNAQELBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyB1cy1lYXN0LTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyMTIzMDQ0NFoYDzIwNjEwNTIyMDAwNDQ0WjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIHVzLWVhc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC74V3eigv+pCj5
-nqDBqplY0Jp16pTeNB06IKbzb4MOTvNde6QjsZxrE1xUmprT8LxQqN9tI3aDYEYk
-b9v4F99WtQVgCv3Y34tYKX9NwWQgwS1vQwnIR8zOFBYqsAsHEkeJuSqAB12AYUSd
-Zv2RVFjiFmYJho2X30IrSLQfS/IE3KV7fCyMMm154+/K1Z2IJlcissydEAwgsUHw
-edrE6CxJVkkJ3EvIgG4ugK/suxd8eEMztaQYJwSdN8TdfT59LFuSPl7zmF3fIBdJ
-//WexcQmGabaJ7Xnx+6o2HTfkP8Zzzzaq8fvjAcvA7gyFH5EP26G2ZqMG+0y4pTx
-SPVTrQEXAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIWWuNEF
-sUMOC82XlfJeqazzrkPDMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
-AQEAgClmxcJaQTGpEZmjElL8G2Zc8lGc+ylGjiNlSIw8X25/bcLRptbDA90nuP+q
-zXAMhEf0ccbdpwxG/P5a8JipmHgqQLHfpkvaXx+0CuP++3k+chAJ3Gk5XtY587jX
-+MJfrPgjFt7vmMaKmynndf+NaIJAYczjhJj6xjPWmGrjM3MlTa9XesmelMwP3jep
-bApIWAvCYVjGndbK9byyMq1nyj0TUzB8oJZQooaR3MMjHTmADuVBylWzkRMxbKPl
-4Nlsk4Ef1JvIWBCzsMt+X17nuKfEatRfp3c9tbpGlAE/DSP0W2/Lnayxr4RpE9ds
-ICF35uSis/7ZlsftODUe8wtpkQ==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjOgAwIBAgIQS7vMpOTVq2Jw457NdZ2ffjAKBggqhkjOPQQDAzCBljEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6
-b24gUkRTIGNhLXdlc3QtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTAgFw0yMzA5MTkyMjExNDNaGA8yMTIzMDkxOTIzMTE0M1owgZYxCzAJBgNV
-BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD
-VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE
-UyBjYS13ZXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw
-djAQBgcqhkjOPQIBBgUrgQQAIgNiAARdgGSs/F2lpWKqS1ZpcmatFED1JurmNbXG
-Sqhv1A/geHrKCS15MPwjtnfZiujYKY4fNkCCUseoGDwkC4281nwkokvnfWR1/cXy
-LxfACoXNxsI4b+37CezSUBl48/5p1/OjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
-VR0OBBYEFFhLokGBuJGwKJhZcYSYKyZIitJtMA4GA1UdDwEB/wQEAwIBhjAKBggq
-hkjOPQQDAwNpADBmAjEA8aQQlzJRHbqFsRY4O3u/cN0T8dzjcqnYn4NV1w+jvhzt
-QPJLB+ggGyQhoFR6G2UrAjEA0be8OP5MWXD8d01KKbo5Dpy6TwukF5qoJmkFJKS3
-bKfEMvFWxXoV06HNZFWdI80u
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/zCCA+egAwIBAgIRAPvvd+MCcp8E36lHziv0xhMwDQYJKoZIhvcNAQEMBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyB1cy1lYXN0LTIgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyMTIzMTEwNloYDzIxMjEwNTIyMDAxMTA2WjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIHVzLWVhc3QtMiBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDbvwekKIKGcV/s
-lDU96a71ZdN2pTYkev1X2e2/ICb765fw/i1jP9MwCzs8/xHBEQBJSxdfO4hPeNx3
-ENi0zbM+TrMKliS1kFVe1trTTEaHYjF8BMK9yTY0VgSpWiGxGwg4tshezIA5lpu8
-sF6XMRxosCEVCxD/44CFqGZTzZaREIvvFPDTXKJ6yOYnuEkhH3OcoOajHN2GEMMQ
-ShuyRFDQvYkqOC/Q5icqFbKg7eGwfl4PmimdV7gOVsxSlw2s/0EeeIILXtHx22z3
-8QBhX25Lrq2rMuaGcD3IOMBeBo2d//YuEtd9J+LGXL9AeOXHAwpvInywJKAtXTMq
-Wsy3LjhuANFrzMlzjR2YdjkGVzeQVx3dKUzJ2//Qf7IXPSPaEGmcgbxuatxjnvfT
-H85oeKr3udKnXm0Kh7CLXeqJB5ITsvxI+Qq2iXtYCc+goHNR01QJwtGDSzuIMj3K
-f+YMrqBXZgYBwU2J/kCNTH31nfw96WTbOfNGwLwmVRDgguzFa+QzmQsJW4FTDMwc
-7cIjwdElQQVA+Gqa67uWmyDKAnoTkudmgAP+OTBkhnmc6NJuZDcy6f/iWUdl0X0u
-/tsfgXXR6ZovnHonM13ANiN7VmEVqFlEMa0VVmc09m+2FYjjlk8F9sC7Rc4wt214
-7u5YvCiCsFZwx44baP5viyRZgkJVpQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBQgCZCsc34nVTRbWsniXBPjnUTQ2DAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQEMBQADggIBAAQas3x1G6OpsIvQeMS9BbiHG3+kU9P/ba6Rrg+E
-lUz8TmL04Bcd+I+R0IyMBww4NznT+K60cFdk+1iSmT8Q55bpqRekyhcdWda1Qu0r
-JiTi7zz+3w2v66akofOnGevDpo/ilXGvCUJiLOBnHIF0izUqzvfczaMZGJT6xzKq
-PcEVRyAN1IHHf5KnGzUlVFv9SGy47xJ9I1vTk24JU0LWkSLzMMoxiUudVmHSqJtN
-u0h+n/x3Q6XguZi1/C1KOntH56ewRh8n5AF7c+9LJJSRM9wunb0Dzl7BEy21Xe9q
-03xRYjf5wn8eDELB8FZPa1PrNKXIOLYM9egdctbKEcpSsse060+tkyBrl507+SJT
-04lvJ4tcKjZFqxn+bUkDQvXYj0D3WK+iJ7a8kZJPRvz8BDHfIqancY8Tgw+69SUn
-WqIb+HNZqFuRs16WFSzlMksqzXv6wcDSyI7aZOmCGGEcYW9NHk8EuOnOQ+1UMT9C
-Qb1GJcipjRzry3M4KN/t5vN3hIetB+/PhmgTO4gKhBETTEyPC3HC1QbdVfRndB6e
-U/NF2U/t8U2GvD26TTFLK4pScW7gyw4FQyXWs8g8FS8f+R2yWajhtS9++VDJQKom
-fAUISoCH+PlPRJpu/nHd1Zrddeiiis53rBaLbXu2J1Q3VqjWOmtj0HjxJJxWnYmz
-Pqj2
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGATCCA+mgAwIBAgIRAI/U4z6+GF8/znpHM8Dq8G0wDQYJKoZIhvcNAQEMBQAw
-gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo
-QW1hem9uIFJEUyBhcC1zb3V0aC0yIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMjA2MDYyMTQ4MThaGA8yMTIyMDYwNjIyNDgxOFowgZgx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h
-em9uIFJEUyBhcC1zb3V0aC0yIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK5WqMvyq888
-3uuOtEj1FcP6iZhqO5kJurdJF59Otp2WCg+zv6I+QwaAspEWHQsKD405XfFsTGKV
-SKTCwoMxwBniuChSmyhlagQGKSnRY9+znOWq0v7hgmJRwp6FqclTbubmr+K6lzPy
-hs86mEp68O5TcOTYWUlPZDqfKwfNTbtCl5YDRr8Gxb5buHmkp6gUSgDkRsXiZ5VV
-b3GBmXRqbnwo5ZRNAzQeM6ylXCn4jKs310lQGUrFbrJqlyxUdfxzqdlaIRn2X+HY
-xRSYbHox3LVNPpJxYSBRvpQVFSy9xbX8d1v6OM8+xluB31cbLBtm08KqPFuqx+cO
-I2H5F0CYqYzhyOSKJsiOEJT6/uH4ewryskZzncx9ae62SC+bB5n3aJLmOSTkKLFY
-YS5IsmDT2m3iMgzsJNUKVoCx2zihAzgBanFFBsG+Xmoq0aKseZUI6vd2qpd5tUST
-/wS1sNk0Ph7teWB2ACgbFE6etnJ6stwjHFZOj/iTYhlnR2zDRU8akunFdGb6CB4/
-hMxGJxaqXSJeGtHm7FpadlUTf+2ESbYcVW+ui/F8sdBJseQdKZf3VdZZMgM0bcaX
-NE47cauDTy72WdU9YJX/YXKYMLDE0iFHTnGpfVGsuWGPYhlwZ3dFIO07mWnCRM6X
-u5JXRB1oy5n5HRluMsmpSN/R92MeBxKFAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFNtH0F0xfijSLHEyIkRGD9gW6NazMA4GA1UdDwEB/wQEAwIB
-hjANBgkqhkiG9w0BAQwFAAOCAgEACo+5jFeY3ygxoDDzL3xpfe5M0U1WxdKk+az4
-/OfjZvkoma7WfChi3IIMtwtKLYC2/seKWA4KjlB3rlTsCVNPnK6D+gAnybcfTKk/
-IRSPk92zagwQkSUWtAk80HpVfWJzpkSU16ejiajhedzOBRtg6BwsbSqLCDXb8hXr
-eXWC1S9ZceGc+LcKRHewGWPu31JDhHE9bNcl9BFSAS0lYVZqxIRWxivZ+45j5uQv
-wPrC8ggqsdU3K8quV6dblUQzzA8gKbXJpCzXZihkPrYpQHTH0szvXvgebh+CNUAG
-rUxm8+yTS0NFI3U+RLbcLFVzSvjMOnEwCX0SPj5XZRYYXs5ajtQCoZhTUkkwpDV8
-RxXk8qGKiXwUxDO8GRvmvM82IOiXz5w2jy/h7b7soyIgdYiUydMq4Ja4ogB/xPZa
-gf4y0o+bremO15HFf1MkaU2UxPK5FFVUds05pKvpSIaQWbF5lw4LHHj4ZtVup7zF
-CLjPWs4Hs/oUkxLMqQDw0FBwlqa4uot8ItT8uq5BFpz196ZZ+4WXw5PVzfSxZibI
-C/nwcj0AS6qharXOs8yPnPFLPSZ7BbmWzFDgo3tpglRqo3LbSPsiZR+sLeivqydr
-0w4RK1btRda5Ws88uZMmW7+2aufposMKcbAdrApDEAVzHijbB/nolS5nsnFPHZoA
-KDPtFEk=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtzCCAj2gAwIBAgIQVZ5Y/KqjR4XLou8MCD5pOjAKBggqhkjOPQQDAzCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLXNvdXRoZWFzdC00IFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIyMDUyNTE2NTgzM1oYDzIxMjIwNTI1MTc1ODMzWjCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLXNvdXRoZWFzdC00IFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEbo473OmpD5vkckdJajXg
-brhmNFyoSa0WCY1njuZC2zMFp3zP6rX4I1r3imrYnJd9pFH/aSiV/r6L5ACE5RPx
-4qdg5SQ7JJUaZc3DWsTOiOed7BCZSzM+KTYK/2QzDMApo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBTmogc06+1knsej1ltKUOdWFvwgsjAOBgNVHQ8BAf8E
-BAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIxAIs7TlLMbGTWNXpGiKf9DxaM07d/iDHe
-F/Vv/wyWSTGdobxBL6iArQNVXz0Gr4dvPAIwd0rsoa6R0x5mtvhdRPtM37FYrbHJ
-pbV+OMusQqcSLseunLBoCHenvJW0QOCQ8EDY
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGBTCCA+2gAwIBAgIRAO9dVdiLTEGO8kjUFExJmgowDQYJKoZIhvcNAQEMBQAw
-gZoxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEzMDEGA1UEAwwq
-QW1hem9uIFJEUyBpbC1jZW50cmFsLTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYD
-VQQHDAdTZWF0dGxlMCAXDTIyMTIwMjIwMjYwOFoYDzIxMjIxMjAyMjEyNjA4WjCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGlsLWNlbnRyYWwtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV
-BAcMB1NlYXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDkVHmJ
-bUc8CNDGBcgPmXHSHj5dS1PDnnpk3doCu6pahyYXW8tqAOmOqsDuNz48exY7YVy4
-u9I9OPBeTYB9ZUKwxq+1ZNLsr1cwVz5DdOyDREVFOjlU4rvw0eTgzhP5yw/d+Ai/
-+WmPebZG0irwPKN2f60W/KJ45UNtR+30MT8ugfnPuSHWjjV+dqCOCp/mj8nOCckn
-k8GoREwjuTFJMKInpQUC0BaVVX6LiIdgtoLY4wdx00EqNBuROoRTAvrked0jvm7J
-UI39CSYxhNZJ9F6LdESZXjI4u2apfNQeSoy6WptxFHr+kh2yss1B2KT6lbwGjwWm
-l9HODk9kbBNSy2NeewAms36q+p8wSLPavL28IRfK0UaBAiN1hr2a/2RDGCwOJmw6
-5erRC5IIX5kCStyXPEGhVPp18EvMuBd37eLIxjZBBO8AIDf4Ue8QmxSeZH0cT204
-3/Bd6XR6+Up9iMTxkHr1URcL1AR8Zd62lg/lbEfxePNMK9mQGxKP8eTMG5AjtW9G
-TatEoRclgE0wZQalXHmKpBNshyYdGqQZhzL1MxCxWzfHNgZkTKIsdzxrjnP7RiBR
-jdRH0YhXn6Y906QfLwMCaufwfQ5J8+nj/tu7nG138kSxsu6VUkhnQJhUcUsxuHD/
-NnBx0KGVEldtZiZf7ccgtRVp1lA0OrVtq3ZLMQIDAQABo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBQ2WC3p8rWeE2N0S4Om01KsNLpk/jAOBgNVHQ8BAf8E
-BAMCAYYwDQYJKoZIhvcNAQEMBQADggIBAFFEVDt45Obr6Ax9E4RMgsKjj4QjMFB9
-wHev1jL7hezl/ULrHuWxjIusaIZEIcKfn+v2aWtqOq13P3ht7jV5KsV29CmFuCdQ
-q3PWiAXVs+hnMskTOmGMDnptqd6/UuSIha8mlOKKAvnmRQJvfX9hIfb/b/mVyKWD
-uvTTmcy3cOTJY5ZIWGyzuvmcqA0YNcb7rkJt/iaLq4RX3/ofq4y4w36hefbcvj++
-pXHOmXk3dAej3y6SMBOUcGMyCJcCluRPNYKDTLn+fitcPxPC3JG7fI5bxQ0D6Hpa
-qbyGBQu96sfahQyMc+//H8EYlo4b0vPeS5RFFXJS/VBf0AyNT4vVc7H17Q6KjeNp
-wEARqsIa7UalHx9MnxrQ/LSTTxiC8qmDkIFuQtw8iQMN0SoL5S0eCZNRD31awgaY
-y1PvY8JMN549ugIUjOXnown/OxharLW1evWUraU5rArq3JfeFpPXl4K/u10T5SCL
-iJRoxFilGPMFE3hvnmbi5rEy8wRUn7TpLb4I4s/CB/lT2qZTPqvQHwxKCnMm9BKF
-NHb4rLL5dCvUi5NJ6fQ/exOoGdOVSfT7jqFeq2TtNunERSz9vpriweliB6iIe1Al
-Thj8aEs1GqA764rLVGA+vUe18NhjJm9EemrdIzjSQFy/NdbN/DMaHqEzJogWloAI
-izQWYnCS19TJ
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICvTCCAkOgAwIBAgIQCIY7E/bFvFN2lK9Kckb0dTAKBggqhkjOPQQDAzCBnjEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTcwNQYDVQQDDC5BbWF6
-b24gUkRTIFByZXZpZXcgdXMtZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYD
-VQQHDAdTZWF0dGxlMCAXDTIxMDUxODIxMDUxMFoYDzIxMjEwNTE4MjIwNTEwWjCB
-njELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTcwNQYDVQQDDC5B
-bWF6b24gUkRTIFByZXZpZXcgdXMtZWFzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEMI0hzf1JCEOI
-Eue4+DmcNnSs2i2UaJxHMrNGGfU7b42a7vwP53F7045ffHPBGP4jb9q02/bStZzd
-VHqfcgqkSRI7beBKjD2mfz82hF/wJSITTgCLs+NRpS6zKMFOFHUNo0IwQDAPBgNV
-HRMBAf8EBTADAQH/MB0GA1UdDgQWBBS8uF/6hk5mPLH4qaWv9NVZaMmyTjAOBgNV
-HQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIxAO7Pu9wzLyM0X7Q08uLIL+vL
-qaxe3UFuzFTWjM16MLJHbzLf1i9IDFKz+Q4hXCSiJwIwClMBsqT49BPUxVsJnjGr
-EbyEk6aOOVfY1p2yQL649zh3M4h8okLnwf+bYIb1YpeU
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIQY+JhwFEQTe36qyRlUlF8ozANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB
-bWF6b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUxOTE5MjQxNloYDzIwNjEwNTE5MjAyNDE2WjCBmDEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6
-b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnIye77j6ev40
-8wRPyN2OdKFSUfI9jB20Or2RLO+RDoL43+USXdrze0Wv4HMRLqaen9BcmCfaKMp0
-E4SFo47bXK/O17r6G8eyq1sqnHE+v288mWtYH9lAlSamNFRF6YwA7zncmE/iKL8J
-0vePHMHP/B6svw8LULZCk+nZk3tgxQn2+r0B4FOz+RmpkoVddfqqUPMbKUxhM2wf
-fO7F6bJaUXDNMBPhCn/3ayKCjYr49ErmnpYV2ZVs1i34S+LFq39J7kyv6zAgbHv9
-+/MtRMoRB1CjpqW0jIOZkHBdYcd1o9p1zFn591Do1wPkmMsWdjIYj+6e7UXcHvOB
-2+ScIRAcnwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQGtq2W
-YSyMMxpdQ3IZvcGE+nyZqTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBAEgoP3ixJsKSD5FN8dQ01RNHERl/IFbA7TRXfwC+L1yFocKnQh4Mp/msPRSV
-+OeHIvemPW/wtZDJzLTOFJ6eTolGekHK1GRTQ6ZqsWiU2fmiOP8ks4oSpI+tQ9Lw
-VrfZqTiEcS5wEIqyfUAZZfKDo7W1xp+dQWzfczSBuZJZwI5iaha7+ILM0r8Ckden
-TVTapc5pLSoO15v0ziRuQ2bT3V3nwu/U0MRK44z+VWOJdSiKxdnOYDs8hFNnKhfe
-klbTZF7kW7WbiNYB43OaAQBJ6BALZsIskEaqfeZT8FD71uN928TcEQyBDXdZpRN+
-iGQZDGhht0r0URGMDSs9waJtTfA=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/jCCA+agAwIBAgIQXY/dmS+72lZPranO2JM9jjANBgkqhkiG9w0BAQwFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIGFwLWVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTI1MjEzNDUxWhgPMjEyMTA1MjUyMjM0NTFaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgYXAtZWFzdC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMyW9kBJjD/hx8e8
-b5E1sF42bp8TXsz1htSYE3Tl3T1Aq379DfEhB+xa/ASDZxt7/vwa81BkNo4M6HYq
-okYIXeE7cu5SnSgjWXqcERhgPevtAwgmhdE3yREe8oz2DyOi2qKKZqah+1gpPaIQ
-fK0uAqoeQlyHosye3KZZKkDHBatjBsQ5kf8lhuf7wVulEZVRHY2bP2X7N98PfbpL
-QdH7mWXzDtJJ0LiwFwds47BrkgK1pkHx2p1mTo+HMkfX0P6Fq1atkVC2RHHtbB/X
-iYyH7paaHBzviFrhr679zNqwXIOKlbf74w3mS11P76rFn9rS1BAH2Qm6eY5S/Fxe
-HEKXm4kjPN63Zy0p3yE5EjPt54yPkvumOnT+RqDGJ2HCI9k8Ehcbve0ogfdRKNqQ
-VHWYTy8V33ndQRHZlx/CuU1yN61TH4WSoMly1+q1ihTX9sApmlQ14B2pJi/9DnKW
-cwECrPy1jAowC2UJ45RtC8UC05CbP9yrIy/7Noj8gQDiDOepm+6w1g6aNlWoiuQS
-kyI6nzz1983GcnOHya73ga7otXo0Qfg9jPghlYiMomrgshlSLDHZG0Ib/3hb8cnR
-1OcN9FpzNmVK2Ll1SmTMLrIhuCkyNYX9O/bOknbcf706XeESxGduSkHEjIw/k1+2
-Atteoq5dT6cwjnJ9hyhiueVlVkiDAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w
-HQYDVR0OBBYEFLUI+DD7RJs+0nRnjcwIVWzzYSsFMA4GA1UdDwEB/wQEAwIBhjAN
-BgkqhkiG9w0BAQwFAAOCAgEAb1mcCHv4qMQetLGTBH9IxsB2YUUhr5dda0D2BcHr
-UtDbfd0VQs4tux6h/6iKwHPx0Ew8fuuYj99WknG0ffgJfNc5/fMspxR/pc1jpdyU
-5zMQ+B9wi0lOZPO9uH7/pr+d2odcNEy8zAwqdv/ihsTwLmGP54is9fVbsgzNW1cm
-HKAVL2t/Ope+3QnRiRilKCN1lzhav4HHdLlN401TcWRWKbEuxF/FgxSO2Hmx86pj
-e726lweCTMmnq/cTsPOVY0WMjs0or3eHDVlyLgVeV5ldyN+ptg3Oit60T05SRa58
-AJPTaVKIcGQ/gKkKZConpu7GDofT67P/ox0YNY57LRbhsx9r5UY4ROgz7WMQ1yoS
-Y+19xizm+mBm2PyjMUbfwZUyCxsdKMwVdOq5/UmTmdms+TR8+m1uBHPOTQ2vKR0s
-Pd/THSzPuu+d3dbzRyDSLQbHFFneG760CUlD/ZmzFlQjJ89/HmAmz8IyENq+Sjhx
-Jgzy+FjVZb8aRUoYLlnffpUpej1n87Ynlr1GrvC4GsRpNpOHlwuf6WD4W0qUTsC/
-C9JO+fBzUj/aWlJzNcLEW6pte1SB+EdkR2sZvWH+F88TxemeDrV0jKJw5R89CDf8
-ZQNfkxJYjhns+YeV0moYjqQdc7tq4i04uggEQEtVzEhRLU5PE83nlh/K2NZZm8Kj
-dIA=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/zCCAuegAwIBAgIRAPVSMfFitmM5PhmbaOFoGfUwDQYJKoZIhvcNAQELBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyB1cy1lYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyNTIyMzQ1N1oYDzIwNjEwNTI1MjMzNDU3WjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIHVzLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDu9H7TBeGoDzMr
-dxN6H8COntJX4IR6dbyhnj5qMD4xl/IWvp50lt0VpmMd+z2PNZzx8RazeGC5IniV
-5nrLg0AKWRQ2A/lGGXbUrGXCSe09brMQCxWBSIYe1WZZ1iU1IJ/6Bp4D2YEHpXrW
-bPkOq5x3YPcsoitgm1Xh8ygz6vb7PsvJvPbvRMnkDg5IqEThapPjmKb8ZJWyEFEE
-QRrkCIRueB1EqQtJw0fvP4PKDlCJAKBEs/y049FoOqYpT3pRy0WKqPhWve+hScMd
-6obq8kxTFy1IHACjHc51nrGII5Bt76/MpTWhnJIJrCnq1/Uc3Qs8IVeb+sLaFC8K
-DI69Sw6bAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE7PCopt
-lyOgtXX0Y1lObBUxuKaCMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
-AQEAFj+bX8gLmMNefr5jRJfHjrL3iuZCjf7YEZgn89pS4z8408mjj9z6Q5D1H7yS
-jNETVV8QaJip1qyhh5gRzRaArgGAYvi2/r0zPsy+Tgf7v1KGL5Lh8NT8iCEGGXwF
-g3Ir+Nl3e+9XUp0eyyzBIjHtjLBm6yy8rGk9p6OtFDQnKF5OxwbAgip42CD75r/q
-p421maEDDvvRFR4D+99JZxgAYDBGqRRceUoe16qDzbMvlz0A9paCZFclxeftAxv6
-QlR5rItMz/XdzpBJUpYhdzM0gCzAzdQuVO5tjJxmXhkSMcDP+8Q+Uv6FA9k2VpUV
-E/O5jgpqUJJ2Hc/5rs9VkAPXeA==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrzCCAjWgAwIBAgIQW0yuFCle3uj4vWiGU0SaGzAKBggqhkjOPQQDAzCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGFmLXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTE5MTkzNTE2WhgPMjEyMTA1MTkyMDM1MTZaMIGXMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS
-RFMgYWYtc291dGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs
-ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABDPiKNZSaXs3Un/J/v+LTsFDANHpi7en
-oL2qh0u0DoqNzEBTbBjvO23bLN3k599zh6CY3HKW0r2k1yaIdbWqt4upMCRCcUFi
-I4iedAmubgzh56wJdoMZztjXZRwDthTkJKNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
-BgNVHQ4EFgQUWbYkcrvVSnAWPR5PJhIzppcAnZIwDgYDVR0PAQH/BAQDAgGGMAoG
-CCqGSM49BAMDA2gAMGUCMCESGqpat93CjrSEjE7z+Hbvz0psZTHwqaxuiH64GKUm
-mYynIiwpKHyBrzjKBmeDoQIxANGrjIo6/b8Jl6sdIZQI18V0pAyLfLiZjlHVOnhM
-MOTVgr82ZuPoEHTX78MxeMnYlw==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIRAIbsx8XOl0sgTNiCN4O+18QwDQYJKoZIhvcNAQELBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTI1MjE1NDU4WhgPMjA2MTA1MjUyMjU0NTha
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-tROxwXWCgn5R9gI/2Ivjzaxc0g95ysBjoJsnhPdJEHQb7w3y2kWrVWU3Y9fOitgb
-CEsnEC3PrhRnzNVW0fPsK6kbvOeCmjvY30rdbxbc8h+bjXfGmIOgAkmoULEr6Hc7
-G1Q/+tvv4lEwIs7bEaf+abSZxRJbZ0MBxhbHn7UHHDiMZYvzK+SV1MGCxx7JVhrm
-xWu3GC1zZCsGDhB9YqY9eR6PmjbqA5wy8vqbC57dZZa1QVtWIQn3JaRXn+faIzHx
-nLMN5CEWihsdmHBXhnRboXprE/OS4MFv1UrQF/XM/h5RBeCywpHePpC+Oe1T3LNC
-iP8KzRFrjC1MX/WXJnmOVQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBS33XbXAUMs1znyZo4B0+B3D68WFTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
-hvcNAQELBQADggEBADuadd2EmlpueY2VlrIIPC30QkoA1EOSoCmZgN6124apkoY1
-HiV4r+QNPljN4WP8gmcARnNkS7ZeR4fvWi8xPh5AxQCpiaBMw4gcbTMCuKDV68Pw
-P2dZCTMspvR3CDfM35oXCufdtFnxyU6PAyINUqF/wyTHguO3owRFPz64+sk3r2pT
-WHmJjG9E7V+KOh0s6REgD17Gqn6C5ijLchSrPUHB0wOIkeLJZndHxN/76h7+zhMt
-fFeNxPWHY2MfpcaLjz4UREzZPSB2U9k+y3pW1omCIcl6MQU9itGx/LpQE+H3ZeX2
-M2bdYd5L+ow+bdbGtsVKOuN+R9Dm17YpswF+vyQ=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGATCCA+mgAwIBAgIRAKlQ+3JX9yHXyjP/Ja6kZhkwDQYJKoZIhvcNAQEMBQAw
-gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo
-QW1hem9uIFJEUyBhcC1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMTA1MTkxNzQ1MjBaGA8yMTIxMDUxOTE4NDUyMFowgZgx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h
-em9uIFJEUyBhcC1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKtahBrpUjQ6
-H2mni05BAKU6Z5USPZeSKmBBJN3YgD17rJ93ikJxSgzJ+CupGy5rvYQ0xznJyiV0
-91QeQN4P+G2MjGQR0RGeUuZcfcZitJro7iAg3UBvw8WIGkcDUg+MGVpRv/B7ry88
-7E4OxKb8CPNoa+a9j6ABjOaaxaI22Bb7j3OJ+JyMICs6CU2bgkJaj3VUV9FCNUOc
-h9PxD4jzT9yyGYm/sK9BAT1WOTPG8XQUkpcFqy/IerZDfiQkf1koiSd4s5VhBkUn
-aQHOdri/stldT7a+HJFVyz2AXDGPDj+UBMOuLq0K6GAT6ThpkXCb2RIf4mdTy7ox
-N5BaJ+ih+Ro3ZwPkok60egnt/RN98jgbm+WstgjJWuLqSNInnMUgkuqjyBWwePqX
-Kib+wdpyx/LOzhKPEFpeMIvHQ3A0sjlulIjnh+j+itezD+dp0UNxMERlW4Bn/IlS
-sYQVNfYutWkRPRLErXOZXtlxxkI98JWQtLjvGzQr+jywxTiw644FSLWdhKa6DtfU
-2JWBHqQPJicMElfZpmfaHZjtXuCZNdZQXWg7onZYohe281ZrdFPOqC4rUq7gYamL
-T+ZB+2P+YCPOLJ60bj/XSvcB7mesAdg8P0DNddPhHUFWx2dFqOs1HxIVB4FZVA9U
-Ppbv4a484yxjTgG7zFZNqXHKTqze6rBBAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFCEAqjighncv/UnWzBjqu1Ka2Yb4MA4GA1UdDwEB/wQEAwIB
-hjANBgkqhkiG9w0BAQwFAAOCAgEAYyvumblckIXlohzi3QiShkZhqFzZultbFIu9
-GhA5CDar1IFMhJ9vJpO9nUK/camKs1VQRs8ZsBbXa0GFUM2p8y2cgUfLwFULAiC/
-sWETyW5lcX/xc4Pyf6dONhqFJt/ovVBxNZtcmMEWv/1D6Tf0nLeEb0P2i/pnSRR4
-Oq99LVFjossXtyvtaq06OSiUUZ1zLPvV6AQINg8dWeBOWRcQYhYcEcC2wQ06KShZ
-0ahuu7ar5Gym3vuLK6nH+eQrkUievVomN/LpASrYhK32joQ5ypIJej3sICIgJUEP
-UoeswJ+Z16f3ECoL1OSnq4A0riiLj1ZGmVHNhM6m/gotKaHNMxsK9zsbqmuU6IT/
-P6cR0S+vdigQG8ZNFf5vEyVNXhl8KcaJn6lMD/gMB2rY0qpaeTg4gPfU5wcg8S4Y
-C9V//tw3hv0f2n+8kGNmqZrylOQDQWSSo8j8M2SRSXiwOHDoTASd1fyBEIqBAwzn
-LvXVg8wQd1WlmM3b0Vrsbzltyh6y4SuKSkmgufYYvC07NknQO5vqvZcNoYbLNea3
-76NkFaMHUekSbwVejZgG5HGwbaYBgNdJEdpbWlA3X4yGRVxknQSUyt4dZRnw/HrX
-k8x6/wvtw7wht0/DOqz1li7baSsMazqxx+jDdSr1h9xML416Q4loFCLgqQhil8Jq
-Em4Hy3A=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEBDCCAuygAwIBAgIQFn6AJ+uxaPDpNVx7174CpjANBgkqhkiG9w0BAQsFADCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGlsLWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV
-BAcMB1NlYXR0bGUwIBcNMjIxMjAyMjAxNDA4WhgPMjA2MjEyMDIyMTE0MDhaMIGa
-MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j
-LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt
-YXpvbiBSRFMgaWwtY2VudHJhbC0xIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL2xGTSJ
-fXorki/dkkTqdLyv4U1neeFYEyUCPN/HJ7ZloNwhj8RBrHYhZ4qtvUAvN+rs8fUm
-L0wmaL69ye61S+CSfDzNwBDGwOzUm/cc1NEJOHCm8XA0unBNBvpJTjsFk2LQ+rz8
-oU0lVV4mjnfGektrTDeADonO1adJvUTYmF6v1wMnykSkp8AnW9EG/6nwcAJuAJ7d
-BfaLThm6lfxPdsBNG81DLKi2me2TLQ4yl+vgRKJi2fJWwA77NaDqQuD5upRIcQwt
-5noJt2kFFmeiro98ZMMRaDTHAHhJfWkwkw5f2QNIww7T4r85IwbQCgJVRo4m4ZTC
-W/1eiEccU2407mECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
-DNhVvGHzKXv0Yh6asK0apP9jJlUwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
-CwUAA4IBAQCoEVTUY/rF9Zrlpb1Y1hptEguw0i2pCLakcmv3YNj6thsubbGeGx8Z
-RjUA/gPKirpoae2HU1y64WEu7akwr6pdTRtXXjbe9NReT6OW/0xAwceSXCOiStqS
-cMsWWTGg6BA3uHqad5clqITjDZr1baQ8X8en4SXRBxXyhJXbOkB60HOQeFR9CNeh
-pJdrWLeNYXwU0Z59juqdVMGwvDAYdugWUhW2rhafVUXszfRA5c8Izc+E31kq90aY
-LmxFXUHUfG0eQOmxmg+Z/nG7yLUdHIFA3id8MRh22hye3KvRdQ7ZVGFni0hG2vQQ
-Q01AvD/rhzyjg0czzJKLK9U/RttwdMaV
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGBTCCA+2gAwIBAgIRAJfKe4Zh4aWNt3bv6ZjQwogwDQYJKoZIhvcNAQEMBQAw
-gZoxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEzMDEGA1UEAwwq
-QW1hem9uIFJEUyBjYS1jZW50cmFsLTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYD
-VQQHDAdTZWF0dGxlMCAXDTIxMDUyMTIyMDg1M1oYDzIxMjEwNTIxMjMwODUzWjCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGNhLWNlbnRyYWwtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV
-BAcMB1NlYXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCpgUH6
-Crzd8cOw9prAh2rkQqAOx2vtuI7xX4tmBG4I/um28eBjyVmgwQ1fpq0Zg2nCKS54
-Nn0pCmT7f3h6Bvopxn0J45AzXEtajFqXf92NQ3iPth95GVfAJSD7gk2LWMhpmID9
-JGQyoGuDPg+hYyr292X6d0madzEktVVGO4mKTF989qEg+tY8+oN0U2fRTrqa2tZp
-iYsmg350ynNopvntsJAfpCO/srwpsqHHLNFZ9jvhTU8uW90wgaKO9i31j/mHggCE
-+CAOaJCM3g+L8DPl/2QKsb6UkBgaaIwKyRgKSj1IlgrK+OdCBCOgM9jjId4Tqo2j
-ZIrrPBGl6fbn1+etZX+2/tf6tegz+yV0HHQRAcKCpaH8AXF44bny9andslBoNjGx
-H6R/3ib4FhPrnBMElzZ5i4+eM/cuPC2huZMBXb/jKgRC/QN1Wm3/nah5FWq+yn+N
-tiAF10Ga0BYzVhHDEwZzN7gn38bcY5yi/CjDUNpY0OzEe2+dpaBKPlXTaFfn9Nba
-CBmXPRF0lLGGtPeTAgjcju+NEcVa82Ht1pqxyu2sDtbu3J5bxp4RKtj+ShwN8nut
-Tkf5Ea9rSmHEY13fzgibZlQhXaiFSKA2ASUwgJP19Putm0XKlBCNSGCoECemewxL
-+7Y8FszS4Uu4eaIwvXVqUEE2yf+4ex0hqQ1acQIDAQABo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBSeUnXIRxNbYsZLtKomIz4Y1nOZEzAOBgNVHQ8BAf8E
-BAMCAYYwDQYJKoZIhvcNAQEMBQADggIBAIpRvxVS0dzoosBh/qw65ghPUGSbP2D4
-dm6oYCv5g/zJr4fR7NzEbHOXX5aOQnHbQL4M/7veuOCLNPOW1uXwywMg6gY+dbKe
-YtPVA1as8G9sUyadeXyGh2uXGsziMFXyaESwiAXZyiYyKChS3+g26/7jwECFo5vC
-XGhWpIO7Hp35Yglp8AnwnEAo/PnuXgyt2nvyTSrxlEYa0jus6GZEZd77pa82U1JH
-qFhIgmKPWWdvELA3+ra1nKnvpWM/xX0pnMznMej5B3RT3Y+k61+kWghJE81Ix78T
-+tG4jSotgbaL53BhtQWBD1yzbbilqsGE1/DXPXzHVf9yD73fwh2tGWSaVInKYinr
-a4tcrB3KDN/PFq0/w5/21lpZjVFyu/eiPj6DmWDuHW73XnRwZpHo/2OFkei5R7cT
-rn/YdDD6c1dYtSw5YNnS6hdCQ3sOiB/xbPRN9VWJa6se79uZ9NLz6RMOr73DNnb2
-bhIR9Gf7XAA5lYKqQk+A+stoKbIT0F65RnkxrXi/6vSiXfCh/bV6B41cf7MY/6YW
-ehserSdjhQamv35rTFdM+foJwUKz1QN9n9KZhPxeRmwqPitAV79PloksOnX25ElN
-SlyxdndIoA1wia1HRd26EFm2pqfZ2vtD2EjU3wD42CXX4H8fKVDna30nNFSYF0yn
-jGKc3k6UNxpg
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/jCCA+agAwIBAgIQaRHaEqqacXN20e8zZJtmDDANBgkqhkiG9w0BAQwFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIHVzLWVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTI1MjIzODM1WhgPMjEyMTA1MjUyMzM4MzVaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgdXMtZWFzdC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAInfBCaHuvj6Rb5c
-L5Wmn1jv2PHtEGMHm+7Z8dYosdwouG8VG2A+BCYCZfij9lIGszrTXkY4O7vnXgru
-JUNdxh0Q3M83p4X+bg+gODUs3jf+Z3Oeq7nTOk/2UYvQLcxP4FEXILxDInbQFcIx
-yen1ESHggGrjEodgn6nbKQNRfIhjhW+TKYaewfsVWH7EF2pfj+cjbJ6njjgZ0/M9
-VZifJFBgat6XUTOf3jwHwkCBh7T6rDpgy19A61laImJCQhdTnHKvzTpxcxiLRh69
-ZObypR7W04OAUmFS88V7IotlPmCL8xf7kwxG+gQfvx31+A9IDMsiTqJ1Cc4fYEKg
-bL+Vo+2Ii4W2esCTGVYmHm73drznfeKwL+kmIC/Bq+DrZ+veTqKFYwSkpHRyJCEe
-U4Zym6POqQ/4LBSKwDUhWLJIlq99bjKX+hNTJykB+Lbcx0ScOP4IAZQoxmDxGWxN
-S+lQj+Cx2pwU3S/7+OxlRndZAX/FKgk7xSMkg88HykUZaZ/ozIiqJqSnGpgXCtED
-oQ4OJw5ozAr+/wudOawaMwUWQl5asD8fuy/hl5S1nv9XxIc842QJOtJFxhyeMIXt
-LVECVw/dPekhMjS3Zo3wwRgYbnKG7YXXT5WMxJEnHu8+cYpMiRClzq2BEP6/MtI2
-AZQQUFu2yFjRGL2OZA6IYjxnXYiRAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w
-HQYDVR0OBBYEFADCcQCPX2HmkqQcmuHfiQ2jjqnrMA4GA1UdDwEB/wQEAwIBhjAN
-BgkqhkiG9w0BAQwFAAOCAgEASXkGQ2eUmudIKPeOIF7RBryCoPmMOsqP0+1qxF8l
-pGkwmrgNDGpmd9s0ArfIVBTc1jmpgB3oiRW9c6n2OmwBKL4UPuQ8O3KwSP0iD2sZ
-KMXoMEyphCEzW1I2GRvYDugL3Z9MWrnHkoaoH2l8YyTYvszTvdgxBPpM2x4pSkp+
-76d4/eRpJ5mVuQ93nC+YG0wXCxSq63hX4kyZgPxgCdAA+qgFfKIGyNqUIqWgeyTP
-n5OgKaboYk2141Rf2hGMD3/hsGm0rrJh7g3C0ZirPws3eeJfulvAOIy2IZzqHUSY
-jkFzraz6LEH3IlArT3jUPvWKqvh2lJWnnp56aqxBR7qHH5voD49UpJWY1K0BjGnS
-OHcurpp0Yt/BIs4VZeWdCZwI7JaSeDcPMaMDBvND3Ia5Fga0thgYQTG6dE+N5fgF
-z+hRaujXO2nb0LmddVyvE8prYlWRMuYFv+Co8hcMdJ0lEZlfVNu0jbm9/GmwAZ+l
-9umeYO9yz/uC7edC8XJBglMAKUmVK9wNtOckUWAcCfnPWYLbYa/PqtXBYcxrso5j
-iaS/A7iEW51uteHBGrViCy1afGG+hiUWwFlesli+Rq4dNstX3h6h2baWABaAxEVJ
-y1RnTQSz6mROT1VmZSgSVO37rgIyY0Hf0872ogcTS+FfvXgBxCxsNWEbiQ/XXva4
-0Ws=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtDCCAjqgAwIBAgIRAMyaTlVLN0ndGp4ffwKAfoMwCgYIKoZIzj0EAwMwgZkx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1h
-em9uIFJEUyBtZS1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjIwNTA3MDA0NDM3WhgPMjEyMjA1MDcwMTQ0MzdaMIGZMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMjAwBgNVBAMMKUFtYXpv
-biBSRFMgbWUtY2VudHJhbC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE19nCV1nsI6CohSor13+B25cr
-zg+IHdi9Y3L7ziQnHWI6yjBazvnKD+oC71aRRlR8b5YXsYGUQxWzPLHN7EGPcSGv
-bzA9SLG1KQYCJaQ0m9Eg/iGrwKWOgylbhVw0bCxoo0IwQDAPBgNVHRMBAf8EBTAD
-AQH/MB0GA1UdDgQWBBS4KsknsJXM9+QPEkBdZxUPaLr11zAOBgNVHQ8BAf8EBAMC
-AYYwCgYIKoZIzj0EAwMDaAAwZQIxAJaRgrYIEfXQMZQQDxMTYS0azpyWSseQooXo
-L3nYq4OHGBgYyQ9gVjvRYWU85PXbfgIwdi82DtANQFkCu+j+BU0JBY/uRKPEeYzo
-JG92igKIcXPqCoxIJ7lJbbzmuf73gQu5
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGATCCA+mgAwIBAgIRAJwCobx0Os8F7ihbJngxrR8wDQYJKoZIhvcNAQEMBQAw
-gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo
-QW1hem9uIFJEUyBtZS1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMTA1MjAxNzE1MzNaGA8yMTIxMDUyMDE4MTUzM1owgZgx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h
-em9uIFJEUyBtZS1zb3V0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANukKwlm+ZaI
-Y5MkWGbEVLApEyLmlrHLEg8PfiiEa9ts7jssQcin3bzEPdTqGr5jo91ONoZ3ccWq
-xJgg1W3bLu5CAO2CqIOXTXHRyCO/u0Ch1FGgWB8xETPSi3UHt/Vn1ltdO6DYdbDU
-mYgwzYrvLBdRCwxsb9o+BuYQHVFzUYonqk/y9ujz3gotzFq7r55UwDTA1ita3vb4
-eDKjIb4b1M4Wr81M23WHonpje+9qkkrAkdQcHrkgvSCV046xsq/6NctzwCUUNsgF
-7Q1a8ut5qJEYpz5ta8vI1rqFqAMBqCbFjRYlmAoTTpFPOmzAVxV+YoqTrW5A16su
-/2SXlMYfJ/n/ad/QfBNPPAAQMpyOr2RCL/YiL/PFZPs7NxYjnZHNWxMLSPgFyI+/
-t2klnn5jR76KJK2qimmaXedB90EtFsMRUU1e4NxH9gDuyrihKPJ3aVnZ35mSipvR
-/1KB8t8gtFXp/VQaz2sg8+uxPMKB81O37fL4zz6Mg5K8+aq3ejBiyHucpFGnsnVB
-3kQWeD36ONkybngmgWoyPceuSWm1hQ0Z7VRAQX+KlxxSaHmSaIk1XxZu9h9riQHx
-fMuev6KXjRn/CjCoUTn+7eFrt0dT5GryQEIZP+nA0oq0LKxogigHNZlwAT4flrqb
-JUfZJrqgoce5HjZSXl10APbtPjJi0fW9AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFEfV+LztI29OVDRm0tqClP3NrmEWMA4GA1UdDwEB/wQEAwIB
-hjANBgkqhkiG9w0BAQwFAAOCAgEAvSNe+0wuk53KhWlRlRf2x/97H2Q76X3anzF0
-5fOSVm022ldALzXMzqOfdnoKIhAu2oVKiHHKs7mMas+T6TL+Mkphx0CYEVxFE3PG
-061q3CqJU+wMm9W9xsB79oB2XG47r1fIEywZZ3GaRsatAbjcNOT8uBaATPQAfJFN
-zjFe4XyN+rA4cFrYNvfHTeu5ftrYmvks7JlRaJgEGWsz+qXux7uvaEEVPqEumd2H
-uYeaRNOZ2V23R009X5lbgBFx9tq5VDTnKhQiTQ2SeT0rc1W3Dz5ik6SbQQNP3nSR
-0Ywy7r/sZ3fcDyfFiqnrVY4Ympfvb4YW2PZ6OsQJbzH6xjdnTG2HtzEU30ngxdp1
-WUEF4zt6rjJCp7QBUqXgdlHvJqYu6949qtWjEPiFN9uSsRV2i1YDjJqN52dLjAPn
-AipJKo8x1PHTwUzuITqnB9BdP+5TlTl8biJfkEf/+08eWDTLlDHr2VrZLOLompTh
-bS5OrhDmqA2Q+O+EWrTIhMflwwlCpR9QYM/Xwvlbad9H0FUHbJsCVNaru3wGOgWo
-tt3dNSK9Lqnv/Ej9K9v6CRr36in4ylJKivhJ5B9E7ABHg7EpBJ1xi7O5eNDkNoJG
-+pFyphJq3AkBR2U4ni2tUaTAtSW2tks7IaiDV+UMtqZyGabT5ISQfWLLtLHSWn2F
-Tspdjbg=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIRAJZFh4s9aZGzKaTMLrSb4acwDQYJKoZIhvcNAQELBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBCZXRhIHVzLWVhc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTE4MjEyODQxWhgPMjA2MTA1MTgyMjI4NDFa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgQmV0YSB1cy1lYXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
-17i2yoU6diep+WrqxIn2CrDEO2NdJVwWTSckx4WMZlLpkQDoymSmkNHjq9ADIApD
-A31Cx+843apL7wub8QkFZD0Tk7/ThdHWJOzcAM3ov98QBPQfOC1W5zYIIRP2F+vQ
-TRETHQnLcW3rLv0NMk5oQvIKpJoC9ett6aeVrzu+4cU4DZVWYlJUoC/ljWzCluau
-8blfW0Vwin6OB7s0HCG5/wijQWJBU5SrP/KAIPeQi1GqG5efbqAXDr/ple0Ipwyo
-Xjjl73LenGUgqpANlC9EAT4i7FkJcllLPeK3NcOHjuUG0AccLv1lGsHAxZLgjk/x
-z9ZcnVV9UFWZiyJTKxeKPwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
-DgQWBBRWyMuZUo4gxCR3Luf9/bd2AqZ7CjAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZI
-hvcNAQELBQADggEBAIqN2DlIKlvDFPO0QUZQVFbsi/tLdYM98/vvzBpttlTGVMyD
-gJuQeHVz+MnhGIwoCGOlGU3OOUoIlLAut0+WG74qYczn43oA2gbMd7HoD7oL/IGg
-njorBwJVcuuLv2G//SqM3nxGcLRtkRnQ+lvqPxMz9+0fKFUn6QcIDuF0QSfthLs2
-WSiGEPKO9c9RSXdRQ4pXA7c3hXng8P4A2ZmdciPne5Nu4I4qLDGZYRrRLRkNTrOi
-TyS6r2HNGUfgF7eOSeKt3NWL+mNChcYj71/Vycf5edeczpUgfnWy9WbPrK1svKyl
-aAs2xg+X6O8qB+Mnj2dNBzm+lZIS3sIlm+nO9sg=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjSgAwIBAgIRAPAlEk8VJPmEzVRRaWvTh2AwCgYIKoZIzj0EAwMwgZYx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h
-em9uIFJEUyB1cy1lYXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTI1MjI0MTU1WhgPMjEyMTA1MjUyMzQxNTVaMIGWMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS
-RFMgdXMtZWFzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEx5xjrup8II4HOJw15NTnS3H5yMrQGlbj
-EDA5MMGnE9DmHp5dACIxmPXPMe/99nO7wNdl7G71OYPCgEvWm0FhdvVUeTb3LVnV
-BnaXt32Ek7/oxGk1T+Df03C+W0vmuJ+wo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G
-A1UdDgQWBBTGXmqBWN/1tkSea4pNw0oHrjk2UDAOBgNVHQ8BAf8EBAMCAYYwCgYI
-KoZIzj0EAwMDaAAwZQIxAIqqZWCSrIkZ7zsv/FygtAusW6yvlL935YAWYPVXU30m
-jkMFLM+/RJ9GMvnO8jHfCgIwB+whlkcItzE9CRQ6CsMo/d5cEHDUu/QW6jSIh9BR
-OGh9pTYPVkUbBiKPA7lVVhre
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/zCCA+egAwIBAgIRAJGY9kZITwfSRaAS/bSBOw8wDQYJKoZIhvcNAQEMBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyBzYS1lYXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUxOTE4MTEyMFoYDzIxMjEwNTE5MTkxMTIwWjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIHNhLWVhc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDe2vlDp6Eo4WQi
-Wi32YJOgdXHhxTFrLjB9SRy22DYoMaWfginJIwJcSR8yse8ZDQuoNhERB9LRggAE
-eng23mhrfvtL1yQkMlZfBu4vG1nOb22XiPFzk7X2wqz/WigdYNBCqa1kK3jrLqPx
-YUy7jk2oZle4GLVRTNGuMfcid6S2hs3UCdXfkJuM2z2wc3WUlvHoVNk37v2/jzR/
-hSCHZv5YHAtzL/kLb/e64QkqxKll5QmKhyI6d7vt6Lr1C0zb+DmwxUoJhseAS0hI
-dRk5DklMb4Aqpj6KN0ss0HAYqYERGRIQM7KKA4+hxDMUkJmt8KqWKZkAlCZgflzl
-m8NZ31o2cvBzf6g+VFHx+6iVrSkohVQydkCxx7NJ743iPKsh8BytSM4qU7xx4OnD
-H2yNXcypu+D5bZnVZr4Pywq0w0WqbTM2bpYthG9IC4JeVUvZ2mDc01lqOlbMeyfT
-og5BRPLDXdZK8lapo7se2teh64cIfXtCmM2lDSwm1wnH2iSK+AWZVIM3iE45WSGc
-vZ+drHfVgjJJ5u1YrMCWNL5C2utFbyF9Obw9ZAwm61MSbPQL9JwznhNlCh7F2ANW
-ZHWQPNcOAJqzE4uVcJB1ZeVl28ORYY1668lx+s9yYeMXk3QQdj4xmdnvoBFggqRB
-ZR6Z0D7ZohADXe024RzEo1TukrQgKQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBT7Vs4Y5uG/9aXnYGNMEs6ycPUT3jAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQEMBQADggIBACN4Htp2PvGcQA0/sAS+qUVWWJoAXSsu8Pgc6Gar
-7tKVlNJ/4W/a6pUV2Xo/Tz3msg4yiE8sMESp2k+USosD5n9Alai5s5qpWDQjrqrh
-76AGyF2nzve4kIN19GArYhm4Mz/EKEG1QHYvBDGgXi3kNvL/a2Zbybp+3LevG+q7
-xtx4Sz9yIyMzuT/6Y7ijtiMZ9XbuxGf5wab8UtwT3Xq1UradJy0KCkzRJAz/Wy/X
-HbTkEvKSaYKExH6sLo0jqdIjV/d2Io31gt4e0Ly1ER2wPyFa+pc/swu7HCzrN+iz
-A2ZM4+KX9nBvFyfkHLix4rALg+WTYJa/dIsObXkdZ3z8qPf5A9PXlULiaa1mcP4+
-rokw74IyLEYooQ8iSOjxumXhnkTS69MAdGzXYE5gnHokABtGD+BB5qLhtLt4fqAp
-8AyHpQWMyV42M9SJLzQ+iOz7kAgJOBOaVtJI3FV/iAg/eqWVm3yLuUTWDxSHrKuL
-N19+pSjF6TNvUSFXwEa2LJkfDqIOCE32iOuy85QY//3NsgrSQF6UkSPa95eJrSGI
-3hTRYYh3Up2GhBGl1KUy7/o0k3KRZTk4s38fylY8bZ3TakUOH5iIGoHyFVVcp361
-Pyy25SzFSmNalWoQd9wZVc/Cps2ldxhcttM+WLkFNzprd0VJa8qTz8vYtHP0ouDN
-nWS0
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtDCCAjmgAwIBAgIQKKqVZvk6NsLET+uYv5myCzAKBggqhkjOPQQDAzCBmTEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTIwMAYDVQQDDClBbWF6
-b24gUkRTIGlsLWNlbnRyYWwtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTAgFw0yMjEyMDIyMDMyMjBaGA8yMTIyMTIwMjIxMzIyMFowgZkxCzAJ
-BgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMw
-EQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1hem9u
-IFJEUyBpbC1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASYwfvj8BmvLAP6UkNQ4X4dXBB/
-webBO7swW+8HnFN2DAu+Cn/lpcDpu+dys1JmkVX435lrCH3oZjol0kCDIM1lF4Cv
-+78yoY1Jr/YMat22E4iz4AZd9q0NToS7+ZA0r2yjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFO/8Py16qPr7J2GWpvxlTMB+op7XMA4GA1UdDwEB/wQEAwIB
-hjAKBggqhkjOPQQDAwNpADBmAjEAwk+rg788+u8JL6sdix7l57WTo8E/M+o3TO5x
-uRuPdShrBFm4ArGR2PPs4zCQuKgqAjEAi0TA3PVqAxKpoz+Ps8/054p9WTgDfBFZ
-i/lm2yTaPs0xjY6FNWoy7fsVw5oEKxOn
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCTCCA/GgAwIBAgIRAOY7gfcBZgR2tqfBzMbFQCUwDQYJKoZIhvcNAQEMBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtNCBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjIwNTI1MTY1NDU5WhgPMjEyMjA1MjUxNzU0NTla
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtc291dGhlYXN0LTQgUm9vdCBDQSBSU0E0MDk2IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
-lfxER43FuLRdL08bddF0YhbCP+XXKj1A/TFMXmd2My8XDei8rPXFYyyjMig9+xZw
-uAsIxLwz8uiA26CKA8bCZKg5VG2kTeOJAfvBJaLv1CZefs3Z4Uf1Sjvm6MF2yqEj
-GoORfyfL9HiZFTDuF/hcjWoKYCfMuG6M/wO8IbdICrX3n+BiYQJu/pFO660Mg3h/
-8YBBWYDbHoCiH/vkqqJugQ5BM3OI5nsElW51P1icEEqti4AZ7JmtSv9t7fIFBVyR
-oaEyOgpp0sm193F/cDJQdssvjoOnaubsSYm1ep3awZAUyGN/X8MBrPY95d0hLhfH
-Ehc5Icyg+hsosBljlAyksmt4hFQ9iBnWIz/ZTfGMck+6p3HVL9RDgvluez+rWv59
-8q7omUGsiPApy5PDdwI/Wt/KtC34/2sjslIJfvgifdAtkRPkhff1WEwER00ADrN9
-eGGInaCpJfb1Rq8cV2n00jxg7DcEd65VR3dmIRb0bL+jWK62ni/WdEyomAOMfmGj
-aWf78S/4rasHllWJ+QwnaUYY3u6N8Cgio0/ep4i34FxMXqMV3V0/qXdfhyabi/LM
-wCxNo1Dwt+s6OtPJbwO92JL+829QAxydfmaMTeHBsgMPkG7RwAekeuatKGHNsc2Z
-x2Q4C2wVvOGAhcHwxfM8JfZs3nDSZJndtVVnFlUY0UECAwEAAaNCMEAwDwYDVR0T
-AQH/BAUwAwEB/zAdBgNVHQ4EFgQUpnG7mWazy6k97/tb5iduRB3RXgQwDgYDVR0P
-AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQCDLqq1Wwa9Tkuv7vxBnIeVvvFF
-ecTn+P+wJxl9Qa2ortzqTHZsBDyJO62d04AgBwiDXkJ9a+bthgG0H1J7Xee8xqv1
-xyX2yKj24ygHjspLotKP4eDMdDi5TYq+gdkbPmm9Q69B1+W6e049JVGXvWG8/7kU
-igxeuCYwtCCdUPRLf6D8y+1XMGgVv3/DSOHWvTg3MJ1wJ3n3+eve3rjGdRYWZeJu
-k21HLSZYzVrCtUsh2YAeLnUbSxVuT2Xr4JehYe9zW5HEQ8Je/OUfnCy9vzoN/ITw
-osAH+EBJQey7RxEDqMwCaRefH0yeHFcnOll0OXg/urnQmwbEYzQ1uutJaBPsjU0J
-Qf06sMxI7GiB5nPE+CnI2sM6A9AW9kvwexGXpNJiLxF8dvPQthpOKGcYu6BFvRmt
-6ctfXd9b7JJoVqMWuf5cCY6ihpk1e9JTlAqu4Eb/7JNyGiGCR40iSLvV28un9wiE
-plrdYxwcNYq851BEu3r3AyYWw/UW1AKJ5tM+/Gtok+AphMC9ywT66o/Kfu44mOWm
-L3nSLSWEcgfUVgrikpnyGbUnGtgCmHiMlUtNVexcE7OtCIZoVAlCGKNu7tyuJf10
-Qlk8oIIzfSIlcbHpOYoN79FkLoDNc2er4Gd+7w1oPQmdAB0jBJnA6t0OUBPKdDdE
-Ufff2jrbfbzECn1ELg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCDCCA/CgAwIBAgIQIuO1A8LOnmc7zZ/vMm3TrDANBgkqhkiG9w0BAQwFADCB
-nDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTUwMwYDVQQDDCxB
-bWF6b24gUkRTIGFwLXNvdXRoZWFzdC0yIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4G
-A1UEBwwHU2VhdHRsZTAgFw0yMTA1MjQyMDQ2MThaGA8yMTIxMDUyNDIxNDYxOFow
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMiBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDq
-qRHKbG8ZK6/GkGm2cenznEF06yHwI1gD5sdsHjTgekDZ2Dl9RwtDmUH2zFuIQwGj
-SeC7E2iKwrJRA5wYzL9/Vk8NOILEKQOP8OIKUHbc7q8rEtjs401KcU6pFBBEdO9G
-CTiRhogq+8mhC13AM/UriZJbKhwgM2UaDOzAneGMhQAGjH8z83NsNcPxpYVE7tqM
-sch5yLtIJLkJRusrmQQTeHUev16YNqyUa+LuFclFL0FzFCimkcxUhXlbfEKXbssS
-yPzjiv8wokGyo7+gA0SueceMO2UjfGfute3HlXZDcNvBbkSY+ver41jPydyRD6Qq
-oEkh0tyIbPoa3oU74kwipJtz6KBEA3u3iq61OUR0ENhR2NeP7CSKrC24SnQJZ/92
-qxusrbyV/0w+U4m62ug/o4hWNK1lUcc2AqiBOvCSJ7qpdteTFxcEIzDwYfERDx6a
-d9+3IPvzMb0ZCxBIIUFMxLTF7yAxI9s6KZBBXSZ6tDcCCYIgEysEPRWMRAcG+ye/
-fZVn9Vnzsj4/2wchC2eQrYpb1QvG4eMXA4M5tFHKi+/8cOPiUzJRgwS222J8YuDj
-yEBval874OzXk8H8Mj0JXJ/jH66WuxcBbh5K7Rp5oJn7yju9yqX6qubY8gVeMZ1i
-u4oXCopefDqa35JplQNUXbWwSebi0qJ4EK0V8F9Q+QIDAQABo0IwQDAPBgNVHRMB
-Af8EBTADAQH/MB0GA1UdDgQWBBT4ysqCxaPe7y+g1KUIAenqu8PAgzAOBgNVHQ8B
-Af8EBAMCAYYwDQYJKoZIhvcNAQEMBQADggIBALU8WN35KAjPZEX65tobtCDQFkIO
-uJjv0alD7qLB0i9eY80C+kD87HKqdMDJv50a5fZdqOta8BrHutgFtDm+xo5F/1M3
-u5/Vva5lV4xy5DqPajcF4Mw52czYBmeiLRTnyPJsU93EQIC2Bp4Egvb6LI4cMOgm
-4pY2hL8DojOC5PXt4B1/7c1DNcJX3CMzHDm4SMwiv2MAxSuC/cbHXcWMk+qXdrVx
-+ayLUSh8acaAOy3KLs1MVExJ6j9iFIGsDVsO4vr4ZNsYQiyHjp+L8ops6YVBO5AT
-k/pI+axHIVsO5qiD4cFWvkGqmZ0gsVtgGUchZaacboyFsVmo6QPrl28l6LwxkIEv
-GGJYvIBW8sfqtGRspjfX5TlNy5IgW/VOwGBdHHsvg/xpRo31PR3HOFw7uPBi7cAr
-FiZRLJut7af98EB2UvovZnOh7uIEGPeecQWeOTQfJeWet2FqTzFYd0NUMgqPuJx1
-vLKferP+ajAZLJvVnW1J7Vccx/pm0rMiUJEf0LRb/6XFxx7T2RGjJTi0EzXODTYI
-gnLfBBjnolQqw+emf4pJ4pAtly0Gq1KoxTG2QN+wTd4lsCMjnelklFDjejwnl7Uy
-vtxzRBAu/hi/AqDkDFf94m6j+edIrjbi9/JDFtQ9EDlyeqPgw0qwi2fwtJyMD45V
-fejbXelUSJSzDIdY
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCTCCA/GgAwIBAgIRAN7Y9G9i4I+ZaslPobE7VL4wDQYJKoZIhvcNAQEMBQAw
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1ub3J0aGVhc3QtMiBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwIBcNMjEwNTIwMTYzMzIzWhgPMjEyMTA1MjAxNzMzMjNa
-MIGcMQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywg
-SW5jLjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExNTAzBgNVBAMM
-LEFtYXpvbiBSRFMgYXAtbm9ydGhlYXN0LTIgUm9vdCBDQSBSU0E0MDk2IEcxMRAw
-DgYDVQQHDAdTZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA
-4BEPCiIfiK66Q/qa8k+eqf1Q3qsa6Xuu/fPkpuStXVBShhtXd3eqrM0iT4Xxs420
-Va0vSB3oZ7l86P9zYfa60n6PzRxdYFckYX330aI7L/oFIdaodB/C9szvROI0oLG+
-6RwmIF2zcprH0cTby8MiM7G3v9ykpq27g4WhDC1if2j8giOQL3oHpUaByekZNIHF
-dIllsI3RkXmR3xmmxoOxJM1B9MZi7e1CvuVtTGOnSGpNCQiqofehTGwxCN2wFSK8
-xysaWlw48G0VzZs7cbxoXMH9QbMpb4tpk0d+T8JfAPu6uWO9UwCLWWydf0CkmA/+
-D50/xd1t33X9P4FEaPSg5lYbHXzSLWn7oLbrN2UqMLaQrkoEBg/VGvzmfN0mbflw
-+T87bJ/VEOVNlG+gepyCTf89qIQVWOjuYMox4sK0PjzZGsYEuYiq1+OUT3vk/e5K
-ag1fCcq2Isy4/iwB2xcXrsQ6ljwdk1fc+EmOnjGKrhuOHJY3S+RFv4ToQBsVyYhC
-XGaC3EkqIX0xaCpDimxYhFjWhpDXAjG/zJ+hRLDAMCMhl/LPGRk/D1kzSbPmdjpl
-lEMK5695PeBvEBTQdBQdOiYgOU3vWU6tzwwHfiM2/wgvess/q0FDAHfJhppbgbb9
-3vgsIUcsvoC5o29JvMsUxsDRvsAfEmMSDGkJoA/X6GECAwEAAaNCMEAwDwYDVR0T
-AQH/BAUwAwEB/zAdBgNVHQ4EFgQUgEWm1mZCbGD6ytbwk2UU1aLaOUUwDgYDVR0P
-AQH/BAQDAgGGMA0GCSqGSIb3DQEBDAUAA4ICAQBb4+ABTGBGwxK1U/q4g8JDqTQM
-1Wh8Oz8yAk4XtPJMAmCctxbd81cRnSnePWw/hxViLVtkZ/GsemvXfqAQyOn1coN7
-QeYSw+ZOlu0j2jEJVynmgsR7nIRqE7QkCyZAU+d2FTJUfmee+IiBiGyFGgxz9n7A
-JhBZ/eahBbiuoOik/APW2JWLh0xp0W0GznfJ8lAlaQTyDa8iDXmVtbJg9P9qzkvl
-FgPXQttzEOyooF8Pb2LCZO4kUz+1sbU7tHdr2YE+SXxt6D3SBv+Yf0FlvyWLiqVk
-GDEOlPPTDSjAWgKnqST8UJ0RDcZK/v1ixs7ayqQJU0GUQm1I7LGTErWXHMnCuHKe
-UKYuiSZwmTcJ06NgdhcCnGZgPq13ryMDqxPeltQc3n5eO7f1cL9ERYLDLOzm6A9P
-oQ3MfcVOsbHgGHZWaPSeNrQRN9xefqBXH0ZPasgcH9WJdsLlEjVUXoultaHOKx3b
-UCCb+d3EfqF6pRT488ippOL6bk7zNubwhRa/+y4wjZtwe3kAX78ACJVcjPobH9jZ
-ErySads5zdQeaoee5wRKdp3TOfvuCe4bwLRdhOLCHWzEcXzY3g/6+ppLvNom8o+h
-Bh5X26G6KSfr9tqhQ3O9IcbARjnuPbvtJnoPY0gz3EHHGPhy0RNW8i2gl3nUp0ah
-PtjwbKW0hYAhIttT0Q==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtzCCAj2gAwIBAgIQQRBQTs6Y3H1DDbpHGta3lzAKBggqhkjOPQQDAzCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLXNvdXRoZWFzdC0zIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDYxMTAwMTI0M1oYDzIxMjEwNjExMDExMjQzWjCBmzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTQwMgYDVQQDDCtBbWF6
-b24gUkRTIGFwLXNvdXRoZWFzdC0zIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEs0942Xj4m/gKA+WA6F5h
-AHYuek9eGpzTRoLJddM4rEV1T3eSueytMVKOSlS3Ub9IhyQrH2D8EHsLYk9ktnGR
-pATk0kCYTqFbB7onNo070lmMJmGT/Q7NgwC8cySChFxbo0IwQDAPBgNVHRMBAf8E
-BTADAQH/MB0GA1UdDgQWBBQ20iKBKiNkcbIZRu0y1uoF1yJTEzAOBgNVHQ8BAf8E
-BAMCAYYwCgYIKoZIzj0EAwMDaAAwZQIwYv0wTSrpQTaPaarfLN8Xcqrqu3hzl07n
-FrESIoRw6Cx77ZscFi2/MV6AFyjCV/TlAjEAhpwJ3tpzPXpThRML8DMJYZ3YgMh3
-CMuLqhPpla3cL0PhybrD27hJWl29C4el6aMO
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrDCCAjOgAwIBAgIQGcztRyV40pyMKbNeSN+vXTAKBggqhkjOPQQDAzCBljEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6
-b24gUkRTIHVzLWVhc3QtMiBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTAgFw0yMTA1MjEyMzE1NTZaGA8yMTIxMDUyMjAwMTU1NlowgZYxCzAJBgNV
-BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD
-VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE
-UyB1cy1lYXN0LTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw
-djAQBgcqhkjOPQIBBgUrgQQAIgNiAAQfDcv+GGRESD9wT+I5YIPRsD3L+/jsiIis
-Tr7t9RSbFl+gYpO7ZbDXvNbV5UGOC5lMJo/SnqFRTC6vL06NF7qOHfig3XO8QnQz
-6T5uhhrhnX2RSY3/10d2kTyHq3ZZg3+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
-VR0OBBYEFLDyD3PRyNXpvKHPYYxjHXWOgfPnMA4GA1UdDwEB/wQEAwIBhjAKBggq
-hkjOPQQDAwNnADBkAjB20HQp6YL7CqYD82KaLGzgw305aUKw2aMrdkBR29J183jY
-6Ocj9+Wcif9xnRMS+7oCMAvrt03rbh4SU9BohpRUcQ2Pjkh7RoY0jDR4Xq4qzjNr
-5UFr3BXpFvACxXF51BksGQ==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjWgAwIBAgIQeKbS5zvtqDvRtwr5H48cAjAKBggqhkjOPQQDAzCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIG1lLXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTIwMTcxOTU1WhgPMjEyMTA1MjAxODE5NTVaMIGXMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS
-RFMgbWUtc291dGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs
-ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABEKjgUaAPmUlRMEQdBC7BScAGosJ1zRV
-LDd38qTBjzgmwBfQJ5ZfGIvyEK5unB09MB4e/3qqK5I/L6Qn5Px/n5g4dq0c7MQZ
-u7G9GBYm90U3WRJBf7lQrPStXaRnS4A/O6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
-BgNVHQ4EFgQUNKcAbGEIn03/vkwd8g6jNyiRdD4wDgYDVR0PAQH/BAQDAgGGMAoG
-CCqGSM49BAMDA2cAMGQCMHIeTrjenCSYuGC6txuBt/0ZwnM/ciO9kHGWVCoK8QLs
-jGghb5/YSFGZbmQ6qpGlSAIwVOQgdFfTpEfe5i+Vs9frLJ4QKAfc27cTNYzRIM0I
-E+AJgK4C4+DiyyMzOpiCfmvq
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGCDCCA/CgAwIBAgIQSFkEUzu9FYgC5dW+5lnTgjANBgkqhkiG9w0BAQwFADCB
-nDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTUwMwYDVQQDDCxB
-bWF6b24gUkRTIGFwLXNvdXRoZWFzdC0zIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4G
-A1UEBwwHU2VhdHRsZTAgFw0yMTA2MTEwMDA4MzZaGA8yMTIxMDYxMTAxMDgzNlow
-gZwxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTE1MDMGA1UEAwws
-QW1hem9uIFJEUyBhcC1zb3V0aGVhc3QtMyBSb290IENBIFJTQTQwOTYgRzExEDAO
-BgNVBAcMB1NlYXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDx
-my5Qmd8zdwaI/KOKV9Xar9oNbhJP5ED0JCiigkuvCkg5qM36klszE8JhsUj40xpp
-vQw9wkYW4y+C8twBpzKGBvakqMnoaVUV7lOCKx0RofrnNwkZCboTBB4X/GCZ3fIl
-YTybS7Ehi1UuiaZspIT5A2jidoA8HiBPk+mTg1UUkoWS9h+MEAPa8L4DY6fGf4pO
-J1Gk2cdePuNzzIrpm2yPto+I8MRROwZ3ha7ooyymOXKtz2c7jEHHJ314boCXAv9G
-cdo27WiebewZkHHH7Zx9iTIVuuk2abyVSzvLVeGv7Nuy4lmSqa5clWYqWsGXxvZ2
-0fZC5Gd+BDUMW1eSpW7QDTk3top6x/coNoWuLSfXiC5ZrJkIKimSp9iguULgpK7G
-abMMN4PR+O+vhcB8E879hcwmS2yd3IwcPTl3QXxufqeSV58/h2ibkqb/W4Bvggf6
-5JMHQPlPHOqMCVFIHP1IffIo+Of7clb30g9FD2j3F4qgV3OLwEDNg/zuO1DiAvH1
-L+OnmGHkfbtYz+AVApkAZrxMWwoYrwpauyBusvSzwRE24vLTd2i80ZDH422QBLXG
-rN7Zas8rwIiBKacJLYtBYETw8mfsNt8gb72aIQX6cZOsphqp6hUtKaiMTVgGazl7
-tBXqbB+sIv3S9X6bM4cZJKkMJOXbnyCCLZFYv8TurwIDAQABo0IwQDAPBgNVHRMB
-Af8EBTADAQH/MB0GA1UdDgQWBBTOVtaS1b/lz6yJDvNk65vEastbQTAOBgNVHQ8B
-Af8EBAMCAYYwDQYJKoZIhvcNAQEMBQADggIBABEONg+TmMZM/PrYGNAfB4S41zp1
-3CVjslZswh/pC4kgXSf8cPJiUOzMwUevuFQj7tCqxQtJEygJM2IFg4ViInIah2kh
-xlRakEGGw2dEVlxZAmmLWxlL1s1lN1565t5kgVwM0GVfwYM2xEvUaby6KDVJIkD3
-aM6sFDBshvVA70qOggM6kU6mwTbivOROzfoIQDnVaT+LQjHqY/T+ok6IN0YXXCWl
-Favai8RDjzLDFwXSRvgIK+1c49vlFFY4W9Efp7Z9tPSZU1TvWUcKdAtV8P2fPHAS
-vAZ+g9JuNfeawhEibjXkwg6Z/yFUueQCQOs9TRXYogzp5CMMkfdNJF8byKYqHscs
-UosIcETnHwqwban99u35sWcoDZPr6aBIrz7LGKTJrL8Nis8qHqnqQBXu/fsQEN8u
-zJ2LBi8sievnzd0qI0kaWmg8GzZmYH1JCt1GXSqOFkI8FMy2bahP7TUQR1LBUKQ3
-hrOSqldkhN+cSAOnvbQcFzLr+iEYEk34+NhcMIFVE+51KJ1n6+zISOinr6mI3ckX
-6p2tmiCD4Shk2Xx/VTY/KGvQWKFcQApWezBSvDNlGe0yV71LtLf3dr1pr4ofo7cE
-rYucCJ40bfxEU/fmzYdBF32xP7AOD9U0FbOR3Mcthc6Z6w20WFC+zru8FGY08gPf
-WT1QcNdw7ntUJP/w
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrzCCAjWgAwIBAgIQARky6+5PNFRkFVOp3Ob1CTAKBggqhkjOPQQDAzCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGV1LXNvdXRoLTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjIwNTIzMTg0MTI4WhgPMjEyMjA1MjMxOTQxMjdaMIGXMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS
-RFMgZXUtc291dGgtMiBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs
-ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABNVGL5oF7cfIBxKyWd2PVK/S5yQfaJY3
-QFHWvEdt6951n9JhiiPrHzfVHsxZp1CBjILRMzjgRbYWmc8qRoLkgGE7htGdwudJ
-Fa/WuKzO574Prv4iZXUnVGTboC7JdvKbh6NCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
-BgNVHQ4EFgQUgDeIIEKynwUbNXApdIPnmRWieZwwDgYDVR0PAQH/BAQDAgGGMAoG
-CCqGSM49BAMDA2gAMGUCMEOOJfucrST+FxuqJkMZyCM3gWGZaB+/w6+XUAJC6hFM
-uSTY0F44/bERkA4XhH+YGAIxAIpJQBakCA1/mXjsTnQ+0El9ty+LODp8ibkn031c
-8DKDS7pR9UK7ZYdR6zFg3ZCjQw==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjOgAwIBAgIQJvkWUcYLbnxtuwnyjMmntDAKBggqhkjOPQQDAzCBljEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMS8wLQYDVQQDDCZBbWF6
-b24gUkRTIGV1LXdlc3QtMyBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTAgFw0yMTA1MjUyMjI2MTJaGA8yMTIxMDUyNTIzMjYxMlowgZYxCzAJBgNV
-BAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMwEQYD
-VQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1hem9uIFJE
-UyBldS13ZXN0LTMgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0bGUw
-djAQBgcqhkjOPQIBBgUrgQQAIgNiAARENn8uHCyjn1dFax4OeXxvbV861qsXFD9G
-DshumTmFzWWHN/69WN/AOsxy9XN5S7Cgad4gQgeYYYgZ5taw+tFo/jQvCLY//uR5
-uihcLuLJ78opvRPvD9kbWZ6oXfBtFkWjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYD
-VR0OBBYEFKiK3LpoF+gDnqPldGSwChBPCYciMA4GA1UdDwEB/wQEAwIBhjAKBggq
-hkjOPQQDAwNpADBmAjEA+7qfvRlnvF1Aosyp9HzxxCbN7VKu+QXXPhLEBWa5oeWW
-UOcifunf/IVLC4/FGCsLAjEAte1AYp+iJyOHDB8UYkhBE/1sxnFaTiEPbvQBU0wZ
-SuwWVLhu2wWDuSW+K7tTuL8p
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/zCCAuegAwIBAgIRAKeDpqX5WFCGNo94M4v69sUwDQYJKoZIhvcNAQELBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyBldS13ZXN0LTMgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyNTIyMTgzM1oYDzIwNjEwNTI1MjMxODMzWjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGV1LXdlc3QtMyBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCcKOTEMTfzvs4H
-WtJR8gI7GXN6xesulWtZPv21oT+fLGwJ+9Bv8ADCGDDrDxfeH/HxJmzG9hgVAzVn
-4g97Bn7q07tGZM5pVi96/aNp11velZT7spOJKfJDZTlGns6DPdHmx48whpdO+dOb
-6+eR0VwCIv+Vl1fWXgoACXYCoKjhxJs+R+fwY//0JJ1YG8yjZ+ghLCJmvlkOJmE1
-TCPUyIENaEONd6T+FHGLVYRRxC2cPO65Jc4yQjsXvvQypoGgx7FwD5voNJnFMdyY
-754JGPOOe/SZdepN7Tz7UEq8kn7NQSbhmCsgA/Hkjkchz96qN/YJ+H/okiQUTNB0
-eG9ogiVFAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFjayw9Y
-MjbxfF14XAhMM2VPl0PfMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
-AQEAAtmx6d9+9CWlMoU0JCirtp4dSS41bBfb9Oor6GQ8WIr2LdfZLL6uES/ubJPE
-1Sh5Vu/Zon5/MbqLMVrfniv3UpQIof37jKXsjZJFE1JVD/qQfRzG8AlBkYgHNEiS
-VtD4lFxERmaCkY1tjKB4Dbd5hfhdrDy29618ZjbSP7NwAfnwb96jobCmMKgxVGiH
-UqsLSiEBZ33b2hI7PJ6iTJnYBWGuiDnsWzKRmheA4nxwbmcQSfjbrNwa93w3caL2
-v/4u54Kcasvcu3yFsUwJygt8z43jsGAemNZsS7GWESxVVlW93MJRn6M+MMakkl9L
-tWaXdHZ+KUV7LhfYLb0ajvb40w==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEBDCCAuygAwIBAgIQJ5oxPEjefCsaESSwrxk68DANBgkqhkiG9w0BAQsFADCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGV1LWNlbnRyYWwtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNV
-BAcMB1NlYXR0bGUwIBcNMjIwNjA2MjExNzA1WhgPMjA2MjA2MDYyMjE3MDVaMIGa
-MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j
-LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt
-YXpvbiBSRFMgZXUtY2VudHJhbC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALTQt5eX
-g+VP3BjO9VBkWJhE0GfLrU/QIk32I6WvrnejayTrlup9H1z4QWlXF7GNJrqScRMY
-KhJHlcP05aPsx1lYco6pdFOf42ybXyWHHJdShj4A5glU81GTT+VrXGzHSarLmtua
-eozkQgPpDsSlPt0RefyTyel7r3Cq+5K/4vyjCTcIqbfgaGwTU36ffjM1LaPCuE4O
-nINMeD6YuImt2hU/mFl20FZ+IZQUIFZZU7pxGLqTRz/PWcH8tDDxnkYg7tNuXOeN
-JbTpXrw7St50/E9ZQ0llGS+MxJD8jGRAa/oL4G/cwnV8P2OEPVVkgN9xDDQeieo0
-3xkzolkDkmeKOnUCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
-bwu8635iQGQMRanekesORM8Hkm4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEB
-CwUAA4IBAQAgN6LE9mUgjsj6xGCX1afYE69fnmCjjb0rC6eEe1mb/QZNcyw4XBIW
-6+zTXo4mjZ4ffoxb//R0/+vdTE7IvaLgfAZgFsLKJCtYDDstXZj8ujQnGR9Pig3R
-W+LpNacvOOSJSawNQq0Xrlcu55AU4buyD5VjcICnfF1dqBMnGTnh27m/scd/ZMx/
-kapHZ/fMoK2mAgSX/NvUKF3UkhT85vSSM2BTtET33DzCPDQTZQYxFBa4rFRmFi4c
-BLlmIReiCGyh3eJhuUUuYAbK6wLaRyPsyEcIOLMQmZe1+gAFm1+1/q5Ke9ugBmjf
-PbTWjsi/lfZ5CdVAhc5lmZj/l5aKqwaS
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjSgAwIBAgIRAKKPTYKln9L4NTx9dpZGUjowCgYIKoZIzj0EAwMwgZYx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h
-em9uIFJEUyBldS13ZXN0LTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTIxMjI1NTIxWhgPMjEyMTA1MjEyMzU1MjFaMIGWMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS
-RFMgZXUtd2VzdC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE/owTReDvaRqdmbtTzXbyRmEpKCETNj6O
-hZMKH0F8oU9Tmn8RU7kQQj6xUKEyjLPrFBN7c+26TvrVO1KmJAvbc8bVliiJZMbc
-C0yV5PtJTalvlMZA1NnciZuhxaxrzlK1o0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G
-A1UdDgQWBBT4i5HaoHtrs7Mi8auLhMbKM1XevDAOBgNVHQ8BAf8EBAMCAYYwCgYI
-KoZIzj0EAwMDaAAwZQIxAK9A+8/lFdX4XJKgfP+ZLy5ySXC2E0Spoy12Gv2GdUEZ
-p1G7c1KbWVlyb1d6subzkQIwKyH0Naf/3usWfftkmq8SzagicKz5cGcEUaULq4tO
-GzA/AMpr63IDBAqkZbMDTCmH
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrzCCAjWgAwIBAgIQTgIvwTDuNWQo0Oe1sOPQEzAKBggqhkjOPQQDAzCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGV1LW5vcnRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTI0MjEwNjM4WhgPMjEyMTA1MjQyMjA2MzhaMIGXMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpvbiBS
-RFMgZXUtbm9ydGgtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwHU2VhdHRs
-ZTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJuzXLU8q6WwSKXBvx8BbdIi3mPhb7Xo
-rNJBfuMW1XRj5BcKH1ZoGaDGw+BIIwyBJg8qNmCK8kqIb4cH8/Hbo3Y+xBJyoXq/
-cuk8aPrxiNoRsKWwiDHCsVxaK9L7GhHHAqNCMEAwDwYDVR0TAQH/BAUwAwEB/zAd
-BgNVHQ4EFgQUYgcsdU4fm5xtuqLNppkfTHM2QMYwDgYDVR0PAQH/BAQDAgGGMAoG
-CCqGSM49BAMDA2gAMGUCMQDz/Rm89+QJOWJecYAmYcBWCcETASyoK1kbr4vw7Hsg
-7Ew3LpLeq4IRmTyuiTMl0gMCMAa0QSjfAnxBKGhAnYxcNJSntUyyMpaXzur43ec0
-3D8npJghwC4DuICtKEkQiI5cSg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGATCCA+mgAwIBAgIRAORIGqQXLTcbbYT2upIsSnQwDQYJKoZIhvcNAQEMBQAw
-gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo
-QW1hem9uIFJEUyBldS1zb3V0aC0yIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMjA1MjMxODM0MjJaGA8yMTIyMDUyMzE5MzQyMlowgZgx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h
-em9uIFJEUyBldS1zb3V0aC0yIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPKukwsW2s/h
-1k+Hf65pOP0knVBnOnMQyT1mopp2XHGdXznj9xS49S30jYoUnWccyXgD983A1bzu
-w4fuJRHg4MFdz/NWTgXvy+zy0Roe83OPIJjUmXnnzwUHQcBa9vl6XUO65iQ3pbSi
-fQfNDFXD8cvuXbkezeADoy+iFAlzhXTzV9MD44GTuo9Z3qAXNGHQCrgRSCL7uRYt
-t1nfwboCbsVRnElopn2cTigyVXE62HzBUmAw1GTbAZeFAqCn5giBWYAfHwTUldRL
-6eEa6atfsS2oPNus4ZENa1iQxXq7ft+pMdNt0qKXTCZiiCZjmLkY0V9kWwHTRRF8
-r+75oSL//3di43QnuSCgjwMRIeWNtMud5jf3eQzSBci+9njb6DrrSUbx7blP0srg
-94/C/fYOp/0/EHH34w99Th14VVuGWgDgKahT9/COychLOubXUT6vD1As47S9KxTv
-yYleVKwJnF9cVjepODN72fNlEf74BwzgSIhUmhksmZSeJBabrjSUj3pdyo/iRZN/
-CiYz9YPQ29eXHPQjBZVIUqWbOVfdwsx0/Xu5T1e7yyXByQ3/oDulahtcoKPAFQ3J
-ee6NJK655MdS7pM9hJnU2Rzu3qZ/GkM6YK7xTlMXVouPUZov/VbiaCKbqYDs8Dg+
-UKdeNXAT6+BMleGQzly1X7vjhgeA8ugVAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFJdaPwpCf78UolFTEn6GO85/QwUIMA4GA1UdDwEB/wQEAwIB
-hjANBgkqhkiG9w0BAQwFAAOCAgEAWkxHIT3mers5YnZRSVjmpxCLivGj1jMB9VYC
-iKqTAeIvD0940L0YaZgivQll5pue8UUcQ6M2uCdVVAsNJdmQ5XHIYiGOknYPtxzO
-aO+bnZp7VIZw/vJ49hvH6RreA2bbxYMZO/ossYdcWsWbOKHFrRmAw0AhtK/my51g
-obV7eQg+WmlE5Iqc75ycUsoZdc3NimkjBi7LQoNP1HMvlLHlF71UZhQDdq+/WdV7
-0zmg+epkki1LjgMmuPyb+xWuYkFKT1/faX+Xs62hIm5BY+aI4if4RuQ+J//0pOSs
-UajrjTo+jLGB8A96jAe8HaFQenbwMjlaHRDAF0wvbkYrMr5a6EbneAB37V05QD0Y
-Rh4L4RrSs9DX2hbSmS6iLDuPEjanHKzglF5ePEvnItbRvGGkynqDVlwF+Bqfnw8l
-0i8Hr1f1/LP1c075UjkvsHlUnGgPbLqA0rDdcxF8Fdlv1BunUjX0pVlz10Ha5M6P
-AdyWUOneOfaA5G7jjv7i9qg3r99JNs1/Lmyg/tV++gnWTAsSPFSSEte81kmPhlK3
-2UtAO47nOdTtk+q4VIRAwY1MaOR7wTFZPfer1mWs4RhKNu/odp8urEY87iIzbMWT
-QYO/4I6BGj9rEWNGncvR5XTowwIthMCj2KWKM3Z/JxvjVFylSf+s+FFfO1bNIm6h
-u3UBpZI=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtDCCAjmgAwIBAgIQenQbcP/Zbj9JxvZ+jXbRnTAKBggqhkjOPQQDAzCBmTEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTIwMAYDVQQDDClBbWF6
-b24gUkRTIGV1LWNlbnRyYWwtMSBSb290IENBIEVDQzM4NCBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTAgFw0yMTA1MjEyMjMzMjRaGA8yMTIxMDUyMTIzMzMyNFowgZkxCzAJ
-BgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMuMRMw
-EQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1hem9u
-IFJEUyBldS1jZW50cmFsLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATlBHiEM9LoEb1Hdnd5j2VpCDOU
-5nGuFoBD8ROUCkFLFh5mHrHfPXwBc63heW9WrP3qnDEm+UZEUvW7ROvtWCTPZdLz
-Z4XaqgAlSqeE2VfUyZOZzBSgUUJk7OlznXfkCMOjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFDT/ThjQZl42Nv/4Z/7JYaPNMly2MA4GA1UdDwEB/wQEAwIB
-hjAKBggqhkjOPQQDAwNpADBmAjEAnZWmSgpEbmq+oiCa13l5aGmxSlfp9h12Orvw
-Dq/W5cENJz891QD0ufOsic5oGq1JAjEAp5kSJj0MxJBTHQze1Aa9gG4sjHBxXn98
-4MP1VGsQuhfndNHQb4V0Au7OWnOeiobq
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/zCCAuegAwIBAgIRAMgnyikWz46xY6yRgiYwZ3swDQYJKoZIhvcNAQELBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyBldS13ZXN0LTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyMDE2NDkxMloYDzIwNjEwNTIwMTc0OTEyWjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGV1LXdlc3QtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCi8JYOc9cYSgZH
-gYPxLk6Xcc7HqzamvsnjYU98Dcb98y6iDqS46Ra2Ne02MITtU5MDL+qjxb8WGDZV
-RUA9ZS69tkTO3gldW8QdiSh3J6hVNJQW81F0M7ZWgV0gB3n76WCmfT4IWos0AXHM
-5v7M/M4tqVmCPViQnZb2kdVlM3/Xc9GInfSMCgNfwHPTXl+PXX+xCdNBePaP/A5C
-5S0oK3HiXaKGQAy3K7VnaQaYdiv32XUatlM4K2WS4AMKt+2cw3hTCjlmqKRHvYFQ
-veWCXAuc+U5PQDJ9SuxB1buFJZhT4VP3JagOuZbh5NWpIbOTxlAJOb5pGEDuJTKi
-1gQQQVEFAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNXm+N87
-OFxK9Af/bjSxDCiulGUzMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOC
-AQEAkqIbkgZ45spvrgRQ6n9VKzDLvNg+WciLtmVrqyohwwJbj4pYvWwnKQCkVc7c
-hUOSBmlSBa5REAPbH5o8bdt00FPRrD6BdXLXhaECKgjsHe1WW08nsequRKD8xVmc
-8bEX6sw/utBeBV3mB+3Zv7ejYAbDFM4vnRsWtO+XqgReOgrl+cwdA6SNQT9oW3e5
-rSQ+VaXgJtl9NhkiIysq9BeYigxqS/A13pHQp0COMwS8nz+kBPHhJTsajHCDc8F4
-HfLi6cgs9G0gaRhT8FCH66OdGSqn196sE7Y3bPFFFs/3U+vxvmQgoZC6jegQXAg5
-Prxd+VNXtNI/azitTysQPumH7A==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEBTCCAu2gAwIBAgIRAO8bekN7rUReuNPG8pSTKtEwDQYJKoZIhvcNAQELBQAw
-gZoxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEzMDEGA1UEAwwq
-QW1hem9uIFJEUyBldS1jZW50cmFsLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYD
-VQQHDAdTZWF0dGxlMCAXDTIxMDUyMTIyMjM0N1oYDzIwNjEwNTIxMjMyMzQ3WjCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGV1LWNlbnRyYWwtMSBSb290IENBIFJTQTIwNDggRzExEDAOBgNV
-BAcMB1NlYXR0bGUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCTTYds
-Tray+Q9VA5j5jTh5TunHKFQzn68ZbOzdqaoi/Rq4ohfC0xdLrxCpfqn2TGDHN6Zi
-2qGK1tWJZEd1H0trhzd9d1CtGK+3cjabUmz/TjSW/qBar7e9MA67/iJ74Gc+Ww43
-A0xPNIWcL4aLrHaLm7sHgAO2UCKsrBUpxErOAACERScVYwPAfu79xeFcX7DmcX+e
-lIqY16pQAvK2RIzrekSYfLFxwFq2hnlgKHaVgZ3keKP+nmXcXmRSHQYUUr72oYNZ
-HcNYl2+gxCc9ccPEHM7xncVEKmb5cWEWvVoaysgQ+osi5f5aQdzgC2X2g2daKbyA
-XL/z5FM9GHpS5BJjAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYE
-FBDAiJ7Py9/A9etNa/ebOnx5l5MGMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0B
-AQsFAAOCAQEALMh/+81fFPdJV/RrJUeoUvFCGMp8iaANu97NpeJyKitNOv7RoeVP
-WjivS0KcCqZaDBs+p6IZ0sLI5ZH098LDzzytcfZg0PsGqUAb8a0MiU/LfgDCI9Ee
-jsOiwaFB8k0tfUJK32NPcIoQYApTMT2e26lPzYORSkfuntme2PTHUnuC7ikiQrZk
-P+SZjWgRuMcp09JfRXyAYWIuix4Gy0eZ4rpRuaTK6mjAb1/LYoNK/iZ/gTeIqrNt
-l70OWRsWW8jEmSyNTIubGK/gGGyfuZGSyqoRX6OKHESkP6SSulbIZHyJ5VZkgtXo
-2XvyRyJ7w5pFyoofrL3Wv0UF8yt/GDszmg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/zCCA+egAwIBAgIRAMDk/F+rrhdn42SfE+ghPC8wDQYJKoZIhvcNAQEMBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyBldS13ZXN0LTIgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyMTIyNTEyMloYDzIxMjEwNTIxMjM1MTIyWjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGV1LXdlc3QtMiBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2twMALVg9vRVu
-VNqsr6N8thmp3Dy8jEGTsm3GCQ+C5P2YcGlD/T/5icfWW84uF7Sx3ezcGlvsqFMf
-Ukj9sQyqtz7qfFFugyy7pa/eH9f48kWFHLbQYm9GEgbYBIrWMp1cy3vyxuMCwQN4
-DCncqU+yNpy0CprQJEha3PzY+3yJOjDQtc3zr99lyECCFJTDUucxHzyQvX89eL74
-uh8la0lKH3v9wPpnEoftbrwmm5jHNFdzj7uXUHUJ41N7af7z7QUfghIRhlBDiKtx
-5lYZemPCXajTc3ryDKUZC/b+B6ViXZmAeMdmQoPE0jwyEp/uaUcdp+FlUQwCfsBk
-ayPFEApTWgPiku2isjdeTVmEgL8bJTDUZ6FYFR7ZHcYAsDzcwHgIu3GGEMVRS3Uf
-ILmioiyly9vcK4Sa01ondARmsi/I0s7pWpKflaekyv5boJKD/xqwz9lGejmJHelf
-8Od2TyqJScMpB7Q8c2ROxBwqwB72jMCEvYigB+Wnbb8RipliqNflIGx938FRCzKL
-UQUBmNAznR/yRRL0wHf9UAE/8v9a09uZABeiznzOFAl/frHpgdAbC00LkFlnwwgX
-g8YfEFlkp4fLx5B7LtoO6uVNFVimLxtwirpyKoj3G4M/kvSTux8bTw0heBCmWmKR
-57MS6k7ODzbv+Kpeht2hqVZCNFMxoQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBRuMnDhJjoj7DcKALj+HbxEqj3r6jAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQEMBQADggIBALSnXfx72C3ldhBP5kY4Mo2DDaGQ8FGpTOOiD95d
-0rf7I9LrsBGVqu/Nir+kqqP80PB70+Jy9fHFFigXwcPBX3MpKGxK8Cel7kVf8t1B
-4YD6A6bqlzP+OUL0uGWfZpdpDxwMDI2Flt4NEldHgXWPjvN1VblEKs0+kPnKowyg
-jhRMgBbD/y+8yg0fIcjXUDTAw/+INcp21gWaMukKQr/8HswqC1yoqW9in2ijQkpK
-2RB9vcQ0/gXR0oJUbZQx0jn0OH8Agt7yfMAnJAdnHO4M3gjvlJLzIC5/4aGrRXZl
-JoZKfJ2fZRnrFMi0nhAYDeInoS+Rwx+QzaBk6fX5VPyCj8foZ0nmqvuYoydzD8W5
-mMlycgxFqS+DUmO+liWllQC4/MnVBlHGB1Cu3wTj5kgOvNs/k+FW3GXGzD3+rpv0
-QTLuwSbMr+MbEThxrSZRSXTCQzKfehyC+WZejgLb+8ylLJUA10e62o7H9PvCrwj+
-ZDVmN7qj6amzvndCP98sZfX7CFZPLfcBd4wVIjHsFjSNEwWHOiFyLPPG7cdolGKA
-lOFvonvo4A1uRc13/zFeP0Xi5n5OZ2go8aOOeGYdI2vB2sgH9R2IASH/jHmr0gvY
-0dfBCcfXNgrS0toq0LX/y+5KkKOxh52vEYsJLdhqrveuZhQnsFEm/mFwjRXkyO7c
-2jpC
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGADCCA+igAwIBAgIQYe0HgSuFFP9ivYM2vONTrTANBgkqhkiG9w0BAQwFADCB
-mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB
-bWF6b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUxOTE4MzMyMVoYDzIxMjEwNTE5MTkzMzIxWjCBmDEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6
-b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuO7QPKfPMTo2
-POQWvzDLwi5f++X98hGjORI1zkN9kotCYH5pAzSBwBPoMNaIfedgmsIxGHj2fq5G
-4oXagNhNuGP79Zl6uKW5H7S74W7aWM8C0s8zuxMOI4GZy5h2IfQk3m/3AzZEX5w8
-UtNPkzo2feDVOkerHT+j+vjXgAxZ4wHnuMDcRT+K4r9EXlAH6X9b/RO0JlfEwmNz
-xlqqGxocq9qRC66N6W0HF2fNEAKP84n8H80xcZBOBthQORRi8HSmKcPdmrvwCuPz
-M+L+j18q6RAVaA0ABbD0jMWcTf0UvjUfBStn5mvu/wGlLjmmRkZsppUTRukfwqXK
-yltUsTq0tOIgCIpne5zA4v+MebbR5JBnsvd4gdh5BI01QH470yB7BkUefZ9bobOm
-OseAAVXcYFJKe4DAA6uLDrqOfFSxV+CzVvEp3IhLRaik4G5MwI/h2c/jEYDqkg2J
-HMflxc2gcSMdk7E5ByLz5f6QrFfSDFk02ZJTs4ssbbUEYohht9znPMQEaWVqATWE
-3n0VspqZyoBNkH/agE5GiGZ/k/QyeqzMNj+c9kr43Upu8DpLrz8v2uAp5xNj3YVg
-ihaeD6GW8+PQoEjZ3mrCmH7uGLmHxh7Am59LfEyNrDn+8Rq95WvkmbyHSVxZnBmo
-h/6O3Jk+0/QhIXZ2hryMflPcYWeRGH0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
-/zAdBgNVHQ4EFgQU2eFK7+R3x/me8roIBNxBrplkM6EwDgYDVR0PAQH/BAQDAgGG
-MA0GCSqGSIb3DQEBDAUAA4ICAQB5gWFe5s7ObQFj1fTO9L6gYgtFhnwdmxU0q8Ke
-HWCrdFmyXdC39qdAFOwM5/7fa9zKmiMrZvy9HNvCXEp4Z7z9mHhBmuqPZQx0qPgU
-uLdP8wGRuWryzp3g2oqkX9t31Z0JnkbIdp7kfRT6ME4I4VQsaY5Y3mh+hIHOUvcy
-p+98i3UuEIcwJnVAV9wTTzrWusZl9iaQ1nSYbmkX9bBssJ2GmtW+T+VS/1hJ/Q4f
-AlE3dOQkLFoPPb3YRWBHr2n1LPIqMVwDNAuWavRA2dSfaLl+kzbn/dua7HTQU5D4
-b2Fu2vLhGirwRJe+V7zdef+tI7sngXqjgObyOeG5O2BY3s+um6D4fS0Th3QchMO7
-0+GwcIgSgcjIjlrt6/xJwJLE8cRkUUieYKq1C4McpZWTF30WnzOPUzRzLHkcNzNA
-0A7sKMK6QoYWo5Rmo8zewUxUqzc9oQSrYADP7PEwGncLtFe+dlRFx+PA1a+lcIgo
-1ZGfXigYtQ3VKkcknyYlJ+hN4eCMBHtD81xDy9iP2MLE41JhLnoB2rVEtewO5diF
-7o95Mwl84VMkLhhHPeGKSKzEbBtYYBifHNct+Bst8dru8UumTltgfX6urH3DN+/8
-JF+5h3U8oR2LL5y76cyeb+GWDXXy9zoQe2QvTyTy88LwZq1JzujYi2k8QiLLhFIf
-FEv9Bg==
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICsDCCAjagAwIBAgIRAMgApnfGYPpK/fD0dbN2U4YwCgYIKoZIzj0EAwMwgZcx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwnQW1h
-em9uIFJEUyBldS1zb3V0aC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMCAXDTIxMDUxOTE4MzgxMVoYDzIxMjEwNTE5MTkzODExWjCBlzELMAkG
-A1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4xEzAR
-BgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6b24g
-UkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1NlYXR0
-bGUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQfEWl6d4qSuIoECdZPp+39LaKsfsX7
-THs3/RrtT0+h/jl3bjZ7Qc68k16x+HGcHbaayHfqD0LPdzH/kKtNSfQKqemdxDQh
-Z4pwkixJu8T1VpXZ5zzCvBXCl75UqgEFS92jQjBAMA8GA1UdEwEB/wQFMAMBAf8w
-HQYDVR0OBBYEFFPrSNtWS5JU+Tvi6ABV231XbjbEMA4GA1UdDwEB/wQEAwIBhjAK
-BggqhkjOPQQDAwNoADBlAjEA+a7hF1IrNkBd2N/l7IQYAQw8chnRZDzh4wiGsZsC
-6A83maaKFWUKIb3qZYXFSi02AjAbp3wxH3myAmF8WekDHhKcC2zDvyOiKLkg9Y6v
-ZVmyMR043dscQbcsVoacOYv198c=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICtDCCAjqgAwIBAgIRAPhVkIsQ51JFhD2kjFK5uAkwCgYIKoZIzj0EAwMwgZkx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEyMDAGA1UEAwwpQW1h
-em9uIFJEUyBldS1jZW50cmFsLTIgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjIwNjA2MjEyOTE3WhgPMjEyMjA2MDYyMjI5MTdaMIGZMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMjAwBgNVBAMMKUFtYXpv
-biBSRFMgZXUtY2VudHJhbC0yIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEA5xnIEBtG5b2nmbj49UEwQza
-yX0844fXjccYzZ8xCDUe9dS2XOUi0aZlGblgSe/3lwjg8fMcKXLObGGQfgIx1+5h
-AIBjORis/dlyN5q/yH4U5sjS8tcR0GDGVHrsRUZCo0IwQDAPBgNVHRMBAf8EBTAD
-AQH/MB0GA1UdDgQWBBRK+lSGutXf4DkTjR3WNfv4+KeNFTAOBgNVHQ8BAf8EBAMC
-AYYwCgYIKoZIzj0EAwMDaAAwZQIxAJ4NxQ1Gerqr70ZrnUqc62Vl8NNqTzInamCG
-Kce3FTsMWbS9qkgrjZkO9QqOcGIw/gIwSLrwUT+PKr9+H9eHyGvpq9/3AIYSnFkb
-Cf3dyWPiLKoAtLFwjzB/CkJlsAS1c8dS
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/jCCA+agAwIBAgIQGZH12Q7x41qIh9vDu9ikTjANBgkqhkiG9w0BAQwFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIGV1LXdlc3QtMyBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTI1MjIyMjMzWhgPMjEyMTA1MjUyMzIyMzNaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgZXUtd2VzdC0zIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMqE47sHXWzdpuqj
-JHb+6jM9tDbQLDFnYjDWpq4VpLPZhb7xPNh9gnYYTPKG4avG421EblAHqzy9D2pN
-1z90yKbIfUb/Sy2MhQbmZomsObhONEra06fJ0Dydyjswf1iYRp2kwpx5AgkVoNo7
-3dlws73zFjD7ImKvUx2C7B75bhnw2pJWkFnGcswl8fZt9B5Yt95sFOKEz2MSJE91
-kZlHtya19OUxZ/cSGci4MlOySzqzbGwUqGxEIDlY8I39VMwXaYQ8uXUN4G780VcL
-u46FeyRGxZGz2n3hMc805WAA1V5uir87vuirTvoSVREET97HVRGVVNJJ/FM6GXr1
-VKtptybbo81nefYJg9KBysxAa2Ao2x2ry/2ZxwhS6VZ6v1+90bpZA1BIYFEDXXn/
-dW07HSCFnYSlgPtSc+Muh15mdr94LspYeDqNIierK9i4tB6ep7llJAnq0BU91fM2
-JPeqyoTtc3m06QhLf68ccSxO4l8Hmq9kLSHO7UXgtdjfRVaffngopTNk8qK7bIb7
-LrgkqhiQw/PRCZjUdyXL153/fUcsj9nFNe25gM4vcFYwH6c5trd2tUl31NTi1MfG
-Mgp3d2dqxQBIYANkEjtBDMy3SqQLIo9EymqmVP8xx2A/gCBgaxvMAsI6FSWRoC7+
-hqJ8XH4mFnXSHKtYMe6WPY+/XZgtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8w
-HQYDVR0OBBYEFIkXqTnllT/VJnI2NqipA4XV8rh1MA4GA1UdDwEB/wQEAwIBhjAN
-BgkqhkiG9w0BAQwFAAOCAgEAKjSle8eenGeHgT8pltWCw/HzWyQruVKhfYIBfKJd
-MhV4EnH5BK7LxBIvpXGsFUrb0ThzSw0fn0zoA9jBs3i/Sj6KyeZ9qUF6b8ycDXd+
-wHonmJiQ7nk7UuMefaYAfs06vosgl1rI7eBHC0itexIQmKh0aX+821l4GEgEoSMf
-loMFTLXv2w36fPHHCsZ67ODldgcZbKNnpCTX0YrCwEYO3Pz/L398btiRcWGrewrK
-jdxAAyietra8DRno1Zl87685tfqc6HsL9v8rVw58clAo9XAQvT+fmSOFw/PogRZ7
-OMHUat3gu/uQ1M5S64nkLLFsKu7jzudBuoNmcJysPlzIbqJ7vYc82OUGe9ucF3wi
-3tbKQ983hdJiTExVRBLX/fYjPsGbG3JtPTv89eg2tjWHlPhCDMMxyRKl6isu2RTq
-6VT489Z2zQrC33MYF8ZqO1NKjtyMAMIZwxVu4cGLkVsqFmEV2ScDHa5RadDyD3Ok
-m+mqybhvEVm5tPgY6p0ILPMN3yvJsMSPSvuBXhO/X5ppNnpw9gnxpwbjQKNhkFaG
-M5pkADZ14uRguOLM4VthSwUSEAr5VQYCFZhEwK+UOyJAGiB/nJz6IxL5XBNUXmRM
-Hl8Xvz4riq48LMQbjcVQj0XvH941yPh+P8xOi00SGaQRaWp55Vyr4YKGbV0mEDz1
-r1o=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIF/zCCA+egAwIBAgIRAKwYju1QWxUZpn6D1gOtwgQwDQYJKoZIhvcNAQEMBQAw
-gZcxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEwMC4GA1UEAwwn
-QW1hem9uIFJEUyBldS13ZXN0LTEgUm9vdCBDQSBSU0E0MDk2IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyMDE2NTM1NFoYDzIxMjEwNTIwMTc1MzU0WjCBlzEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdBbWF6
-b24gUkRTIGV1LXdlc3QtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCKdBP1U4lqWWkc
-Cb25/BKRTsvNVnISiKocva8GAzJyKfcGRa85gmgu41U+Hz6+39K+XkRfM0YS4BvQ
-F1XxWT0bNyypuvwCvmYShSTjN1TY0ltncDddahTajE/4MdSOZb/c98u0yt03cH+G
-hVwRyT50h0v/UEol50VfwcVAEZEgcQQYhf1IFUFlIvKpmDOqLuFakOnc7c9akK+i
-ivST+JO1tgowbnNkn2iLlSSgUWgb1gjaOsNfysagv1RXdlyPw3EyfwkFifAQvF2P
-Q0ayYZfYS640cccv7efM1MSVyFHR9PrrDsF/zr2S2sGPbeHr7R/HwLl+S5J/l9N9
-y0rk6IHAWV4dEkOvgpnuJKURwA48iu1Hhi9e4moNS6eqoK2KmY3VFpuiyWcA73nH
-GSmyaH+YuMrF7Fnuu7GEHZL/o6+F5cL3mj2SJJhL7sz0ryf5Cs5R4yN9BIEj/f49
-wh84pM6nexoI0Q4wiSFCxWiBpjSmOK6h7z6+2utaB5p20XDZHhxAlmlx4vMuWtjh
-XckgRFxc+ZpVMU3cAHUpVEoO49e/+qKEpPzp8Xg4cToKw2+AfTk3cmyyXQfGwXMQ
-ZUHNZ3w9ILMWihGCM2aGUsLcGDRennvNmnmin/SENsOQ8Ku0/a3teEzwV9cmmdYz
-5iYs1YtgPvKFobY6+T2RXXh+A5kprwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/
-MB0GA1UdDgQWBBSyUrsQVnKmA8z6/2Ech0rCvqpNmTAOBgNVHQ8BAf8EBAMCAYYw
-DQYJKoZIhvcNAQEMBQADggIBAFlj3IFmgiFz5lvTzFTRizhVofhTJsGr14Yfkuc7
-UrXPuXOwJomd4uot2d/VIeGJpfnuS84qGdmQyGewGTJ9inatHsGZgHl9NHNWRwKZ
-lTKTbBiq7aqgtUSFa06v202wpzU+1kadxJJePrbABxiXVfOmIW/a1a4hPNcT3syH
-FIEg1+CGsp71UNjBuwg3JTKWna0sLSKcxLOSOvX1fzxK5djzVpEsvQMB4PSAzXca
-vENgg2ErTwgTA+4s6rRtiBF9pAusN1QVuBahYP3ftrY6f3ycS4K65GnqscyfvKt5
-YgjtEKO3ZeeX8NpubMbzC+0Z6tVKfPFk/9TXuJtwvVeqow0YMrLLyRiYvK7EzJ97
-rrkxoKnHYQSZ+rH2tZ5SE392/rfk1PJL0cdHnkpDkUDO+8cKsFjjYKAQSNC52sKX
-74AVh6wMwxYwVZZJf2/2XxkjMWWhKNejsZhUkTISSmiLs+qPe3L67IM7GyKm9/m6
-R3r8x6NGjhTsKH64iYJg7AeKeax4b2e4hBb6GXFftyOs7unpEOIVkJJgM6gh3mwn
-R7v4gwFbLKADKt1vHuerSZMiTuNTGhSfCeDM53XI/mjZl2HeuCKP1mCDLlaO+gZR
-Q/G+E0sBKgEX4xTkAc3kgkuQGfExdGtnN2U2ehF80lBHB8+2y2E+xWWXih/ZyIcW
-wOx+
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGBDCCA+ygAwIBAgIQM4C8g5iFRucSWdC8EdqHeDANBgkqhkiG9w0BAQwFADCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGV1LWNlbnRyYWwtMSBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV
-BAcMB1NlYXR0bGUwIBcNMjEwNTIxMjIyODI2WhgPMjEyMTA1MjEyMzI4MjZaMIGa
-MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j
-LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt
-YXpvbiBSRFMgZXUtY2VudHJhbC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANeTsD/u
-6saPiY4Sg0GlJlMXMBltnrcGAEkwq34OKQ0bCXqcoNJ2rcAMmuFC5x9Ho1Y3YzB7
-NO2GpIh6bZaO76GzSv4cnimcv9n/sQSYXsGbPD+bAtnN/RvNW1avt4C0q0/ghgF1
-VFS8JihIrgPYIArAmDtGNEdl5PUrdi9y6QGggbRfidMDdxlRdZBe1C18ZdgERSEv
-UgSTPRlVczONG5qcQkUGCH83MMqL5MKQiby/Br5ZyPq6rxQMwRnQ7tROuElzyYzL
-7d6kke+PNzG1mYy4cbYdjebwANCtZ2qYRSUHAQsOgybRcSoarv2xqcjO9cEsDiRU
-l97ToadGYa4VVERuTaNZxQwrld4mvzpyKuirqZltOqg0eoy8VUsaRPL3dc5aChR0
-dSrBgRYmSAClcR2/2ZCWpXemikwgt031Dsc0A/+TmVurrsqszwbr0e5xqMow9LzO
-MI/JtLd0VFtoOkL/7GG2tN8a+7gnLFxpv+AQ0DH5n4k/BY/IyS+H1erqSJhOTQ11
-vDOFTM5YplB9hWV9fp5PRs54ILlHTlZLpWGs3I2BrJwzRtg/rOlvsosqcge9ryai
-AKm2j+JBg5wJ19R8oxRy8cfrNTftZePpISaLTyV2B16w/GsSjqixjTQe9LRN2DHk
-cC+HPqYyzW2a3pUVyTGHhW6a7YsPBs9yzt6hAgMBAAGjQjBAMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFIqA8QkOs2cSirOpCuKuOh9VDfJfMA4GA1UdDwEB/wQE
-AwIBhjANBgkqhkiG9w0BAQwFAAOCAgEAOUI90mEIsa+vNJku0iUwdBMnHiO4gm7E
-5JloP7JG0xUr7d0hypDorMM3zVDAL+aZRHsq8n934Cywj7qEp1304UF6538ByGdz
-tkfacJsUSYfdlNJE9KbA4T+U+7SNhj9jvePpVjdQbhgzxITE9f8CxY/eM40yluJJ
-PhbaWvOiRagzo74wttlcDerzLT6Y/JrVpWhnB7IY8HvzK+BwAdaCsBUPC3HF+kth
-CIqLq7J3YArTToejWZAp5OOI6DLPM1MEudyoejL02w0jq0CChmZ5i55ElEMnapRX
-7GQTARHmjgAOqa95FjbHEZzRPqZ72AtZAWKFcYFNk+grXSeWiDgPFOsq6mDg8DDB
-0kfbYwKLFFCC9YFmYzR2YrWw2NxAScccUc2chOWAoSNHiqBbHR8ofrlJSWrtmKqd
-YRCXzn8wqXnTS3NNHNccqJ6dN+iMr9NGnytw8zwwSchiev53Fpc1mGrJ7BKTWH0t
-ZrA6m32wzpMymtKozlOPYoE5mtZEzrzHEXfa44Rns7XIHxVQSXVWyBHLtIsZOrvW
-U5F41rQaFEpEeUQ7sQvqUoISfTUVRNDn6GK6YaccEhCji14APLFIvhRQUDyYMIiM
-4vll0F/xgVRHTgDVQ8b8sxdhSYlqB4Wc2Ym41YRz+X2yPqk3typEZBpc4P5Tt1/N
-89cEIGdbjsA=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIQYjbPSg4+RNRD3zNxO1fuKDANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB
-bWF6b24gUkRTIGV1LW5vcnRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUyNDIwNTkyMVoYDzIwNjEwNTI0MjE1OTIxWjCBmDEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6
-b24gUkRTIGV1LW5vcnRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA179eQHxcV0YL
-XMkqEmhSBazHhnRVd8yICbMq82PitE3BZcnv1Z5Zs/oOgNmMkOKae4tCXO/41JCX
-wAgbs/eWWi+nnCfpQ/FqbLPg0h3dqzAgeszQyNl9IzTzX4Nd7JFRBVJXPIIKzlRf
-+GmFsAhi3rYgDgO27pz3ciahVSN+CuACIRYnA0K0s9lhYdddmrW/SYeWyoB7jPa2
-LmWpAs7bDOgS4LlP2H3eFepBPgNufRytSQUVA8f58lsE5w25vNiUSnrdlvDrIU5n
-Qwzc7NIZCx4qJpRbSKWrUtbyJriWfAkGU7i0IoainHLn0eHp9bWkwb9D+C/tMk1X
-ERZw2PDGkwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBSFmR7s
-dAblusFN+xhf1ae0KUqhWTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBAHsXOpjPMyH9lDhPM61zYdja1ebcMVgfUvsDvt+w0xKMKPhBzYDMs/cFOi1N
-Q8LV79VNNfI2NuvFmGygcvTIR+4h0pqqZ+wjWl3Kk5jVxCrbHg3RBX02QLumKd/i
-kwGcEtTUvTssn3SM8bgM0/1BDXgImZPC567ciLvWDo0s/Fe9dJJC3E0G7d/4s09n
-OMdextcxFuWBZrBm/KK3QF0ByA8MG3//VXaGO9OIeeOJCpWn1G1PjT1UklYhkg61
-EbsTiZVA2DLd1BGzfU4o4M5mo68l0msse/ndR1nEY6IywwpgIFue7+rEleDh6b9d
-PYkG1rHVw2I0XDG4o17aOn5E94I=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIQC6W4HFghUkkgyQw14a6JljANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB
-bWF6b24gUkRTIGV1LXNvdXRoLTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIyMDUyMzE4MTYzMloYDzIwNjIwNTIzMTkxNjMyWjCBmDEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6
-b24gUkRTIGV1LXNvdXRoLTIgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiM/t4FV2R9Nx
-UQG203UY83jInTa/6TMq0SPyg617FqYZxvz2kkx09x3dmxepUg9ttGMlPgjsRZM5
-LCFEi1FWk+hxHzt7vAdhHES5tdjwds3aIkgNEillmRDVrUsbrDwufLaa+MMDO2E1
-wQ/JYFXw16WBCCi2g1EtyQ2Xp+tZDX5IWOTnvhZpW8vVDptZ2AcJ5rMhfOYO3OsK
-5EF0GGA5ldzuezP+BkrBYGJ4wVKGxeaq9+5AT8iVZrypjwRkD7Y5CurywK3+aBwm
-s9Q5Nd8t45JCOUzYp92rFKsCriD86n/JnEvgDfdP6Hvtm0/DkwXK40Wz2q0Zrd0k
-mjP054NRPwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRR7yqd
-SfKcX2Q8GzhcVucReIpewTAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBAEszBRDwXcZyNm07VcFwI1Im94oKwKccuKYeJEsizTBsVon8VpEiMwDs+yGu
-3p8kBhvkLwWybkD/vv6McH7T5b9jDX2DoOudqYnnaYeypsPH/00Vh3LvKagqzQza
-orWLx+0tLo8xW4BtU+Wrn3JId8LvAhxyYXTn9bm+EwPcStp8xGLwu53OPD1RXYuy
-uu+3ps/2piP7GVfou7H6PRaqbFHNfiGg6Y+WA0HGHiJzn8uLmrRJ5YRdIOOG9/xi
-qTmAZloUNM7VNuurcMM2hWF494tQpsQ6ysg2qPjbBqzlGoOt3GfBTOZmqmwmqtam
-K7juWM/mdMQAJ3SMlE5wI8nVdx4=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIICrjCCAjSgAwIBAgIRAL9SdzVPcpq7GOpvdGoM80IwCgYIKoZIzj0EAwMwgZYx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTEvMC0GA1UEAwwmQW1h
-em9uIFJEUyBldS13ZXN0LTEgUm9vdCBDQSBFQ0MzODQgRzExEDAOBgNVBAcMB1Nl
-YXR0bGUwIBcNMjEwNTIwMTY1ODA3WhgPMjEyMTA1MjAxNzU4MDdaMIGWMQswCQYD
-VQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjETMBEG
-A1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExLzAtBgNVBAMMJkFtYXpvbiBS
-RFMgZXUtd2VzdC0xIFJvb3QgQ0EgRUNDMzg0IEcxMRAwDgYDVQQHDAdTZWF0dGxl
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEJWDgXebvwjR+Ce+hxKOLbnsfN5W5dOlP
-Zn8kwWnD+SLkU81Eac/BDJsXGrMk6jFD1vg16PEkoSevsuYWlC8xR6FmT6F6pmeh
-fsMGOyJpfK4fyoEPhKeQoT23lFIc5Orjo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0G
-A1UdDgQWBBSVNAN1CHAz0eZ77qz2adeqjm31TzAOBgNVHQ8BAf8EBAMCAYYwCgYI
-KoZIzj0EAwMDaAAwZQIxAMlQeHbcjor49jqmcJ9gRLWdEWpXG8thIf6zfYQ/OEAg
-d7GDh4fR/OUk0VfjsBUN/gIwZB0bGdXvK38s6AAE/9IT051cz/wMe9GIrX1MnL1T
-1F5OqnXJdiwfZRRTHsRQ/L00
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGBDCCA+ygAwIBAgIQalr16vDfX4Rsr+gfQ4iVFDANBgkqhkiG9w0BAQwFADCB
-mjELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTMwMQYDVQQDDCpB
-bWF6b24gUkRTIGV1LWNlbnRyYWwtMiBSb290IENBIFJTQTQwOTYgRzExEDAOBgNV
-BAcMB1NlYXR0bGUwIBcNMjIwNjA2MjEyNTIzWhgPMjEyMjA2MDYyMjI1MjNaMIGa
-MQswCQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5j
-LjETMBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMzAxBgNVBAMMKkFt
-YXpvbiBSRFMgZXUtY2VudHJhbC0yIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANbHbFg7
-2VhZor1YNtez0VlNFaobS3PwOMcEn45BE3y7HONnElIIWXGQa0811M8V2FnyqnE8
-Z5aO1EuvijvWf/3D8DPZkdmAkIfh5hlZYY6Aatr65kEOckwIAm7ZZzrwFogYuaFC
-z/q0CW+8gxNK+98H/zeFx+IxiVoPPPX6UlrLvn+R6XYNERyHMLNgoZbbS5gGHk43
-KhENVv3AWCCcCc85O4rVd+DGb2vMVt6IzXdTQt6Kih28+RGph+WDwYmf+3txTYr8
-xMcCBt1+whyCPlMbC+Yn/ivtCO4LRf0MPZDRQrqTTrFf0h/V0BGEUmMGwuKgmzf5
-Kl9ILdWv6S956ioZin2WgAxhcn7+z//sN++zkqLreSf90Vgv+A7xPRqIpTdJ/nWG
-JaAOUofBfsDsk4X4SUFE7xJa1FZAiu2lqB/E+y7jnWOvFRalzxVJ2Y+D/ZfUfrnK
-4pfKtyD1C6ni1celrZrAwLrJ3PoXPSg4aJKh8+CHex477SRsGj8KP19FG8r0P5AG
-8lS1V+enFCNvT5KqEBpDZ/Y5SQAhAYFUX+zH4/n4ql0l/emS+x23kSRrF+yMkB9q
-lhC/fMk6Pi3tICBjrDQ8XAxv56hfud9w6+/ljYB2uQ1iUYtlE3JdIiuE+3ws26O8
-i7PLMD9zQmo+sVi12pLHfBHQ6RRHtdVRXbXRAgMBAAGjQjBAMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFBFot08ipEL9ZUXCG4lagmF53C0/MA4GA1UdDwEB/wQE
-AwIBhjANBgkqhkiG9w0BAQwFAAOCAgEAi2mcZi6cpaeqJ10xzMY0F3L2eOKYnlEQ
-h6QyhmNKCUF05q5u+cok5KtznzqMwy7TFOZtbVHl8uUX+xvgq/MQCxqFAnuStBXm
-gr2dg1h509ZwvTdk7TDxGdftvPCfnPNJBFbMSq4CZtNcOFBg9Rj8c3Yj+Qvwd56V
-zWs65BUkDNJrXmxdvhJZjUkMa9vi/oFN+M84xXeZTaC5YDYNZZeW9706QqDbAVES
-5ulvKLavB8waLI/lhRBK5/k0YykCMl0A8Togt8D1QsQ0eWWbIM8/HYJMPVFhJ8Wj
-vT1p/YVeDA3Bo1iKDOttgC5vILf5Rw1ZEeDxjf/r8A7VS13D3OLjBmc31zxRTs3n
-XvHKP9MieQHn9GE44tEYPjK3/yC6BDFzCBlvccYHmqGb+jvDEXEBXKzimdC9mcDl
-f4BBQWGJBH5jkbU9p6iti19L/zHhz7qU6UJWbxY40w92L9jS9Utljh4A0LCTjlnR
-NQUgjnGC6K+jkw8hj0LTC5Ip87oqoT9w7Av5EJ3VJ4hcnmNMXJJ1DkWYdnytcGpO
-DMVITQzzDZRwhbitCVPHagTN2wdi9TEuYE33J0VmFeTc6FSI50wP2aOAZ0Q1/8Aj
-bxeM5jS25eaHc2CQAuhrc/7GLnxOcPwdWQb2XWT8eHudhMnoRikVv/KSK3mf6om4
-1YfpdH2jp30=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIID/jCCAuagAwIBAgIQTDc+UgTRtYO7ZGTQ8UWKDDANBgkqhkiG9w0BAQsFADCB
-lzELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTAwLgYDVQQDDCdB
-bWF6b24gUkRTIGV1LXdlc3QtMiBSb290IENBIFJTQTIwNDggRzExEDAOBgNVBAcM
-B1NlYXR0bGUwIBcNMjEwNTIxMjI0NjI0WhgPMjA2MTA1MjEyMzQ2MjRaMIGXMQsw
-CQYDVQQGEwJVUzEiMCAGA1UECgwZQW1hem9uIFdlYiBTZXJ2aWNlcywgSW5jLjET
-MBEGA1UECwwKQW1hem9uIFJEUzELMAkGA1UECAwCV0ExMDAuBgNVBAMMJ0FtYXpv
-biBSRFMgZXUtd2VzdC0yIFJvb3QgQ0EgUlNBMjA0OCBHMTEQMA4GA1UEBwwHU2Vh
-dHRsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM1oGtthQ1YiVIC2
-i4u4swMAGxAjc/BZp0yq0eP5ZQFaxnxs7zFAPabEWsrjeDzrRhdVO0h7zskrertP
-gblGhfD20JfjvCHdP1RUhy/nzG+T+hn6Takan/GIgs8grlBMRHMgBYHW7tklhjaH
-3F7LujhceAHhhgp6IOrpb6YTaTTaJbF3GTmkqxSJ3l1LtEoWz8Al/nL/Ftzxrtez
-Vs6ebpvd7sw37sxmXBWX2OlvUrPCTmladw9OrllGXtCFw4YyLe3zozBlZ3cHzQ0q
-lINhpRcajTMfZrsiGCkQtoJT+AqVJPS2sHjqsEH8yiySW9Jbq4zyMbM1yqQ2vnnx
-MJgoYMcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUaQG88UnV
-JPTI+Pcti1P+q3H7pGYwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4IB
-AQBAkgr75V0sEJimC6QRiTVWEuj2Khy7unjSfudbM6zumhXEU2/sUaVLiYy6cA/x
-3v0laDle6T07x9g64j5YastE/4jbzrGgIINFlY0JnaYmR3KZEjgi1s1fkRRf3llL
-PJm9u4Q1mbwAMQK/ZjLuuRcL3uRIHJek18nRqT5h43GB26qXyvJqeYYpYfIjL9+/
-YiZAbSRRZG+Li23cmPWrbA1CJY121SB+WybCbysbOXzhD3Sl2KSZRwSw4p2HrFtV
-1Prk0dOBtZxCG9luf87ultuDZpfS0w6oNBAMXocgswk24ylcADkkFxBWW+7BETn1
-EpK+t1Lm37mU4sxtuha00XAi
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIEADCCAuigAwIBAgIQcY44/8NUvBwr6LlHfRy7KjANBgkqhkiG9w0BAQsFADCB
-mDELMAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIElu
-Yy4xEzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChB
-bWF6b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQH
-DAdTZWF0dGxlMCAXDTIxMDUxOTE4MjcxOFoYDzIwNjEwNTE5MTkyNzE4WjCBmDEL
-MAkGA1UEBhMCVVMxIjAgBgNVBAoMGUFtYXpvbiBXZWIgU2VydmljZXMsIEluYy4x
-EzARBgNVBAsMCkFtYXpvbiBSRFMxCzAJBgNVBAgMAldBMTEwLwYDVQQDDChBbWF6
-b24gUkRTIGV1LXNvdXRoLTEgUm9vdCBDQSBSU0EyMDQ4IEcxMRAwDgYDVQQHDAdT
-ZWF0dGxlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0UaBeC+Usalu
-EtXnV7+PnH+gi7/71tI/jkKVGKuhD2JDVvqLVoqbMHRh3+wGMvqKCjbHPcC2XMWv
-566fpAj4UZ9CLB5fVzss+QVNTl+FH2XhEzigopp+872ajsNzcZxrMkifxGb4i0U+
-t0Zi+UrbL5tsfP2JonKR1crOrbS6/DlzHBjIiJazGOQcMsJjNuTOItLbMohLpraA
-/nApa3kOvI7Ufool1/34MG0+wL3UUA4YkZ6oBJVxjZvvs6tI7Lzz/SnhK2widGdc
-snbLqBpHNIZQSorVoiwcFaRBGYX/uzYkiw44Yfa4cK2V/B5zgu1Fbr0gbI2am4eh
-yVYyg4jPawIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBS9gM1m
-IIjyh9O5H/7Vj0R/akI7UzAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD
-ggEBAF0Sm9HC2AUyedBVnwgkVXMibnYChOzz7T+0Y+fOLXYAEXex2s8oqGeZdGYX
-JHkjBn7JXu7LM+TpTbPbFFDoc1sgMguD/ls+8XsqAl1CssW+amryIL+jfcfbgQ+P
-ICwEUD9hGdjBgJ5WcuS+qqxHsEIlFNci3HxcxfBa9VsWs5TjI7Vsl4meL5lf7ZyL
-wDV7dHRuU+cImqG1MIvPRIlvPnT7EghrCYi2VCPhP2pM/UvShuwVnkz4MJ29ebIk
-WR9kpblFxFdE92D5UUvMCjC2kmtgzNiErvTcwIvOO9YCbBHzRB1fFiWrXUHhJWq9
-IkaxR5icb/IpAV0A1lYZEWMVsfQ=
------END CERTIFICATE-----
------BEGIN CERTIFICATE-----
-MIIGATCCA+mgAwIBAgIRAMa0TPL+QgbWfUPpYXQkf8wwDQYJKoZIhvcNAQEMBQAw
-gZgxCzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJ
-bmMuMRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwo
-QW1hem9uIFJEUyBldS1ub3J0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UE
-BwwHU2VhdHRsZTAgFw0yMTA1MjQyMTAzMjBaGA8yMTIxMDUyNDIyMDMyMFowgZgx
-CzAJBgNVBAYTAlVTMSIwIAYDVQQKDBlBbWF6b24gV2ViIFNlcnZpY2VzLCBJbmMu
-MRMwEQYDVQQLDApBbWF6b24gUkRTMQswCQYDVQQIDAJXQTExMC8GA1UEAwwoQW1h
-em9uIFJEUyBldS1ub3J0aC0xIFJvb3QgQ0EgUlNBNDA5NiBHMTEQMA4GA1UEBwwH
-U2VhdHRsZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANhS9LJVJyWp
-6Rudy9t47y6kzvgnFYDrvJVtgEK0vFn5ifdlHE7xqMz4LZqWBFTnS+3oidwVRqo7
-tqsuuElsouStO8m315/YUzKZEPmkw8h5ufWt/lg3NTCoUZNkB4p4skr7TspyMUwE
-VdlKQuWTCOLtofwmWT+BnFF3To6xTh3XPlT3ssancw27Gob8kJegD7E0TSMVsecP
-B8je65+3b8CGwcD3QB3kCTGLy87tXuS2+07pncHvjMRMBdDQQQqhXWsRSeUNg0IP
-xdHTWcuwMldYPWK5zus9M4dCNBDlmZjKdcZZVUOKeBBAm7Uo7CbJCk8r/Fvfr6mw
-nXXDtuWhqn/WhJiI/y0QU27M+Hy5CQMxBwFsfAjJkByBpdXmyYxUgTmMpLf43p7H
-oWfH1xN0cT0OQEVmAQjMakauow4AQLNkilV+X6uAAu3STQVFRSrpvMen9Xx3EPC3
-G9flHueTa71bU65Xe8ZmEmFhGeFYHY0GrNPAFhq9RThPRY0IPyCZe0Th8uGejkek
-jQjm0FHPOqs5jc8CD8eJs4jSEFt9lasFLVDcAhx0FkacLKQjGHvKAnnbRwhN/dF3
-xt4oL8Z4JGPCLau056gKnYaEyviN7PgO+IFIVOVIdKEBu2ASGE8/+QJB5bcHefNj
-04hEkDW0UYJbSfPpVbGAR0gFI/QpycKnAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB
-Af8wHQYDVR0OBBYEFFMXvvjoaGGUcul8GA3FT05DLbZcMA4GA1UdDwEB/wQEAwIB
-hjANBgkqhkiG9w0BAQwFAAOCAgEAQLwFhd2JKn4K/6salLyIA4mP58qbA/9BTB/r
-D9l0bEwDlVPSdY7R3gZCe6v7SWLfA9RjE5tdWDrQMi5IU6W2OVrVsZS/yGJfwnwe
-a/9iUAYprA5QYKDg37h12XhVsDKlYCekHdC+qa5WwB1SL3YUprDLPWeaIQdg+Uh2
-+LxvpZGoxoEbca0fc7flwq9ke/3sXt/3V4wJDyY6AL2YNdjFzC+FtYjHHx8rYxHs
-aesP7yunuN17KcfOZBBnSFRrx96k+Xm95VReTEEpwiBqAECqEpMbd+R0mFAayMb1
-cE77GaK5yeC2f67NLYGpkpIoPbO9p9rzoXLE5GpSizMjimnz6QCbXPFAFBDfSzim
-u6azp40kEUO6kWd7rBhqRwLc43D3TtNWQYxMve5mTRG4Od+eMKwYZmQz89BQCeqm
-aZiJP9y9uwJw4p/A5V3lYHTDQqzmbOyhGUk6OdpdE8HXs/1ep1xTT20QDYOx3Ekt
-r4mmNYfH/8v9nHNRlYJOqFhmoh1i85IUl5IHhg6OT5ZTTwsGTSxvgQQXrmmHVrgZ
-rZIqyBKllCgVeB9sMEsntn4bGLig7CS/N1y2mYdW/745yCLZv2gj0NXhPqgEIdVV
-f9DhFD4ohE1C63XP0kOQee+LYg/MY5vH8swpCSWxQgX5icv5jVDz8YTdCKgUc5u8
-rM2p0kk=
------END CERTIFICATE-----
diff --git a/redash/query_runner/google_analytics.py b/redash/query_runner/google_analytics.py
index dede7340e7..fbd6763506 100644
--- a/redash/query_runner/google_analytics.py
+++ b/redash/query_runner/google_analytics.py
@@ -3,27 +3,19 @@
from datetime import datetime
from urllib.parse import parse_qs, urlparse
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
-)
-from redash.utils import json_loads
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
try:
- import google.auth
+ from oauth2client.service_account import ServiceAccountCredentials
from apiclient.discovery import build
from apiclient.errors import HttpError
- from google.oauth2.service_account import Credentials
+ import httplib2
enabled = True
-except ImportError:
+except ImportError as e:
enabled = False
@@ -56,7 +48,9 @@ def parse_ga_response(response):
d = {}
for c, value in enumerate(r):
column_name = response["columnHeaders"][c]["name"]
- column_type = [col for col in columns if col["name"] == column_name][0]["type"]
+ column_type = [col for col in columns if col["name"] == column_name][0][
+ "type"
+ ]
# mcf results come a bit different than ga results:
if isinstance(value, dict):
@@ -65,7 +59,9 @@ def parse_ga_response(response):
elif "conversionPathValue" in value:
steps = []
for step in value["conversionPathValue"]:
- steps.append("{}:{}".format(step["interactionType"], step["nodeValue"]))
+ steps.append(
+ "{}:{}".format(step["interactionType"], step["nodeValue"])
+ )
value = ", ".join(steps)
else:
raise Exception("Results format not supported")
@@ -78,7 +74,9 @@ def parse_ga_response(response):
elif len(value) == 12:
value = datetime.strptime(value, "%Y%m%d%H%M")
else:
- raise Exception("Unknown date/time format in results: '{}'".format(value))
+ raise Exception(
+ "Unknown date/time format in results: '{}'".format(value)
+ )
d[column_name] = value
rows.append(d)
@@ -105,8 +103,16 @@ def enabled(cls):
def configuration_schema(cls):
return {
"type": "object",
- "properties": {"jsonKeyFile": {"type": "string", "title": "JSON Key File (ADC is used if omitted)"}},
- "required": [],
+ "properties": {
+ "jsonKeyFile": {"type": "string", "title": "JSON Key File"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ },
+ "required": ["jsonKeyFile"],
"secret": ["jsonKeyFile"],
}
@@ -115,18 +121,20 @@ def __init__(self, configuration):
self.syntax = "json"
def _get_analytics_service(self):
- scopes = ["https://www.googleapis.com/auth/analytics.readonly"]
-
- try:
- key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
- creds = Credentials.from_service_account_info(key, scopes=scopes)
- except KeyError:
- creds = google.auth.default(scopes=scopes)[0]
-
- return build("analytics", "v3", credentials=creds)
+ scope = ["https://www.googleapis.com/auth/analytics.readonly"]
+ key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
+ creds = ServiceAccountCredentials.from_json_keyfile_dict(key, scope)
+ return build("analytics", "v3", http=creds.authorize(httplib2.Http()))
def _get_tables(self, schema):
- accounts = self._get_analytics_service().management().accounts().list().execute().get("items")
+ accounts = (
+ self._get_analytics_service()
+ .management()
+ .accounts()
+ .list()
+ .execute()
+ .get("items")
+ )
if accounts is None:
raise Exception("Failed getting accounts.")
else:
@@ -143,7 +151,9 @@ def _get_tables(self, schema):
for property_ in properties:
if "defaultProfileId" in property_ and "name" in property_:
schema[account["name"]]["columns"].append(
- "{0} (ga:{1})".format(property_["name"], property_["defaultProfileId"])
+ "{0} (ga:{1})".format(
+ property_["name"], property_["defaultProfileId"]
+ )
)
return list(schema.values())
@@ -160,14 +170,16 @@ def run_query(self, query, user):
logger.debug("Analytics is about to execute query: %s", query)
try:
params = json_loads(query)
- except Exception:
- query_string = parse_qs(urlparse(query).query, keep_blank_values=True)
- params = {k.replace("-", "_"): ",".join(v) for k, v in query_string.items()}
+ except:
+ query_string = parse_qs(urlparse(query).query, keep_blank_values=True)
+ params = {k.replace('-', '_'): ",".join(v) for k,v in query_string.items()}
if "mcf:" in params["metrics"] and "ga:" in params["metrics"]:
raise Exception("Can't mix mcf: and ga: metrics.")
- if "mcf:" in params.get("dimensions", "") and "ga:" in params.get("dimensions", ""):
+ if "mcf:" in params.get("dimensions", "") and "ga:" in params.get(
+ "dimensions", ""
+ ):
raise Exception("Can't mix mcf: and ga: dimensions.")
if "mcf:" in params["metrics"]:
@@ -180,14 +192,15 @@ def run_query(self, query, user):
response = api.get(**params).execute()
data = parse_ga_response(response)
error = None
+ json_data = json_dumps(data)
except HttpError as e:
# Make sure we return a more readable error to the end user
error = e._get_reason()
- data = None
+ json_data = None
else:
error = "Wrong query format."
- data = None
- return data, error
+ json_data = None
+ return json_data, error
register(GoogleAnalytics)
diff --git a/redash/query_runner/google_analytics4.py b/redash/query_runner/google_analytics4.py
deleted file mode 100644
index 302e5ae909..0000000000
--- a/redash/query_runner/google_analytics4.py
+++ /dev/null
@@ -1,181 +0,0 @@
-import datetime
-import logging
-from base64 import b64decode
-
-import requests
-
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-from redash.utils import json_loads
-
-logger = logging.getLogger(__name__)
-
-try:
- import google.auth
- import google.auth.transport.requests
- from google.oauth2.service_account import Credentials
-
- enabled = True
-except ImportError:
- enabled = False
-
-types_conv = dict(
- STRING=TYPE_STRING,
- INTEGER=TYPE_INTEGER,
- FLOAT=TYPE_FLOAT,
- DATE=TYPE_DATE,
- DATETIME=TYPE_DATETIME,
-)
-
-ga_report_endpoint = "https://analyticsdata.googleapis.com/v1beta/properties/{propertyId}:runReport"
-ga_metadata_endpoint = "https://analyticsdata.googleapis.com/v1beta/properties/{propertyId}/metadata"
-
-
-def format_column_value(column_name, value, columns):
- column_type = [col for col in columns if col["name"] == column_name][0]["type"]
-
- if column_type == TYPE_DATE:
- value = datetime.datetime.strptime(value, "%Y%m%d")
- elif column_type == TYPE_DATETIME:
- if len(value) == 10:
- value = datetime.datetime.strptime(value, "%Y%m%d%H")
- elif len(value) == 12:
- value = datetime.datetime.strptime(value, "%Y%m%d%H%M")
- else:
- raise Exception("Unknown date/time format in results: '{}'".format(value))
-
- return value
-
-
-def get_formatted_column_json(column_name):
- data_type = None
-
- if column_name == "date":
- data_type = "DATE"
- elif column_name == "dateHour":
- data_type = "DATETIME"
-
- result = {
- "name": column_name,
- "friendly_name": column_name,
- "type": types_conv.get(data_type, "string"),
- }
-
- return result
-
-
-def parse_ga_response(response):
- columns = []
-
- for dim_header in response["dimensionHeaders"]:
- columns.append(get_formatted_column_json(dim_header["name"]))
-
- for met_header in response["metricHeaders"]:
- columns.append(get_formatted_column_json(met_header["name"]))
-
- rows = []
- for r in response["rows"]:
- counter = 0
- d = {}
- for item in r["dimensionValues"]:
- column_name = columns[counter]["name"]
- value = item["value"]
-
- d[column_name] = format_column_value(column_name, value, columns)
- counter = counter + 1
-
- for item in r["metricValues"]:
- column_name = columns[counter]["name"]
- value = item["value"]
-
- d[column_name] = format_column_value(column_name, value, columns)
- counter = counter + 1
-
- rows.append(d)
-
- return {"columns": columns, "rows": rows}
-
-
-class GoogleAnalytics4(BaseQueryRunner):
- should_annotate_query = False
-
- @classmethod
- def type(cls):
- return "google_analytics4"
-
- @classmethod
- def name(cls):
- return "Google Analytics 4"
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "propertyId": {"type": "number", "title": "Property Id"},
- "jsonKeyFile": {"type": "string", "title": "JSON Key File (ADC is used if omitted)"},
- },
- "required": ["propertyId"],
- "secret": ["jsonKeyFile"],
- }
-
- def _get_access_token(self):
- scopes = ["https://www.googleapis.com/auth/analytics.readonly"]
-
- try:
- key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
- creds = Credentials.from_service_account_info(key, scopes=scopes)
- except KeyError:
- creds = google.auth.default(scopes=scopes)[0]
-
- creds.refresh(google.auth.transport.requests.Request())
-
- return creds.token
-
- def run_query(self, query, user):
- access_token = self._get_access_token()
- params = json_loads(query)
-
- property_id = self.configuration["propertyId"]
-
- headers = {"Content-Type": "application/json", "Authorization": f"Bearer {access_token}"}
-
- url = ga_report_endpoint.replace("{propertyId}", str(property_id))
- r = requests.post(url, json=params, headers=headers)
- r.raise_for_status()
-
- raw_result = r.json()
-
- data = parse_ga_response(raw_result)
-
- error = None
-
- return data, error
-
- def test_connection(self):
- try:
- access_token = self._get_access_token()
- property_id = self.configuration["propertyId"]
-
- url = ga_metadata_endpoint.replace("{propertyId}", str(property_id))
-
- headers = {"Content-Type": "application/json", "Authorization": f"Bearer {access_token}"}
-
- r = requests.get(url, headers=headers)
- r.raise_for_status()
- except Exception as e:
- raise Exception(e)
-
-
-register(GoogleAnalytics4)
diff --git a/redash/query_runner/google_search_console.py b/redash/query_runner/google_search_console.py
deleted file mode 100644
index e0106a023a..0000000000
--- a/redash/query_runner/google_search_console.py
+++ /dev/null
@@ -1,164 +0,0 @@
-import logging
-from base64 import b64decode
-from datetime import datetime
-
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
-)
-from redash.utils import json_loads
-
-logger = logging.getLogger(__name__)
-
-try:
- import google.auth
- from apiclient.discovery import build
- from apiclient.errors import HttpError
- from google.oauth2.service_account import Credentials
-
- enabled = True
-except ImportError:
- enabled = False
-
-
-types_conv = dict(
- STRING=TYPE_STRING,
- INTEGER=TYPE_INTEGER,
- FLOAT=TYPE_FLOAT,
- DATE=TYPE_DATE,
- DATETIME=TYPE_DATETIME,
-)
-
-
-def parse_ga_response(response, dimensions):
- columns = []
-
- for item in dimensions:
- if item == "date":
- data_type = "date"
- else:
- data_type = "string"
- columns.append(
- {
- "name": item,
- "friendly_name": item,
- "type": data_type,
- }
- )
-
- default_items = ["clicks", "impressions", "ctr", "position"]
- for item in default_items:
- columns.append({"name": item, "friendly_name": item, "type": "number"})
-
- rows = []
- for r in response.get("rows", []):
- d = {}
- for k, value in r.items():
- if k == "keys":
- for index, val in enumerate(value):
- column_name = columns[index]["name"]
- column_type = columns[index]["type"]
- val = get_formatted_value(column_type, val)
- d[column_name] = val
- else:
- column_name = k
- column_type = [col for col in columns if col["name"] == column_name][0]["type"]
- value = get_formatted_value(column_type, value)
- d[column_name] = value
- rows.append(d)
-
- return {"columns": columns, "rows": rows}
-
-
-def get_formatted_value(column_type, value):
- if column_type == "number":
- value = round(value, 2)
- elif column_type == TYPE_DATE:
- value = datetime.strptime(value, "%Y-%m-%d")
- elif column_type == TYPE_DATETIME:
- if len(value) == 10:
- value = datetime.strptime(value, "%Y%m%d%H")
- elif len(value) == 12:
- value = datetime.strptime(value, "%Y%m%d%H%M")
- else:
- raise Exception("Unknown date/time format in results: '{}'".format(value))
- return value
-
-
-class GoogleSearchConsole(BaseSQLQueryRunner):
- should_annotate_query = False
-
- @classmethod
- def type(cls):
- return "google_search_console"
-
- @classmethod
- def name(cls):
- return "Google Search Console"
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "siteURL": {"type": "string", "title": "Site URL"},
- "jsonKeyFile": {"type": "string", "title": "JSON Key File (ADC is used if omitted)"},
- },
- "required": [],
- "secret": ["jsonKeyFile"],
- }
-
- def __init__(self, configuration):
- super(GoogleSearchConsole, self).__init__(configuration)
- self.syntax = "json"
-
- def _get_search_service(self):
- scopes = ["https://www.googleapis.com/auth/webmasters.readonly"]
-
- try:
- key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
- creds = Credentials.from_service_account_info(key, scopes=scopes)
- except KeyError:
- creds = google.auth.default(scopes=scopes)[0]
-
- return build("searchconsole", "v1", credentials=creds)
-
- def test_connection(self):
- try:
- service = self._get_search_service()
- service.sites().list().execute()
- except HttpError as e:
- # Make sure we return a more readable error to the end user
- raise Exception(e._get_reason())
-
- def run_query(self, query, user):
- logger.debug("Search Analytics is about to execute query: %s", query)
- params = json_loads(query)
- site_url = self.configuration["siteURL"]
- api = self._get_search_service()
-
- if len(params) > 0:
- try:
- response = api.searchanalytics().query(siteUrl=site_url, body=params).execute()
- data = parse_ga_response(response, params["dimensions"])
- error = None
- except HttpError as e:
- # Make sure we return a more readable error to the end user
- error = e._get_reason()
- data = None
- else:
- error = "Wrong query format."
- data = None
- return data, error
-
-
-register(GoogleSearchConsole)
diff --git a/redash/query_runner/google_spreadsheets.py b/redash/query_runner/google_spreadsheets.py
index 6ea9757c4e..e747e7075b 100644
--- a/redash/query_runner/google_spreadsheets.py
+++ b/redash/query_runner/google_spreadsheets.py
@@ -1,32 +1,19 @@
import logging
-import re
from base64 import b64decode
from dateutil import parser
from requests import Session
from xlsxwriter.utility import xl_col_to_name
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- guess_type,
- register,
-)
-from redash.utils import json_loads
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
try:
- import google.auth
import gspread
- from google.auth.exceptions import GoogleAuthError
- from google.oauth2.service_account import Credentials
from gspread.exceptions import APIError
- from gspread.exceptions import WorksheetNotFound as GSWorksheetNotFound
+ from oauth2client.service_account import ServiceAccountCredentials
enabled = True
except ImportError:
@@ -52,7 +39,9 @@ def _get_columns_and_column_names(row):
duplicate_counter += 1
column_names.append(column_name)
- columns.append({"name": column_name, "friendly_name": column_name, "type": TYPE_STRING})
+ columns.append(
+ {"name": column_name, "friendly_name": column_name, "type": TYPE_STRING}
+ )
return columns, column_names
@@ -92,27 +81,14 @@ def __init__(self, worksheet_num, worksheet_count):
super(WorksheetNotFoundError, self).__init__(message)
-class WorksheetNotFoundByTitleError(Exception):
- def __init__(self, worksheet_title):
- message = "Worksheet title '{}' not found.".format(worksheet_title)
- super(WorksheetNotFoundByTitleError, self).__init__(message)
-
-
def parse_query(query):
values = query.split("|")
key = values[0] # key of the spreadsheet
- worksheet_num_or_title = 0 # A default value for when a number of inputs is invalid
- if len(values) == 2:
- s = values[1].strip()
- if len(s) > 0:
- if re.match(r"^\"(.*?)\"$", s):
- # A string quoted by " means a title of worksheet
- worksheet_num_or_title = s[1:-1]
- else:
- # if spreadsheet contains more than one worksheet - this is the number of it
- worksheet_num_or_title = int(s)
+ worksheet_num = (
+ 0 if len(values) != 2 else int(values[1])
+ ) # if spreadsheet contains more than one worksheet - this is the number of it
- return key, worksheet_num_or_title
+ return key, worksheet_num
def parse_worksheet(worksheet):
@@ -126,27 +102,24 @@ def parse_worksheet(worksheet):
columns[j]["type"] = guess_type(value)
column_types = [c["type"] for c in columns]
- rows = [dict(zip(column_names, _value_eval_list(row, column_types))) for row in worksheet[HEADER_INDEX + 1 :]]
+ rows = [
+ dict(zip(column_names, _value_eval_list(row, column_types)))
+ for row in worksheet[HEADER_INDEX + 1 :]
+ ]
data = {"columns": columns, "rows": rows}
return data
-def parse_spreadsheet(spreadsheet, worksheet_num_or_title):
- worksheet = None
- if isinstance(worksheet_num_or_title, int):
- worksheet = spreadsheet.get_worksheet_by_index(worksheet_num_or_title)
- if worksheet is None:
- worksheet_count = len(spreadsheet.worksheets())
- raise WorksheetNotFoundError(worksheet_num_or_title, worksheet_count)
- elif isinstance(worksheet_num_or_title, str):
- worksheet = spreadsheet.get_worksheet_by_title(worksheet_num_or_title)
- if worksheet is None:
- raise WorksheetNotFoundByTitleError(worksheet_num_or_title)
+def parse_spreadsheet(spreadsheet, worksheet_num):
+ worksheets = spreadsheet.worksheets()
+ worksheet_count = len(worksheets)
+ if worksheet_num >= worksheet_count:
+ raise WorksheetNotFoundError(worksheet_num, worksheet_count)
- worksheet_values = worksheet.get_all_values()
+ worksheet = worksheets[worksheet_num].get_all_values()
- return parse_worksheet(worksheet_values)
+ return parse_worksheet(worksheet)
def is_url_key(key):
@@ -164,23 +137,6 @@ def parse_api_error(error):
return message
-class SpreadsheetWrapper:
- def __init__(self, spreadsheet):
- self.spreadsheet = spreadsheet
-
- def worksheets(self):
- return self.spreadsheet.worksheets()
-
- def get_worksheet_by_index(self, index):
- return self.spreadsheet.get_worksheet(index)
-
- def get_worksheet_by_title(self, title):
- try:
- return self.spreadsheet.worksheet(title)
- except GSWorksheetNotFound:
- return None
-
-
class TimeoutSession(Session):
def request(self, *args, **kwargs):
kwargs.setdefault("timeout", 300)
@@ -210,19 +166,24 @@ def enabled(cls):
def configuration_schema(cls):
return {
"type": "object",
- "properties": {"jsonKeyFile": {"type": "string", "title": "JSON Key File (ADC is used if omitted)"}},
- "required": [],
+ "properties": {
+ "jsonKeyFile": {"type": "string", "title": "JSON Key File"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ },
+ "required": ["jsonKeyFile"],
"secret": ["jsonKeyFile"],
}
def _get_spreadsheet_service(self):
- scopes = ["https://spreadsheets.google.com/feeds"]
+ scope = ["https://spreadsheets.google.com/feeds"]
- try:
- key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
- creds = Credentials.from_service_account_info(key, scopes=scopes)
- except KeyError:
- creds = google.auth.default(scopes=scopes)[0]
+ key = json_loads(b64decode(self.configuration["jsonKeyFile"]))
+ creds = ServiceAccountCredentials.from_json_keyfile_dict(key, scope)
timeout_session = Session()
timeout_session.requests_session = TimeoutSession()
@@ -231,21 +192,17 @@ def _get_spreadsheet_service(self):
return spreadsheetservice
def test_connection(self):
+ service = self._get_spreadsheet_service()
test_spreadsheet_key = "1S0mld7LMbUad8LYlo13Os9f7eNjw57MqVC0YiCd1Jis"
try:
- service = self._get_spreadsheet_service()
service.open_by_key(test_spreadsheet_key).worksheets()
except APIError as e:
- logger.exception(e)
message = parse_api_error(e)
raise Exception(message)
- except GoogleAuthError as e:
- logger.exception(e)
- raise Exception(str(e))
def run_query(self, query, user):
logger.debug("Spreadsheet is about to execute query: %s", query)
- key, worksheet_num_or_title = parse_query(query)
+ key, worksheet_num = parse_query(query)
try:
spreadsheet_service = self._get_spreadsheet_service()
@@ -255,13 +212,15 @@ def run_query(self, query, user):
else:
spreadsheet = spreadsheet_service.open_by_key(key)
- data = parse_spreadsheet(SpreadsheetWrapper(spreadsheet), worksheet_num_or_title)
+ data = parse_spreadsheet(spreadsheet, worksheet_num)
- return data, None
+ return json_dumps(data), None
except gspread.SpreadsheetNotFound:
return (
None,
- "Spreadsheet ({}) not found. Make sure you used correct id.".format(key),
+ "Spreadsheet ({}) not found. Make sure you used correct id.".format(
+ key
+ ),
)
except APIError as e:
return None, parse_api_error(e)
diff --git a/redash/query_runner/graphite.py b/redash/query_runner/graphite.py
index 06bdbc61db..d7285b8637 100644
--- a/redash/query_runner/graphite.py
+++ b/redash/query_runner/graphite.py
@@ -3,13 +3,8 @@
import requests
-from redash.query_runner import (
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -34,7 +29,8 @@ def _transform_result(response):
}
)
- return {"columns": columns, "rows": rows}
+ data = {"columns": columns, "rows": rows}
+ return json_dumps(data)
class Graphite(BaseQueryRunner):
@@ -49,6 +45,12 @@ def configuration_schema(cls):
"username": {"type": "string"},
"password": {"type": "string"},
"verify": {"type": "boolean", "title": "Verify SSL certificate"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["url"],
"secret": ["password"],
@@ -73,7 +75,11 @@ def test_connection(self):
verify=self.verify,
)
if r.status_code != 200:
- raise Exception("Got invalid response from Graphite (http status code: {0}).".format(r.status_code))
+ raise Exception(
+ "Got invalid response from Graphite (http status code: {0}).".format(
+ r.status_code
+ )
+ )
def run_query(self, query, user):
url = "%s%s" % (self.base_url, "&".join(query.split("\n")))
diff --git a/redash/query_runner/hive_ds.py b/redash/query_runner/hive_ds.py
index cb44a9457e..2fa2389759 100644
--- a/redash/query_runner/hive_ds.py
+++ b/redash/query_runner/hive_ds.py
@@ -1,17 +1,9 @@
-import base64
import logging
+import sys
+import base64
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -58,6 +50,12 @@ def configuration_schema(cls):
"port": {"type": "number"},
"database": {"type": "string"},
"username": {"type": "string"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"order": ["host", "port", "database", "username"],
"required": ["host"],
@@ -79,17 +77,27 @@ def _get_tables(self, schema):
columns_query = "show columns in %s.%s"
for schema_name in [
- a for a in [str(a["database_name"]) for a in self._run_query_internal(schemas_query)] if len(a) > 0
+ a
+ for a in [
+ str(a["database_name"]) for a in self._run_query_internal(schemas_query)
+ ]
+ if len(a) > 0
]:
for table_name in [
a
- for a in [str(a["tab_name"]) for a in self._run_query_internal(tables_query % schema_name)]
+ for a in [
+ str(a["tab_name"])
+ for a in self._run_query_internal(tables_query % schema_name)
+ ]
if len(a) > 0
]:
columns = [
a
for a in [
- str(a["field"]) for a in self._run_query_internal(columns_query % (schema_name, table_name))
+ str(a["field"])
+ for a in self._run_query_internal(
+ columns_query % (schema_name, table_name)
+ )
]
if len(a) > 0
]
@@ -109,7 +117,6 @@ def _get_connection(self):
database=self.configuration.get("database", "default"),
username=self.configuration.get("username", None),
)
-
return connection
def run_query(self, query, user):
@@ -138,6 +145,7 @@ def run_query(self, query, user):
rows = [dict(zip(column_names, row)) for row in cursor]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
except (KeyboardInterrupt, JobTimeoutException):
if connection:
@@ -148,12 +156,12 @@ def run_query(self, query, user):
error = e.args[0].status.errorMessage
except AttributeError:
error = str(e)
- data = None
+ json_data = None
finally:
if connection:
connection.close()
- return data, error
+ return json_data, error
class HiveHttp(Hive):
diff --git a/redash/query_runner/ignite.py b/redash/query_runner/ignite.py
deleted file mode 100644
index bba24fb064..0000000000
--- a/redash/query_runner/ignite.py
+++ /dev/null
@@ -1,174 +0,0 @@
-import datetime
-import importlib.util
-import logging
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
-
-ignite_available = importlib.util.find_spec("pyignite") is not None
-gridgain_available = importlib.util.find_spec("pygridgain") is not None
-
-
-logger = logging.getLogger(__name__)
-
-types_map = {
- "java.lang.String": TYPE_STRING,
- "java.lang.Float": TYPE_FLOAT,
- "java.lang.Double": TYPE_FLOAT,
- "java.sql.Date": TYPE_DATETIME,
- "java.sql.Timestamp": TYPE_DATETIME,
- "java.lang.Long": TYPE_INTEGER,
- "java.lang.Integer": TYPE_INTEGER,
- "java.lang.Short": TYPE_INTEGER,
- "java.lang.Boolean": TYPE_BOOLEAN,
- "java.lang.Decimal": TYPE_FLOAT,
-}
-
-
-class Ignite(BaseSQLQueryRunner):
- should_annotate_query = False
- noop_query = "SELECT 1"
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "user": {"type": "string"},
- "password": {"type": "string"},
- "server": {"type": "string", "default": "127.0.0.1:10800"},
- "tls": {"type": "boolean", "default": False, "title": "Use SSL/TLS connection"},
- "schema": {"type": "string", "title": "Schema Name", "default": "PUBLIC"},
- "distributed_joins": {"type": "boolean", "title": "Allow distributed joins", "default": False},
- "enforce_join_order": {"type": "boolean", "title": "Enforce join order", "default": False},
- "lazy": {"type": "boolean", "title": "Lazy query execution", "default": True},
- "gridgain": {"type": "boolean", "title": "Use GridGain libraries", "default": gridgain_available},
- },
- "required": ["server"],
- "secret": ["password"],
- }
-
- @classmethod
- def name(cls):
- return "Apache Ignite"
-
- @classmethod
- def type(cls):
- return "ignite"
-
- @classmethod
- def enabled(cls):
- return ignite_available or gridgain_available
-
- def _get_tables(self, schema):
- query = """
- SELECT schema_name, table_name, column_name, type
- FROM SYS.TABLE_COLUMNS
- WHERE schema_name NOT IN ('SYS') and column_name not in ('_KEY','_VAL');
- """
-
- results, error = self.run_query(query, None)
-
- if error is not None:
- raise Exception("Failed getting schema.")
-
- for row in results["rows"]:
- if row["SCHEMA_NAME"] != self.configuration.get("schema", "PUBLIC"):
- table_name = "{}.{}".format(row["SCHEMA_NAME"], row["TABLE_NAME"])
- else:
- table_name = row["TABLE_NAME"]
-
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
- col_type = TYPE_STRING
- if row["TYPE"] in types_map:
- col_type = types_map[row["TYPE"]]
-
- schema[table_name]["columns"].append({"name": row["COLUMN_NAME"], "type": col_type})
-
- return list(schema.values())
-
- def normalise_column(self, col):
- # if it's a datetime, just return the milliseconds
- if type(col) is tuple and len(col) == 2 and type(col[0]) is datetime.datetime and isinstance(col[1], int):
- return col[0]
- else:
- return col
-
- def normalise_row(self, row):
- return [self.normalise_column(col) for col in row]
-
- def server_to_connection(self, s):
- st = s.split(":")
- if len(st) == 1:
- server = s
- port = 10800
- elif len(st) == 2:
- server = st[0]
- port = int(st[1])
- else:
- server = "unknown"
- port = 10800
- return (server, port)
-
- def _parse_results(self, c):
- column_names = next(c)
- columns = [{"name": col, "friendly_name": col.lower()} for col in column_names]
- rows = [dict(zip(column_names, self.normalise_row(row))) for row in c]
-
- return (columns, rows)
-
- def run_query(self, query, user):
- connection = None
-
- try:
- server = self.configuration.get("server", "127.0.0.1:10800")
- user = self.configuration.get("user", None)
- password = self.configuration.get("password", None)
- tls = self.configuration.get("tls", False)
- distributed_joins = self.configuration.get("distributed_joins", False)
- enforce_join_order = self.configuration.get("enforce_join_order", False)
- lazy = self.configuration.get("lazy", True)
- gridgain = self.configuration.get("gridgain", False)
-
- if gridgain:
- from pygridgain import Client
- else:
- from pyignite import Client
-
- connection = Client(username=user, password=password, use_ssl=tls)
- connection.connect([self.server_to_connection(s) for s in server.split(",")])
-
- cursor = connection.sql(
- query,
- include_field_names=True,
- distributed_joins=distributed_joins,
- enforce_join_order=enforce_join_order,
- lazy=lazy,
- )
- logger.debug("Ignite running query: %s", query)
-
- result = self._parse_results(cursor)
- data = {"columns": result[0], "rows": result[1]}
- error = None
-
- except (KeyboardInterrupt, JobTimeoutException):
- connection.cancel()
- raise
- finally:
- if connection:
- connection.close()
-
- return data, error
-
-
-register(Ignite)
diff --git a/redash/query_runner/impala_ds.py b/redash/query_runner/impala_ds.py
index 8a78147346..68596513d6 100644
--- a/redash/query_runner/impala_ds.py
+++ b/redash/query_runner/impala_ds.py
@@ -1,15 +1,7 @@
import logging
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -18,7 +10,7 @@
from impala.error import DatabaseError, RPCError
enabled = True
-except ImportError:
+except ImportError as e:
enabled = False
COLUMN_NAME = 0
@@ -61,10 +53,15 @@ def configuration_schema(cls):
},
"database": {"type": "string"},
"use_ldap": {"type": "boolean"},
- "use_ssl": {"type": "boolean"},
"ldap_user": {"type": "string"},
"ldap_password": {"type": "string"},
"timeout": {"type": "number"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["host"],
"secret": ["ldap_password"],
@@ -76,13 +73,21 @@ def type(cls):
def _get_tables(self, schema_dict):
schemas_query = "show schemas;"
- tables_query = "show tables in `%s`;"
- columns_query = "show column stats `%s`.`%s`;"
-
- for schema_name in [str(a["name"]) for a in self._run_query_internal(schemas_query)]:
- for table_name in [str(a["name"]) for a in self._run_query_internal(tables_query % schema_name)]:
+ tables_query = "show tables in %s;"
+ columns_query = "show column stats %s.%s;"
+
+ for schema_name in [
+ str(a["name"]) for a in self._run_query_internal(schemas_query)
+ ]:
+ for table_name in [
+ str(a["name"])
+ for a in self._run_query_internal(tables_query % schema_name)
+ ]:
columns = [
- str(a["Column"]) for a in self._run_query_internal(columns_query % (schema_name, table_name))
+ str(a["Column"])
+ for a in self._run_query_internal(
+ columns_query % (schema_name, table_name)
+ )
]
if schema_name != "default":
@@ -93,6 +98,7 @@ def _get_tables(self, schema_dict):
return list(schema_dict.values())
def run_query(self, query, user):
+
connection = None
try:
connection = connect(**self.configuration.to_dict())
@@ -119,13 +125,14 @@ def run_query(self, query, user):
rows = [dict(zip(column_names, row)) for row in cursor]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
cursor.close()
except DatabaseError as e:
- data = None
+ json_data = None
error = str(e)
except RPCError as e:
- data = None
+ json_data = None
error = "Metastore Error [%s]" % str(e)
except (KeyboardInterrupt, JobTimeoutException):
connection.cancel()
@@ -134,7 +141,7 @@ def run_query(self, query, user):
if connection:
connection.close()
- return data, error
+ return json_data, error
register(Impala)
diff --git a/redash/query_runner/influx_db.py b/redash/query_runner/influx_db.py
index 7f5249b1ac..fbd90e1437 100644
--- a/redash/query_runner/influx_db.py
+++ b/redash/query_runner/influx_db.py
@@ -1,12 +1,7 @@
import logging
-from redash.query_runner import (
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -19,36 +14,25 @@
enabled = False
-TYPES_MAP = {
- str: TYPE_STRING,
- int: TYPE_INTEGER,
- float: TYPE_FLOAT,
-}
-
-
-def _get_type(value):
- return TYPES_MAP.get(type(value), TYPE_STRING)
-
-
def _transform_result(results):
- column_names = []
+ result_columns = []
result_rows = []
for result in results:
for series in result.raw.get("series", []):
for column in series["columns"]:
- if column not in column_names:
- column_names.append(column)
+ if column not in result_columns:
+ result_columns.append(column)
tags = series.get("tags", {})
for key in tags.keys():
- if key not in column_names:
- column_names.append(key)
+ if key not in result_columns:
+ result_columns.append(key)
for result in results:
for series in result.raw.get("series", []):
for point in series["values"]:
result_row = {}
- for column in column_names:
+ for column in result_columns:
tags = series.get("tags", {})
if column in tags:
result_row[column] = tags[column]
@@ -58,12 +42,9 @@ def _transform_result(results):
result_row[column] = value
result_rows.append(result_row)
- if len(result_rows) > 0:
- result_columns = [{"name": c, "type": _get_type(result_rows[0][c])} for c in result_rows[0].keys()]
- else:
- result_columns = [{"name": c, "type": TYPE_STRING} for c in column_names]
-
- return {"columns": result_columns, "rows": result_rows}
+ return json_dumps(
+ {"columns": [{"name": c} for c in result_columns], "rows": result_rows}
+ )
class InfluxDB(BaseQueryRunner):
@@ -74,7 +55,15 @@ class InfluxDB(BaseQueryRunner):
def configuration_schema(cls):
return {
"type": "object",
- "properties": {"url": {"type": "string"}},
+ "properties": {
+ "url": {"type": "string"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ },
"required": ["url"],
}
diff --git a/redash/query_runner/influx_db_v2.py b/redash/query_runner/influx_db_v2.py
deleted file mode 100644
index 1c23ad5ac1..0000000000
--- a/redash/query_runner/influx_db_v2.py
+++ /dev/null
@@ -1,214 +0,0 @@
-import logging
-import os
-from base64 import b64decode
-from tempfile import NamedTemporaryFile
-from typing import Any, Dict, Optional, Tuple, Type, TypeVar
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-
-try:
- from influxdb_client import InfluxDBClient
-
- enabled = True
-except ImportError:
- enabled = False
-
-logger = logging.getLogger(__name__)
-
-T = TypeVar("T")
-
-TYPES_MAP = {
- "integer": TYPE_INTEGER,
- "long": TYPE_INTEGER,
- "float": TYPE_FLOAT,
- "double": TYPE_FLOAT,
- "boolean": TYPE_BOOLEAN,
- "string": TYPE_STRING,
- "datetime:RFC3339": TYPE_DATETIME,
-}
-
-
-class InfluxDBv2(BaseQueryRunner):
- """
- Query runner for influxdb version 2.
- """
-
- should_annotate_query = False
-
- def _get_influx_kwargs(self) -> Dict:
- """
- Determines additional arguments for influxdb client connection.
- :return: An object with additional arguments for influxdb client.
- """
- return {
- "verify_ssl": self.configuration.get("verify_ssl", None),
- "cert_file": self._create_cert_file("cert_File"),
- "cert_key_file": self._create_cert_file("cert_key_File"),
- "cert_key_password": self.configuration.get("cert_key_password", None),
- "ssl_ca_cert": self._create_cert_file("ssl_ca_cert_File"),
- }
-
- def _create_cert_file(self, key: str) -> str:
- """
- Creates a temporary file from base64 encoded content from stored
- configuration in filesystem.
- :param key: The key to get the content from configuration object.
- :return: The name of temporary file.
- """
- cert_file_name = None
-
- if self.configuration.get(key, None) is not None:
- with NamedTemporaryFile(mode="w", delete=False) as cert_file:
- cert_bytes = b64decode(self.configuration[key])
- cert_file.write(cert_bytes.decode("utf-8"))
- cert_file_name = cert_file.name
-
- return cert_file_name
-
- def _cleanup_cert_files(self, influx_kwargs: Dict) -> None:
- """
- Deletes temporary stored files in filesystem.
- """
- for key in ["cert_file", "cert_key_file", "ssl_ca_cert"]:
- cert_path = influx_kwargs.get(key, None)
- if cert_path is not None and os.path.exists(cert_path):
- os.remove(cert_path)
-
- @classmethod
- def configuration_schema(cls: Type[T]) -> Dict:
- """
- Defines a configuration schema for this query runner.
- :param cls: Object of this class.
- :return: The defined configuration schema.
- """
- # files has to end with "File" in name
- return {
- "type": "object",
- "properties": {
- "url": {"type": "string", "title": "URL"},
- "org": {"type": "string", "title": "Organization"},
- "token": {"type": "string", "title": "Token"},
- "verify_ssl": {"type": "boolean", "title": "Verify SSL", "default": False},
- "cert_File": {"type": "string", "title": "SSL Client Certificate", "default": None},
- "cert_key_File": {"type": "string", "title": "SSL Client Key", "default": None},
- "cert_key_password": {"type": "string", "title": "Password for SSL Client Key", "default": None},
- "ssl_ca_cert_File": {"type": "string", "title": "SSL Root Certificate", "default": None},
- },
- "order": ["url", "org", "token", "cert_File", "cert_key_File", "cert_key_password", "ssl_ca_cert_File"],
- "required": ["url", "org", "token"],
- "secret": ["token", "cert_File", "cert_key_File", "cert_key_password", "ssl_ca_cert_File"],
- "extra_options": ["verify_ssl", "cert_File", "cert_key_File", "cert_key_password", "ssl_ca_cert_File"],
- }
-
- @classmethod
- def enabled(cls: Type[T]) -> bool:
- """
- Determines, if this query runner is enabled or not.
- :param cls: Object of this class.
- :return: True, if this query runner is enabled; otherwise False.
- """
- return enabled
-
- def test_connection(self) -> None:
- """
- Tests the healthiness of the influxdb instance. If it is not healthy,
- it logs an error message and raises an exception with an appropriate
- message.
- :raises Exception: If the remote influxdb instance is not healthy.
- """
- try:
- influx_kwargs = self._get_influx_kwargs()
- with InfluxDBClient(
- url=self.configuration["url"],
- token=self.configuration["token"],
- org=self.configuration["org"],
- **influx_kwargs,
- ) as client:
- healthy = client.health()
- if healthy.status == "fail":
- logger.error("Connection test failed, due to: " f"{healthy.message!r}.")
- raise Exception("InfluxDB is not healthy. Check logs for more " "information.")
- except Exception:
- raise
- finally:
- self._cleanup_cert_files(influx_kwargs)
-
- def _get_type(self, type_: str) -> str:
- """
- Determines the internal type of a passed data type which the database
- uses.
- :param type_: The type from the database to map to internal datatype.
- :return: The name of the internal datatype.
- """
- return TYPES_MAP.get(type_, "string")
-
- def _get_data_from_tables(self, tables: Any) -> Dict:
- """
- Determines the data of the given tables in an appropriate schema for
- redash ui to render it. It retrieves all available columns and records
- from the tables.
- :param tables: A list of FluxTable instances.
- :return: An object with columns and rows list.
- """
- columns = []
- rows = []
-
- for table in tables:
- for column in table.columns:
- column_entry = {
- "name": column.label,
- "type": self._get_type(column.data_type),
- "friendly_name": column.label.title(),
- }
- if column_entry not in columns:
- columns.append(column_entry)
-
- rows.extend([row.values for row in [record for record in table.records]])
-
- return {"columns": columns, "rows": rows}
-
- def run_query(self, query: str, user: str) -> Tuple[Optional[str], Optional[str]]:
- """
- Runs a given query against the influxdb instance and returns its
- result.
- :param query: The query, this runner is executed.
- :param user: The user who runs the query.
- :return: A 2-tuple:
- 1. element: The queried result in an appropriate format for redash
- ui. If an error occurred, it returns None.
- 2. element: An error message, if an error occured. None, if no
- error occurred.
- """
- data = None
- error = None
-
- try:
- influx_kwargs = self._get_influx_kwargs()
- with InfluxDBClient(
- url=self.configuration["url"],
- token=self.configuration["token"],
- org=self.configuration["org"],
- **influx_kwargs,
- ) as client:
- logger.debug(f"InfluxDB got query: {query!r}")
-
- tables = client.query_api().query(query)
-
- data = self._get_data_from_tables(tables)
- except Exception as ex:
- error = str(ex)
- finally:
- self._cleanup_cert_files(influx_kwargs)
-
- return data, error
-
-
-register(InfluxDBv2)
diff --git a/redash/query_runner/jql.py b/redash/query_runner/jql.py
index f7d26aa24c..d2a4dcfd5d 100644
--- a/redash/query_runner/jql.py
+++ b/redash/query_runner/jql.py
@@ -1,12 +1,12 @@
import re
from collections import OrderedDict
-from redash.query_runner import TYPE_STRING, BaseHTTPQueryRunner, register
-from redash.utils import json_loads
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
# TODO: make this more general and move into __init__.py
-class ResultSet:
+class ResultSet(object):
def __init__(self):
self.columns = OrderedDict()
self.rows = []
@@ -26,13 +26,13 @@ def add_column(self, column, column_type=TYPE_STRING):
}
def to_json(self):
- return {"rows": self.rows, "columns": list(self.columns.values())}
+ return json_dumps({"rows": self.rows, "columns": list(self.columns.values())})
def merge(self, set):
self.rows = self.rows + set.rows
-def parse_issue(issue, field_mapping): # noqa: C901
+def parse_issue(issue, field_mapping):
result = OrderedDict()
result["key"] = issue["key"]
@@ -45,7 +45,9 @@ def parse_issue(issue, field_mapping): # noqa: C901
# if field mapping with dict member mappings defined get value of each member
for member_name in member_names:
if member_name in v:
- result[field_mapping.get_dict_output_field_name(k, member_name)] = v[member_name]
+ result[
+ field_mapping.get_dict_output_field_name(k, member_name)
+ ] = v[member_name]
else:
# these special mapping rules are kept for backwards compatibility
@@ -70,7 +72,9 @@ def parse_issue(issue, field_mapping): # noqa: C901
if member_name in listItem:
listValues.append(listItem[member_name])
if len(listValues) > 0:
- result[field_mapping.get_dict_output_field_name(k, member_name)] = ",".join(listValues)
+ result[
+ field_mapping.get_dict_output_field_name(k, member_name)
+ ] = ",".join(listValues)
else:
# otherwise support list values only for non-dict items
@@ -110,7 +114,7 @@ def __init__(cls, query_field_mapping):
member_name = None
# check for member name contained in field name
- member_parser = re.search(r"(\w+)\.(\w+)", k)
+ member_parser = re.search("(\w+)\.(\w+)", k)
if member_parser:
field_name = member_parser.group(1)
member_name = member_parser.group(2)
diff --git a/redash/query_runner/json_ds.py b/redash/query_runner/json_ds.py
index 4f47878c6c..ed8562bcc4 100644
--- a/redash/query_runner/json_ds.py
+++ b/redash/query_runner/json_ds.py
@@ -1,18 +1,18 @@
-import datetime
import logging
-from urllib.parse import urljoin
-
import yaml
+import datetime
from funcy import compact, project
-
+from redash import settings
+from redash.utils import json_dumps
from redash.query_runner import (
+ BaseHTTPQueryRunner,
+ register,
TYPE_BOOLEAN,
TYPE_DATETIME,
TYPE_FLOAT,
TYPE_INTEGER,
TYPE_STRING,
- BaseHTTPQueryRunner,
- register,
+ is_private_address,
)
@@ -58,10 +58,12 @@ def _get_type(value):
def add_column(columns, column_name, column_type):
if _get_column_by_name(columns, column_name) is None:
- columns.append({"name": column_name, "friendly_name": column_name, "type": column_type})
+ columns.append(
+ {"name": column_name, "friendly_name": column_name, "type": column_type}
+ )
-def _apply_path_search(response, path, default=None):
+def _apply_path_search(response, path):
if path is None:
return response
@@ -71,8 +73,6 @@ def _apply_path_search(response, path, default=None):
current_path = path_parts.pop()
if current_path in response:
response = response[current_path]
- elif default is not None:
- return default
else:
raise Exception("Couldn't find path {} in response.".format(path))
@@ -80,8 +80,6 @@ def _apply_path_search(response, path, default=None):
def _normalize_json(data, path):
- if not data:
- return None
data = _apply_path_search(data, path)
if isinstance(data, dict):
@@ -98,7 +96,9 @@ def _sort_columns_with_fields(columns, fields):
# TODO: merge the logic here with the one in MongoDB's queyr runner
-def parse_json(data, fields):
+def parse_json(data, path, fields):
+ data = _normalize_json(data, path)
+
rows = []
columns = []
@@ -132,19 +132,17 @@ def parse_json(data, fields):
class JSON(BaseHTTPQueryRunner):
requires_url = False
- base_url_title = "Base URL"
@classmethod
def configuration_schema(cls):
return {
"type": "object",
"properties": {
- "base_url": {"type": "string", "title": cls.base_url_title},
"username": {"type": "string", "title": cls.username_title},
"password": {"type": "string", "title": cls.password_title},
},
"secret": ["password"],
- "order": ["base_url", "username", "password"],
+ "order": ["username", "password"],
}
def __init__(self, configuration):
@@ -157,36 +155,30 @@ def test_connection(self):
def run_query(self, query, user):
query = parse_query(query)
- data, error = self._run_json_query(query)
- if error is not None:
- return None, error
-
- if data:
- return data, None
- return None, "Got empty response from '{}'.".format(query["url"])
-
- def _run_json_query(self, query):
if not isinstance(query, dict):
- raise QueryParseError("Query should be a YAML object describing the URL to query.")
+ raise QueryParseError(
+ "Query should be a YAML object describing the URL to query."
+ )
if "url" not in query:
raise QueryParseError("Query must include 'url' option.")
+ if is_private_address(query["url"]) and settings.ENFORCE_PRIVATE_ADDRESS_BLOCK:
+ raise Exception("Can't query private addresses.")
+
method = query.get("method", "get")
- request_options = project(query, ("params", "headers", "data", "auth", "json", "verify"))
+ request_options = project(query, ("params", "headers", "data", "auth", "json"))
fields = query.get("fields")
path = query.get("path")
- if "pagination" in query:
- pagination = RequestPagination.from_config(self.configuration, query["pagination"])
- else:
- pagination = None
-
if isinstance(request_options.get("auth", None), list):
request_options["auth"] = tuple(request_options["auth"])
elif self.configuration.get("username") or self.configuration.get("password"):
- request_options["auth"] = (self.configuration.get("username"), self.configuration.get("password"))
+ request_options["auth"] = (
+ self.configuration.get("username"),
+ self.configuration.get("password"),
+ )
if method not in ("get", "post"):
raise QueryParseError("Only GET or POST methods are allowed.")
@@ -194,91 +186,19 @@ def _run_json_query(self, query):
if fields and not isinstance(fields, list):
raise QueryParseError("'fields' needs to be a list.")
- results, error = self._get_all_results(query["url"], method, path, pagination, **request_options)
- return parse_json(results, fields), error
-
- def _get_all_results(self, url, method, result_path, pagination, **request_options):
- """Get all results from a paginated endpoint."""
- base_url = self.configuration.get("base_url")
- url = urljoin(base_url, url)
-
- results = []
- has_more = True
- while has_more:
- response, error = self._get_json_response(url, method, **request_options)
- has_more = False
-
- result = _normalize_json(response, result_path)
- if result:
- results.extend(result)
- if pagination:
- has_more, url, request_options = pagination.next(url, request_options, response)
+ response, error = self.get_response(
+ query["url"], http_method=method, **request_options
+ )
- return results, error
-
- def _get_json_response(self, url, method, **request_options):
- response, error = self.get_response(url, http_method=method, **request_options)
- result = response.json() if error is None else {}
- return result, error
-
-
-class RequestPagination:
- def next(self, url, request_options, response):
- """Checks the response for another page.
-
- Returns:
- has_more, next_url, next_request_options
- """
- return False, None, request_options
-
- @staticmethod
- def from_config(configuration, pagination):
- if not isinstance(pagination, dict) or not isinstance(pagination.get("type"), str):
- raise QueryParseError("'pagination' should be an object with a `type` property")
-
- if pagination["type"] == "url":
- return UrlPagination(pagination)
- elif pagination["type"] == "token":
- return TokenPagination(pagination)
-
- raise QueryParseError("Unknown 'pagination.type' {}".format(pagination["type"]))
-
-
-class UrlPagination(RequestPagination):
- def __init__(self, pagination):
- self.path = pagination.get("path", "_links.next.href")
- if not isinstance(self.path, str):
- raise QueryParseError("'pagination.path' should be a string")
-
- def next(self, url, request_options, response):
- next_url = _apply_path_search(response, self.path, "")
- if not next_url:
- return False, None, request_options
-
- next_url = urljoin(url, next_url)
- return True, next_url, request_options
-
-
-class TokenPagination(RequestPagination):
- def __init__(self, pagination):
- self.fields = pagination.get("fields", ["next_page_token", "page_token"])
- if not isinstance(self.fields, list) or len(self.fields) != 2:
- raise QueryParseError("'pagination.fields' should be a list of 2 field names")
-
- def next(self, url, request_options, response):
- next_token = _apply_path_search(response, self.fields[0], "")
- if not next_token:
- return False, None, request_options
-
- params = request_options.get("params", {})
+ if error is not None:
+ return None, error
- # prevent infinite loop that can happen if self.fields[1] is wrong
- if next_token == params.get(self.fields[1]):
- raise Exception("{} did not change; possible misconfiguration".format(self.fields[0]))
+ data = json_dumps(parse_json(response.json(), path, fields))
- params[self.fields[1]] = next_token
- request_options["params"] = params
- return True, url, request_options
+ if data:
+ return data, None
+ else:
+ return None, "Got empty response from '{}'.".format(query["url"])
register(JSON)
diff --git a/redash/query_runner/kylin.py b/redash/query_runner/kylin.py
index 18f6ff9c6b..cfc02c671f 100644
--- a/redash/query_runner/kylin.py
+++ b/redash/query_runner/kylin.py
@@ -1,20 +1,11 @@
-import logging
import os
-
+import logging
import requests
from requests.auth import HTTPBasicAuth
from redash import settings
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -101,7 +92,7 @@ def run_query(self, query, user):
columns = self.get_columns(data["columnMetas"])
rows = self.get_rows(columns, data["results"])
- return {"columns": columns, "rows": rows}, None
+ return json_dumps({"columns": columns, "rows": rows}), None
def get_schema(self, get_stats=False):
url = self.configuration["url"]
@@ -136,7 +127,9 @@ def get_columns(self, colmetas):
)
def get_rows(self, columns, results):
- return [dict(zip((column["name"] for column in columns), row)) for row in results]
+ return [
+ dict(zip((column["name"] for column in columns), row)) for row in results
+ ]
def get_table_schema(self, table):
name = table["table_NAME"]
diff --git a/redash/query_runner/mapd.py b/redash/query_runner/mapd.py
new file mode 100644
index 0000000000..45f77cc273
--- /dev/null
+++ b/redash/query_runner/mapd.py
@@ -0,0 +1,109 @@
+try:
+ import pymapd
+
+ enabled = True
+except ImportError:
+ enabled = False
+
+from redash.query_runner import BaseSQLQueryRunner, register
+from redash.query_runner import (
+ TYPE_STRING,
+ TYPE_DATE,
+ TYPE_DATETIME,
+ TYPE_INTEGER,
+ TYPE_FLOAT,
+ TYPE_BOOLEAN,
+)
+from redash.utils import json_dumps
+
+TYPES_MAP = {
+ 0: TYPE_INTEGER,
+ 1: TYPE_INTEGER,
+ 2: TYPE_INTEGER,
+ 3: TYPE_FLOAT,
+ 4: TYPE_FLOAT,
+ 5: TYPE_FLOAT,
+ 6: TYPE_STRING,
+ 7: TYPE_DATE,
+ 8: TYPE_DATETIME,
+ 9: TYPE_DATE,
+ 10: TYPE_BOOLEAN,
+ 11: TYPE_DATE,
+ 12: TYPE_DATE,
+}
+
+
+class Mapd(BaseSQLQueryRunner):
+ @classmethod
+ def configuration_schema(cls):
+ return {
+ "type": "object",
+ "properties": {
+ "host": {"type": "string", "default": "localhost"},
+ "port": {"type": "number", "default": 9091},
+ "user": {"type": "string", "default": "mapd", "title": "username"},
+ "password": {"type": "string", "default": "HyperInteractive"},
+ "database": {"type": "string", "default": "mapd"},
+ },
+ "order": ["user", "password", "host", "port", "database"],
+ "required": ["host", "port", "user", "password", "database"],
+ "secret": ["password"],
+ }
+
+ @classmethod
+ def enabled(cls):
+ return enabled
+
+ def connect_database(self):
+ connection = pymapd.connect(
+ user=self.configuration["user"],
+ password=self.configuration["password"],
+ host=self.configuration["host"],
+ port=self.configuration["port"],
+ dbname=self.configuration["database"],
+ )
+ return connection
+
+ def run_query(self, query, user):
+ connection = self.connect_database()
+ cursor = connection.cursor()
+
+ try:
+ cursor.execute(query)
+ columns = self.fetch_columns(
+ [(i[0], TYPES_MAP.get(i[1], None)) for i in cursor.description]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row)) for row in cursor
+ ]
+ data = {"columns": columns, "rows": rows}
+ error = None
+ json_data = json_dumps(data)
+ finally:
+ cursor.close()
+ connection.close()
+
+ return json_data, error
+
+ def _get_tables(self, schema):
+ connection = self.connect_database()
+ try:
+ for table_name in connection.get_tables():
+ schema[table_name] = {"name": table_name, "columns": []}
+ for row_column in connection.get_table_details(table_name):
+ schema[table_name]["columns"].append(row_column[0])
+ finally:
+ connection.close
+
+ return list(schema.values())
+
+ def test_connection(self):
+ connection = self.connect_database()
+ try:
+ tables = connection.get_tables()
+ num_tables = tables.count(tables)
+ finally:
+ connection.close
+
+
+register(Mapd)
diff --git a/redash/query_runner/memsql_ds.py b/redash/query_runner/memsql_ds.py
index f24c230816..dc0bf9d8c8 100644
--- a/redash/query_runner/memsql_ds.py
+++ b/redash/query_runner/memsql_ds.py
@@ -1,15 +1,8 @@
import logging
+import sys
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -57,6 +50,12 @@ def configuration_schema(cls):
"port": {"type": "number"},
"user": {"type": "string"},
"password": {"type": "string"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["host", "port"],
"secret": ["password"],
@@ -78,19 +77,27 @@ def _get_tables(self, schema):
columns_query = "show columns in %s"
for schema_name in [
- a for a in [str(a["Database"]) for a in self._run_query_internal(schemas_query)] if len(a) > 0
+ a
+ for a in [
+ str(a["Database"]) for a in self._run_query_internal(schemas_query)
+ ]
+ if len(a) > 0
]:
for table_name in [
a
for a in [
- str(a["Tables_in_%s" % schema_name]) for a in self._run_query_internal(tables_query % schema_name)
+ str(a["Tables_in_%s" % schema_name])
+ for a in self._run_query_internal(tables_query % schema_name)
]
if len(a) > 0
]:
table_name = ".".join((schema_name, table_name))
columns = [
a
- for a in [str(a["Field"]) for a in self._run_query_internal(columns_query % table_name)]
+ for a in [
+ str(a["Field"])
+ for a in self._run_query_internal(columns_query % table_name)
+ ]
if len(a) > 0
]
@@ -98,6 +105,7 @@ def _get_tables(self, schema):
return list(schema.values())
def run_query(self, query, user):
+
cursor = None
try:
cursor = database.connect(**self.configuration.to_dict())
@@ -126,9 +134,12 @@ def run_query(self, query, user):
if column_names:
for column in column_names:
- columns.append({"name": column, "friendly_name": column, "type": TYPE_STRING})
+ columns.append(
+ {"name": column, "friendly_name": column, "type": TYPE_STRING}
+ )
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
except (KeyboardInterrupt, JobTimeoutException):
cursor.close()
@@ -137,7 +148,7 @@ def run_query(self, query, user):
if cursor:
cursor.close()
- return data, error
+ return json_data, error
register(MemSQL)
diff --git a/redash/query_runner/mongodb.py b/redash/query_runner/mongodb.py
index 3a54d2536b..74faaf8e12 100644
--- a/redash/query_runner/mongodb.py
+++ b/redash/query_runner/mongodb.py
@@ -4,27 +4,18 @@
from dateutil.parser import parse
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-from redash.utils import json_loads, parse_human_time
+from redash.query_runner import *
+from redash.utils import JSONEncoder, json_dumps, json_loads, parse_human_time
logger = logging.getLogger(__name__)
try:
import pymongo
- from bson.decimal128 import Decimal128
- from bson.json_util import JSONOptions
- from bson.json_util import object_hook as bson_object_hook
from bson.objectid import ObjectId
- from bson.son import SON
from bson.timestamp import Timestamp
+ from bson.decimal128 import Decimal128
+ from bson.son import SON
+ from bson.json_util import object_hook as bson_object_hook
enabled = True
@@ -42,7 +33,18 @@
}
-date_regex = re.compile(r'ISODate\("(.*)"\)', re.IGNORECASE)
+class MongoDBJSONEncoder(JSONEncoder):
+ def default(self, o):
+ if isinstance(o, ObjectId):
+ return str(o)
+ elif isinstance(o, Timestamp):
+ return super(MongoDBJSONEncoder, self).default(o.as_datetime())
+ elif isinstance(o, Decimal128):
+ return o.to_decimal()
+ return super(MongoDBJSONEncoder, self).default(o)
+
+
+date_regex = re.compile('ISODate\("(.*)"\)', re.IGNORECASE)
def parse_oids(oids):
@@ -65,11 +67,10 @@ def datetime_parser(dct):
if "$oids" in dct:
return parse_oids(dct["$oids"])
- opts = JSONOptions(tz_aware=True)
- return bson_object_hook(dct, json_options=opts)
+ return bson_object_hook(dct)
-def parse_query_json(query: str):
+def parse_query_json(query):
query_data = json_loads(query, object_hook=datetime_parser)
return query_data
@@ -82,64 +83,45 @@ def _get_column_by_name(columns, column_name):
return None
-def _parse_dict(dic: dict, flatten: bool = False) -> dict:
- res = {}
-
- def _flatten(x, name=""):
- if isinstance(x, dict):
- for k, v in x.items():
- _flatten(v, "{}.{}".format(name, k))
- elif isinstance(x, list):
- for idx, item in enumerate(x):
- _flatten(item, "{}.{}".format(name, idx))
- else:
- res[name[1:]] = x
-
- if flatten:
- _flatten(dic)
- else:
- for key, value in dic.items():
- if isinstance(value, dict):
- for tmp_key, tmp_value in _parse_dict(value).items():
- new_key = "{}.{}".format(key, tmp_key)
- res[new_key] = tmp_value
- else:
- res[key] = value
- return res
-
-
-def parse_results(results: list, flatten: bool = False) -> list:
+def parse_results(results):
rows = []
columns = []
for row in results:
parsed_row = {}
- parsed_row = _parse_dict(row, flatten)
- for column_name, value in parsed_row.items():
- if _get_column_by_name(columns, column_name) is None:
- columns.append(
- {
- "name": column_name,
- "friendly_name": column_name,
- "type": TYPES_MAP.get(type(value), TYPE_STRING),
- }
- )
+ for key in row:
+ if isinstance(row[key], dict):
+ for inner_key in row[key]:
+ column_name = "{}.{}".format(key, inner_key)
+ if _get_column_by_name(columns, column_name) is None:
+ columns.append(
+ {
+ "name": column_name,
+ "friendly_name": column_name,
+ "type": TYPES_MAP.get(
+ type(row[key][inner_key]), TYPE_STRING
+ ),
+ }
+ )
+
+ parsed_row[column_name] = row[key][inner_key]
- rows.append(parsed_row)
-
- return rows, columns
+ else:
+ if _get_column_by_name(columns, key) is None:
+ columns.append(
+ {
+ "name": key,
+ "friendly_name": key,
+ "type": TYPES_MAP.get(type(row[key]), TYPE_STRING),
+ }
+ )
+ parsed_row[key] = row[key]
-def _sorted_fields(fields):
- ord = {}
- for k, v in fields.items():
- if isinstance(v, int):
- ord[k] = v
- else:
- ord[k] = len(fields)
+ rows.append(parsed_row)
- return sorted(ord, key=ord.get)
+ return rows, columns
class MongoDB(BaseQueryRunner):
@@ -151,8 +133,6 @@ def configuration_schema(cls):
"type": "object",
"properties": {
"connectionString": {"type": "string", "title": "Connection String"},
- "username": {"type": "string"},
- "password": {"type": "string"},
"dbName": {"type": "string", "title": "Database Name"},
"replicaSetName": {"type": "string", "title": "Replica Set Name"},
"readPreference": {
@@ -166,16 +146,13 @@ def configuration_schema(cls):
],
"title": "Replica Set Read Preference",
},
- "flatten": {
+ "toggle_table_string": {
"type": "string",
- "extendedEnum": [
- {"value": "False", "name": "False"},
- {"value": "True", "name": "True"},
- ],
- "title": "Flatten Results",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
},
},
- "secret": ["password"],
"required": ["connectionString", "dbName"],
}
@@ -188,25 +165,15 @@ def __init__(self, configuration):
self.syntax = "json"
- self.db_name = self.configuration.get("dbName", "")
+ self.db_name = self.configuration["dbName"]
self.is_replica_set = (
- True if "replicaSetName" in self.configuration and self.configuration["replicaSetName"] else False
+ True
+ if "replicaSetName" in self.configuration
+ and self.configuration["replicaSetName"]
+ else False
)
- self.flatten = self.configuration.get("flatten", "False").upper() in ["TRUE", "YES", "ON", "1", "Y", "T"]
- logger.debug("flatten: {}".format(self.flatten))
-
- @classmethod
- def custom_json_encoder(cls, dec, o):
- if isinstance(o, ObjectId):
- return str(o)
- elif isinstance(o, Timestamp):
- return dec.default(o.as_datetime())
- elif isinstance(o, Decimal128):
- return o.to_decimal()
- return None
-
def _get_db(self):
kwargs = {}
if self.is_replica_set:
@@ -215,13 +182,9 @@ def _get_db(self):
if readPreference:
kwargs["readPreference"] = readPreference
- if "username" in self.configuration:
- kwargs["username"] = self.configuration["username"]
-
- if "password" in self.configuration:
- kwargs["password"] = self.configuration["password"]
-
- db_connection = pymongo.MongoClient(self.configuration["connectionString"], **kwargs)
+ db_connection = pymongo.MongoClient(
+ self.configuration["connectionString"], **kwargs
+ )
return db_connection[self.db_name]
@@ -255,21 +218,15 @@ def _get_collection_fields(self, db, collection_name):
# document written.
collection_is_a_view = self._is_collection_a_view(db, collection_name)
documents_sample = []
- try:
- if collection_is_a_view:
- for d in db[collection_name].find().limit(2):
- documents_sample.append(d)
- else:
- for d in db[collection_name].find().sort([("$natural", 1)]).limit(1):
- documents_sample.append(d)
-
- for d in db[collection_name].find().sort([("$natural", -1)]).limit(1):
- documents_sample.append(d)
- except Exception as ex:
- template = "An exception of type {0} occurred. Arguments:\n{1!r}"
- message = template.format(type(ex).__name__, ex.args)
- logger.error(message)
- return []
+ if collection_is_a_view:
+ for d in db[collection_name].find().limit(2):
+ documents_sample.append(d)
+ else:
+ for d in db[collection_name].find().sort([("$natural", 1)]).limit(1):
+ documents_sample.append(d)
+
+ for d in db[collection_name].find().sort([("$natural", -1)]).limit(1):
+ documents_sample.append(d)
columns = []
for d in documents_sample:
self._merge_property_names(columns, d)
@@ -278,28 +235,29 @@ def _get_collection_fields(self, db, collection_name):
def get_schema(self, get_stats=False):
schema = {}
db = self._get_db()
- for collection_name in db.list_collection_names():
+ for collection_name in db.collection_names():
if collection_name.startswith("system."):
continue
columns = self._get_collection_fields(db, collection_name)
- if columns:
- schema[collection_name] = {
- "name": collection_name,
- "columns": sorted(columns),
- }
+ schema[collection_name] = {
+ "name": collection_name,
+ "columns": sorted(columns),
+ }
return list(schema.values())
- def run_query(self, query, user): # noqa: C901
+ def run_query(self, query, user):
db = self._get_db()
- logger.debug("mongodb connection string: %s", self.configuration["connectionString"])
+ logger.debug(
+ "mongodb connection string: %s", self.configuration["connectionString"]
+ )
logger.debug("mongodb got query: %s", query)
try:
query_data = parse_query_json(query)
- except ValueError as error:
- return None, f"Invalid JSON format. {error.__str__()}"
+ except ValueError:
+ return None, "Invalid query format. The query is not a valid JSON."
if "collection" not in query_data:
return None, "'collection' must have a value to run a query"
@@ -315,10 +273,8 @@ def run_query(self, query, user): # noqa: C901
if "$sort" in step:
sort_list = []
for sort_item in step["$sort"]:
- if isinstance(sort_item, dict):
- sort_list.append((sort_item["name"], sort_item.get("direction", 1)))
- elif isinstance(sort_item, list):
- sort_list.append(tuple(sort_item))
+ sort_list.append((sort_item["name"], sort_item["direction"]))
+
step["$sort"] = SON(sort_list)
if "fields" in query_data:
@@ -328,30 +284,26 @@ def run_query(self, query, user): # noqa: C901
if "sort" in query_data and query_data["sort"]:
s = []
for field_data in query_data["sort"]:
- if isinstance(field_data, dict):
- s.append((field_data["name"], field_data.get("direction", 1)))
- elif isinstance(field_data, list):
- s.append(tuple(field_data))
+ s.append((field_data["name"], field_data["direction"]))
columns = []
rows = []
cursor = None
if q or (not q and not aggregate):
- if "count" in query_data:
- options = {opt: query_data[opt] for opt in ("skip", "limit") if opt in query_data}
- cursor = db[collection].count_documents(q, **options)
+ if s:
+ cursor = db[collection].find(q, f).sort(s)
else:
- if s:
- cursor = db[collection].find(q, f).sort(s)
- else:
- cursor = db[collection].find(q, f)
+ cursor = db[collection].find(q, f)
- if "skip" in query_data:
- cursor = cursor.skip(query_data["skip"])
+ if "skip" in query_data:
+ cursor = cursor.skip(query_data["skip"])
- if "limit" in query_data:
- cursor = cursor.limit(query_data["limit"])
+ if "limit" in query_data:
+ cursor = cursor.limit(query_data["limit"])
+
+ if "count" in query_data:
+ cursor = cursor.count()
elif aggregate:
allow_disk_use = query_data.get("allowDiskUse", False)
@@ -368,21 +320,22 @@ def run_query(self, query, user): # noqa: C901
cursor = r
if "count" in query_data:
- columns.append({"name": "count", "friendly_name": "count", "type": TYPE_INTEGER})
+ columns.append(
+ {"name": "count", "friendly_name": "count", "type": TYPE_INTEGER}
+ )
rows.append({"count": cursor})
else:
- rows, columns = parse_results(cursor, flatten=self.flatten)
+ rows, columns = parse_results(cursor)
if f:
ordered_columns = []
- for k in _sorted_fields(f):
+ for k in sorted(f, key=f.get):
column = _get_column_by_name(columns, k)
if column:
ordered_columns.append(column)
columns = ordered_columns
- logger.debug("columns: {}".format(columns))
if query_data.get("sortColumns"):
reverse = query_data["sortColumns"] == "desc"
@@ -390,8 +343,9 @@ def run_query(self, query, user): # noqa: C901
data = {"columns": columns, "rows": rows}
error = None
+ json_data = json_dumps(data, cls=MongoDBJSONEncoder)
- return data, error
+ return json_data, error
register(MongoDB)
diff --git a/redash/query_runner/mssql.py b/redash/query_runner/mssql.py
index 4bb53c0d9f..79db424e26 100644
--- a/redash/query_runner/mssql.py
+++ b/redash/query_runner/mssql.py
@@ -1,13 +1,9 @@
import logging
+import sys
+import uuid
-from redash.query_runner import (
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_STRING,
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
@@ -34,10 +30,6 @@ class SqlServer(BaseSQLQueryRunner):
should_annotate_query = False
noop_query = "SELECT 1"
- limit_query = " TOP 1000"
- limit_keywords = ["TOP"]
- limit_after_select = True
-
@classmethod
def configuration_schema(cls):
return {
@@ -58,6 +50,12 @@ def configuration_schema(cls):
"title": "Character Set",
},
"db": {"type": "string", "title": "Database Name"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["db"],
"secret": ["password"],
@@ -88,7 +86,9 @@ def _get_tables(self, schema):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
if row["table_schema"] != self.configuration["db"]:
@@ -137,17 +137,22 @@ def run_query(self, query, user):
data = cursor.fetchall()
if cursor.description is not None:
- columns = self.fetch_columns([(i[0], types_map.get(i[1], None)) for i in cursor.description])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in data]
+ columns = self.fetch_columns(
+ [(i[0], types_map.get(i[1], None)) for i in cursor.description]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row))
+ for row in data
+ ]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
else:
error = "No data was returned."
- data = None
+ json_data = None
cursor.close()
- connection.commit()
except pymssql.Error as e:
try:
# Query errors are at `args[1]`
@@ -155,7 +160,7 @@ def run_query(self, query, user):
except IndexError:
# Connection errors are `args[0][1]`
error = e.args[0][1]
- data = None
+ json_data = None
except (KeyboardInterrupt, JobTimeoutException):
connection.cancel()
raise
@@ -163,7 +168,7 @@ def run_query(self, query, user):
if connection:
connection.close()
- return data, error
+ return json_data, error
register(SqlServer)
diff --git a/redash/query_runner/mssql_odbc.py b/redash/query_runner/mssql_odbc.py
index ad6ca5dab6..0c02db4138 100644
--- a/redash/query_runner/mssql_odbc.py
+++ b/redash/query_runner/mssql_odbc.py
@@ -1,11 +1,10 @@
import logging
+import sys
+import uuid
-from redash.query_runner import (
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import *
from redash.query_runner.mssql import types_map
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
@@ -21,10 +20,6 @@ class SQLServerODBC(BaseSQLQueryRunner):
should_annotate_query = False
noop_query = "SELECT 1"
- limit_query = " TOP 1000"
- limit_keywords = ["TOP"]
- limit_after_select = True
-
@classmethod
def configuration_schema(cls):
return {
@@ -40,15 +35,11 @@ def configuration_schema(cls):
"default": "UTF-8",
"title": "Character Set",
},
- "use_ssl": {
- "type": "boolean",
- "title": "Use SSL",
- "default": False,
- },
+ "use_ssl": {"type": "boolean", "title": "Use SSL", "default": False,},
"verify_ssl": {
"type": "boolean",
"title": "Verify SSL certificate",
- "default": False,
+ "default": True,
},
},
"order": [
@@ -78,10 +69,6 @@ def name(cls):
def type(cls):
return "mssql_odbc"
- @property
- def supports_auto_limit(self):
- return False
-
def _get_tables(self, schema):
query = """
SELECT table_schema, table_name, column_name
@@ -95,7 +82,9 @@ def _get_tables(self, schema):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
if row["table_schema"] != self.configuration["db"]:
@@ -119,30 +108,18 @@ def run_query(self, query, user):
password = self.configuration.get("password", "")
db = self.configuration["db"]
port = self.configuration.get("port", 1433)
+ charset = self.configuration.get("charset", "UTF-8")
- connection_params = {
- "Driver": "{ODBC Driver 18 for SQL Server}",
- "Server": server,
- "Port": port,
- "Database": db,
- "Uid": user,
- "Pwd": password,
- }
+ connection_string_fmt = "DRIVER={{ODBC Driver 17 for SQL Server}};PORT={};SERVER={};DATABASE={};UID={};PWD={}"
+ connection_string = connection_string_fmt.format(
+ port, server, db, user, password
+ )
if self.configuration.get("use_ssl", False):
- connection_params["Encrypt"] = "YES"
+ connection_string += ";Encrypt=YES"
if not self.configuration.get("verify_ssl"):
- connection_params["TrustServerCertificate"] = "YES"
- else:
- connection_params["TrustServerCertificate"] = "NO"
- else:
- connection_params["Encrypt"] = "NO"
-
- def fn(k):
- return "{}={}".format(k, connection_params[k])
-
- connection_string = ";".join(list(map(fn, connection_params)))
+ connection_string += ";TrustServerCertificate=YES"
connection = pyodbc.connect(connection_string)
cursor = connection.cursor()
@@ -151,14 +128,20 @@ def fn(k):
data = cursor.fetchall()
if cursor.description is not None:
- columns = self.fetch_columns([(i[0], types_map.get(i[1], None)) for i in cursor.description])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in data]
+ columns = self.fetch_columns(
+ [(i[0], types_map.get(i[1], None)) for i in cursor.description]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row))
+ for row in data
+ ]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
else:
error = "No data was returned."
- data = None
+ json_data = None
cursor.close()
except pyodbc.Error as e:
@@ -168,7 +151,7 @@ def fn(k):
except IndexError:
# Connection errors are `args[0][1]`
error = e.args[0][1]
- data = None
+ json_data = None
except (KeyboardInterrupt, JobTimeoutException):
connection.cancel()
raise
@@ -176,7 +159,7 @@ def fn(k):
if connection:
connection.close()
- return data, error
+ return json_data, error
register(SQLServerODBC)
diff --git a/redash/query_runner/mysql.py b/redash/query_runner/mysql.py
index ef196236c2..f1c03d05d3 100644
--- a/redash/query_runner/mysql.py
+++ b/redash/query_runner/mysql.py
@@ -3,17 +3,18 @@
import threading
from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
TYPE_FLOAT,
TYPE_INTEGER,
+ TYPE_DATETIME,
TYPE_STRING,
+ TYPE_DATE,
BaseSQLQueryRunner,
InterruptException,
JobTimeoutException,
register,
)
from redash.settings import parse_boolean
+from redash.utils import json_dumps, json_loads
try:
import MySQLdb
@@ -43,17 +44,20 @@
}
-class Result:
+class Result(object):
def __init__(self):
pass
class Mysql(BaseSQLQueryRunner):
noop_query = "SELECT 1"
+ sample_query = "SELECT * FROM {table} LIMIT 1"
@classmethod
def configuration_schema(cls):
- show_ssl_settings = parse_boolean(os.environ.get("MYSQL_SHOW_SSL_SETTINGS", "true"))
+ show_ssl_settings = parse_boolean(
+ os.environ.get("MYSQL_SHOW_SSL_SETTINGS", "true")
+ )
schema = {
"type": "object",
@@ -63,22 +67,15 @@ def configuration_schema(cls):
"passwd": {"type": "string", "title": "Password"},
"db": {"type": "string", "title": "Database name"},
"port": {"type": "number", "default": 3306},
- "connect_timeout": {"type": "number", "default": 60, "title": "Connection Timeout"},
- "charset": {"type": "string", "default": "utf8"},
- "use_unicode": {"type": "boolean", "default": True},
- "autocommit": {"type": "boolean", "default": False},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ "samples": {"type": "boolean", "title": "Show Data Samples"},
},
- "order": [
- "host",
- "port",
- "user",
- "passwd",
- "db",
- "connect_timeout",
- "charset",
- "use_unicode",
- "autocommit",
- ],
+ "order": ["host", "port", "user", "passwd", "db"],
"required": ["db"],
"secret": ["passwd"],
}
@@ -86,18 +83,6 @@ def configuration_schema(cls):
if show_ssl_settings:
schema["properties"].update(
{
- "ssl_mode": {
- "type": "string",
- "title": "SSL Mode",
- "default": "preferred",
- "extendedEnum": [
- {"value": "disabled", "name": "Disabled"},
- {"value": "preferred", "name": "Preferred"},
- {"value": "required", "name": "Required"},
- {"value": "verify-ca", "name": "Verify CA"},
- {"value": "verify-identity", "name": "Verify Identity"},
- ],
- },
"use_ssl": {"type": "boolean", "title": "Use SSL"},
"ssl_cacert": {
"type": "string",
@@ -131,10 +116,9 @@ def _connection(self):
passwd=self.configuration.get("passwd", ""),
db=self.configuration["db"],
port=self.configuration.get("port", 3306),
- charset=self.configuration.get("charset", "utf8"),
- use_unicode=self.configuration.get("use_unicode", True),
- connect_timeout=self.configuration.get("connect_timeout", 60),
- autocommit=self.configuration.get("autocommit", True),
+ charset="utf8",
+ use_unicode=True,
+ connect_timeout=60,
)
ssl_options = self._get_ssl_parameters()
@@ -150,15 +134,18 @@ def _get_tables(self, schema):
query = """
SELECT col.table_schema as table_schema,
col.table_name as table_name,
- col.column_name as column_name
+ col.column_name as column_name,
+ col.column_type as column_type
FROM `information_schema`.`columns` col
- WHERE LOWER(col.table_schema) NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys');
+ WHERE col.table_schema NOT IN ('information_schema', 'performance_schema', 'mysql', 'sys');
"""
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
if row["table_schema"] != self.configuration["db"]:
@@ -167,12 +154,16 @@ def _get_tables(self, schema):
table_name = row["table_name"]
if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
+ schema[table_name] = {"name": table_name, "columns": [], "metadata": []}
schema[table_name]["columns"].append(row["column_name"])
+ schema[table_name]["metadata"].append(
+ {"name": row["column_name"], "type": row["column_type"]}
+ )
return list(schema.values())
+
def run_query(self, query, user):
ev = threading.Event()
thread_id = ""
@@ -182,7 +173,9 @@ def run_query(self, query, user):
try:
connection = self._connection()
thread_id = connection.thread_id()
- t = threading.Thread(target=self._run_query, args=(query, user, connection, r, ev))
+ t = threading.Thread(
+ target=self._run_query, args=(query, user, connection, r, ev)
+ )
t.start()
while not ev.wait(1):
pass
@@ -191,7 +184,7 @@ def run_query(self, query, user):
t.join()
raise
- return r.data, r.error
+ return r.json_data, r.error
def _run_query(self, query, user, connection, r, ev):
try:
@@ -209,21 +202,26 @@ def _run_query(self, query, user, connection, r, ev):
# TODO - very similar to pg.py
if desc is not None:
- columns = self.fetch_columns([(i[0], types_map.get(i[1], None)) for i in desc])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in data]
+ columns = self.fetch_columns(
+ [(i[0], types_map.get(i[1], None)) for i in desc]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row))
+ for row in data
+ ]
data = {"columns": columns, "rows": rows}
- r.data = data
+ r.json_data = json_dumps(data)
r.error = None
else:
- r.data = None
+ r.json_data = None
r.error = "No data was returned."
cursor.close()
except MySQLdb.Error as e:
if cursor:
cursor.close()
- r.data = None
+ r.json_data = None
r.error = e.args[1]
finally:
ev.set()
@@ -237,7 +235,7 @@ def _get_ssl_parameters(self):
ssl_params = {}
if self.configuration.get("use_ssl"):
- config_map = {"ssl_mode": "preferred", "ssl_cacert": "ca", "ssl_cert": "cert", "ssl_key": "key"}
+ config_map = {"ssl_cacert": "ca", "ssl_cert": "cert", "ssl_key": "key"}
for key, cfg in config_map.items():
val = self.configuration.get(key)
if val:
@@ -295,7 +293,9 @@ def configuration_schema(cls):
def _get_ssl_parameters(self):
if self.configuration.get("use_ssl"):
- ca_path = os.path.join(os.path.dirname(__file__), "./files/rds-combined-ca-bundle.pem")
+ ca_path = os.path.join(
+ os.path.dirname(__file__), "./files/rds-combined-ca-bundle.pem"
+ )
return {"ca": ca_path}
return None
diff --git a/redash/query_runner/nz.py b/redash/query_runner/nz.py
deleted file mode 100644
index 51f68ef1ed..0000000000
--- a/redash/query_runner/nz.py
+++ /dev/null
@@ -1,173 +0,0 @@
-import logging
-import traceback
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
-)
-
-logger = logging.getLogger(__name__)
-
-try:
- import nzpy
- import nzpy.core
-
- _enabled = True
- _nztypes = {
- nzpy.core.NzTypeInt1: TYPE_INTEGER,
- nzpy.core.NzTypeInt2: TYPE_INTEGER,
- nzpy.core.NzTypeInt: TYPE_INTEGER,
- nzpy.core.NzTypeInt8: TYPE_INTEGER,
- nzpy.core.NzTypeBool: TYPE_BOOLEAN,
- nzpy.core.NzTypeDate: TYPE_DATE,
- nzpy.core.NzTypeTimestamp: TYPE_DATETIME,
- nzpy.core.NzTypeDouble: TYPE_FLOAT,
- nzpy.core.NzTypeFloat: TYPE_FLOAT,
- nzpy.core.NzTypeChar: TYPE_STRING,
- nzpy.core.NzTypeNChar: TYPE_STRING,
- nzpy.core.NzTypeNVarChar: TYPE_STRING,
- nzpy.core.NzTypeVarChar: TYPE_STRING,
- nzpy.core.NzTypeVarFixedChar: TYPE_STRING,
- nzpy.core.NzTypeNumeric: TYPE_FLOAT,
- }
-
- _cat_types = {
- 16: TYPE_BOOLEAN, # boolean
- 17: TYPE_STRING, # bytea
- 19: TYPE_STRING, # name type
- 20: TYPE_INTEGER, # int8
- 21: TYPE_INTEGER, # int2
- 23: TYPE_INTEGER, # int4
- 25: TYPE_STRING, # TEXT type
- 26: TYPE_INTEGER, # oid
- 28: TYPE_INTEGER, # xid
- 700: TYPE_FLOAT, # float4
- 701: TYPE_FLOAT, # float8
- 705: TYPE_STRING, # unknown
- 829: TYPE_STRING, # MACADDR type
- 1042: TYPE_STRING, # CHAR type
- 1043: TYPE_STRING, # VARCHAR type
- 1082: TYPE_DATE, # date
- 1083: TYPE_DATETIME,
- 1114: TYPE_DATETIME, # timestamp w/ tz
- 1184: TYPE_DATETIME,
- 1700: TYPE_FLOAT, # NUMERIC
- 2275: TYPE_STRING, # cstring
- 2950: TYPE_STRING, # uuid
- }
-except ImportError:
- _enabled = False
- _nztypes = {}
- _cat_types = {}
-
-
-class Netezza(BaseSQLQueryRunner):
- noop_query = "SELECT 1"
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "user": {"type": "string"},
- "password": {"type": "string"},
- "host": {"type": "string", "default": "127.0.0.1"},
- "port": {"type": "number", "default": 5480},
- "database": {"type": "string", "title": "Database Name", "default": "system"},
- },
- "order": ["host", "port", "user", "password", "database"],
- "required": ["user", "password", "database"],
- "secret": ["password"],
- }
-
- @classmethod
- def type(cls):
- return "nz"
-
- def __init__(self, configuration):
- super().__init__(configuration)
- self._conn = None
-
- @property
- def connection(self):
- if self._conn is None:
- self._conn = nzpy.connect(
- host=self.configuration.get("host"),
- user=self.configuration.get("user"),
- password=self.configuration.get("password"),
- port=self.configuration.get("port"),
- database=self.configuration.get("database"),
- )
- return self._conn
-
- def get_schema(self, get_stats=False):
- qry = """
- select
- table_schema || '.' || table_name as table_name,
- column_name,
- data_type
- from
- columns
- where
- table_schema not in (^information_schema^, ^definition_schema^) and
- table_catalog = current_catalog;
- """
- schema = {}
- with self.connection.cursor() as cursor:
- cursor.execute(qry)
- for table_name, column_name, data_type in cursor:
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
- schema[table_name]["columns"].append({"name": column_name, "type": data_type})
- return list(schema.values())
-
- @classmethod
- def enabled(cls):
- global _enabled
- return _enabled
-
- def type_map(self, typid, func):
- global _nztypes, _cat_types
- typ = _nztypes.get(typid)
- if typ is None:
- return _cat_types.get(typid)
- # check for conflicts
- if typid == nzpy.core.NzTypeVarChar:
- return TYPE_BOOLEAN if "bool" in func.__name__ else typ
-
- if typid == nzpy.core.NzTypeInt2:
- return TYPE_STRING if "text" in func.__name__ else typ
-
- if typid in (nzpy.core.NzTypeVarFixedChar, nzpy.core.NzTypeVarBinary, nzpy.core.NzTypeNVarChar):
- return TYPE_INTEGER if "int" in func.__name__ else typ
- return typ
-
- def run_query(self, query, user):
- data, error = None, None
- try:
- with self.connection.cursor() as cursor:
- cursor.execute(query)
- if cursor.description is None:
- columns = {"columns": [], "rows": []}
- else:
- columns = self.fetch_columns(
- [
- (val[0], self.type_map(val[1], cursor.ps["row_desc"][i]["func"]))
- for i, val in enumerate(cursor.description)
- ]
- )
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
-
- data = {"columns": columns, "rows": rows}
- except Exception:
- error = traceback.format_exc()
- return data, error
-
-
-register(Netezza)
diff --git a/redash/query_runner/oracle.py b/redash/query_runner/oracle.py
index c721de9069..4fe41db067 100644
--- a/redash/query_runner/oracle.py
+++ b/redash/query_runner/oracle.py
@@ -1,33 +1,26 @@
-import logging
import os
+import logging
-from redash.query_runner import (
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
+from redash.utils import json_dumps, json_loads
+from redash.query_runner import *
try:
- import oracledb
+ import cx_Oracle
TYPES_MAP = {
- oracledb.DATETIME: TYPE_DATETIME,
- oracledb.CLOB: TYPE_STRING,
- oracledb.LOB: TYPE_STRING,
- oracledb.FIXED_CHAR: TYPE_STRING,
- oracledb.FIXED_NCHAR: TYPE_STRING,
- oracledb.INTERVAL: TYPE_DATETIME,
- oracledb.LONG_STRING: TYPE_STRING,
- oracledb.NATIVE_FLOAT: TYPE_FLOAT,
- oracledb.NCHAR: TYPE_STRING,
- oracledb.NUMBER: TYPE_FLOAT,
- oracledb.ROWID: TYPE_INTEGER,
- oracledb.STRING: TYPE_STRING,
- oracledb.TIMESTAMP: TYPE_DATETIME,
+ cx_Oracle.DATETIME: TYPE_DATETIME,
+ cx_Oracle.CLOB: TYPE_STRING,
+ cx_Oracle.LOB: TYPE_STRING,
+ cx_Oracle.FIXED_CHAR: TYPE_STRING,
+ cx_Oracle.FIXED_NCHAR: TYPE_STRING,
+ cx_Oracle.INTERVAL: TYPE_DATETIME,
+ cx_Oracle.LONG_STRING: TYPE_STRING,
+ cx_Oracle.NATIVE_FLOAT: TYPE_FLOAT,
+ cx_Oracle.NCHAR: TYPE_STRING,
+ cx_Oracle.NUMBER: TYPE_FLOAT,
+ cx_Oracle.ROWID: TYPE_INTEGER,
+ cx_Oracle.STRING: TYPE_STRING,
+ cx_Oracle.TIMESTAMP: TYPE_DATETIME,
}
ENABLED = True
@@ -38,14 +31,11 @@
class Oracle(BaseSQLQueryRunner):
- should_annotate_query = False
noop_query = "SELECT 1 FROM dual"
- limit_query = " FETCH NEXT 1000 ROWS ONLY"
- limit_keywords = ["ROW", "ROWS", "ONLY", "TIES"]
@classmethod
def get_col_type(cls, col_type, scale):
- if col_type == oracledb.NUMBER:
+ if col_type == cx_Oracle.NUMBER:
if scale is None:
return TYPE_INTEGER
if scale > 0:
@@ -65,13 +55,16 @@ def configuration_schema(cls):
"properties": {
"user": {"type": "string"},
"password": {"type": "string"},
- "host": {
- "type": "string",
- "title": "Host: To use a DSN Service Name instead, use the text string `_useservicename` in the host name field.",
- },
+ "host": {"type": "string"},
"port": {"type": "number"},
"servicename": {"type": "string", "title": "DSN Service Name"},
"encoding": {"type": "string"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["servicename", "user", "password", "host", "port"],
"extra_options": ["encoding"],
@@ -95,10 +88,12 @@ def _get_tables(self, schema):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
- if row["OWNER"] is not None:
+ if row["OWNER"] != None:
table_name = "{}.{}".format(row["OWNER"], row["TABLE_NAME"])
else:
table_name = row["TABLE_NAME"]
@@ -114,21 +109,21 @@ def _get_tables(self, schema):
def _convert_number(cls, value):
try:
return int(value)
- except BaseException:
+ except:
return value
@classmethod
def output_handler(cls, cursor, name, default_type, length, precision, scale):
- if default_type in (oracledb.CLOB, oracledb.LOB):
- return cursor.var(oracledb.LONG_STRING, 80000, cursor.arraysize)
+ if default_type in (cx_Oracle.CLOB, cx_Oracle.LOB):
+ return cursor.var(cx_Oracle.LONG_STRING, 80000, cursor.arraysize)
- if default_type in (oracledb.STRING, oracledb.FIXED_CHAR):
+ if default_type in (cx_Oracle.STRING, cx_Oracle.FIXED_CHAR):
return cursor.var(str, length, cursor.arraysize)
- if default_type == oracledb.NUMBER:
+ if default_type == cx_Oracle.NUMBER:
if scale <= 0:
return cursor.var(
- oracledb.STRING,
+ cx_Oracle.STRING,
255,
outconverter=Oracle._convert_number,
arraysize=cursor.arraysize,
@@ -138,17 +133,13 @@ def run_query(self, query, user):
if self.configuration.get("encoding"):
os.environ["NLS_LANG"] = self.configuration["encoding"]
- # To use a DSN Service Name instead, use the text string `_useservicename` in the host name field.
- if self.configuration["host"].lower() == "_useservicename":
- dsn = self.configuration["servicename"]
- else:
- dsn = oracledb.makedsn(
- self.configuration["host"],
- self.configuration["port"],
- service_name=self.configuration["servicename"],
- )
+ dsn = cx_Oracle.makedsn(
+ self.configuration["host"],
+ self.configuration["port"],
+ service_name=self.configuration["servicename"],
+ )
- connection = oracledb.connect(
+ connection = cx_Oracle.connect(
user=self.configuration["user"],
password=self.configuration["password"],
dsn=dsn,
@@ -161,21 +152,25 @@ def run_query(self, query, user):
cursor.execute(query)
rows_count = cursor.rowcount
if cursor.description is not None:
- columns = self.fetch_columns([(i[0], Oracle.get_col_type(i[1], i[5])) for i in cursor.description])
+ columns = self.fetch_columns(
+ [
+ (i[0], Oracle.get_col_type(i[1], i[5]))
+ for i in cursor.description
+ ]
+ )
rows = [dict(zip((c["name"] for c in columns), row)) for row in cursor]
data = {"columns": columns, "rows": rows}
error = None
+ json_data = json_dumps(data)
else:
columns = [{"name": "Row(s) Affected", "type": "TYPE_INTEGER"}]
rows = [{"Row(s) Affected": rows_count}]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
connection.commit()
- except oracledb.DatabaseError as err:
- (err_args,) = err.args
- line_number = query.count("\n", 0, err_args.offset) + 1
- column_number = err_args.offset - query.rfind("\n", 0, err_args.offset) - 1
- error = "Query failed at line {}, column {}: {}".format(str(line_number), str(column_number), str(err))
- data = None
+ except cx_Oracle.DatabaseError as err:
+ error = "Query failed. {}.".format(str(err))
+ json_data = None
except (KeyboardInterrupt, JobTimeoutException):
connection.cancel()
raise
@@ -183,7 +178,7 @@ def run_query(self, query, user):
os.environ.pop("NLS_LANG", None)
connection.close()
- return data, error
+ return json_data, error
register(Oracle)
diff --git a/redash/query_runner/pg.py b/redash/query_runner/pg.py
index c7ddef1eb7..6c2f3f0b81 100644
--- a/redash/query_runner/pg.py
+++ b/redash/query_runner/pg.py
@@ -1,6 +1,7 @@
-import logging
import os
+import logging
import select
+from contextlib import contextmanager
from base64 import b64decode
from tempfile import NamedTemporaryFile
from uuid import uuid4
@@ -8,18 +9,8 @@
import psycopg2
from psycopg2.extras import Range
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- InterruptException,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import *
+from redash.utils import JSONEncoder, json_dumps, json_loads
logger = logging.getLogger(__name__)
@@ -39,22 +30,30 @@
701: TYPE_FLOAT,
16: TYPE_BOOLEAN,
1082: TYPE_DATE,
- 1182: TYPE_DATE,
1114: TYPE_DATETIME,
1184: TYPE_DATETIME,
- 1115: TYPE_DATETIME,
- 1185: TYPE_DATETIME,
1014: TYPE_STRING,
1015: TYPE_STRING,
1008: TYPE_STRING,
1009: TYPE_STRING,
2951: TYPE_STRING,
- 1043: TYPE_STRING,
- 1002: TYPE_STRING,
- 1003: TYPE_STRING,
}
+class PostgreSQLJSONEncoder(JSONEncoder):
+ def default(self, o):
+ if isinstance(o, Range):
+ # From: https://github.com/psycopg/psycopg2/pull/779
+ if o._bounds is None:
+ return ""
+
+ items = [o._bounds[0], str(o._lower), ", ", str(o._upper), o._bounds[1]]
+
+ return "".join(items)
+
+ return super(PostgreSQLJSONEncoder, self).default(o)
+
+
def _wait(conn, timeout=None):
while 1:
try:
@@ -73,9 +72,9 @@ def _wait(conn, timeout=None):
def full_table_name(schema, name):
if "." in name:
- name = '"{}"'.format(name)
+ name = u'"{}"'.format(name)
- return "{}.{}".format(schema, name)
+ return u"{}.{}".format(schema, name)
def build_schema(query_result, schema):
@@ -103,19 +102,18 @@ def build_schema(query_result, schema):
table_name = row["table_name"]
if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
- column = row["column_name"]
- if row.get("data_type") is not None:
- column = {"name": row["column_name"], "type": row["data_type"]}
+ schema[table_name] = {"name": table_name, "columns": [], "metadata": []}
- schema[table_name]["columns"].append(column)
+ schema[table_name]["columns"].append(row["column_name"])
+ schema[table_name]["metadata"].append(
+ {"name": row["column_name"], "type": row["column_type"]}
+ )
def _create_cert_file(configuration, key, ssl_config):
- file_key = key + "File"
+ file_key = key + 'File'
if file_key in configuration:
- with NamedTemporaryFile(mode="w", delete=False) as cert_file:
+ with NamedTemporaryFile(mode='w', delete=False) as cert_file:
cert_bytes = b64decode(configuration[file_key])
cert_file.write(cert_bytes.decode("utf-8"))
@@ -124,22 +122,23 @@ def _create_cert_file(configuration, key, ssl_config):
def _cleanup_ssl_certs(ssl_config):
for k, v in ssl_config.items():
- if k != "sslmode":
+ if k != 'sslmode':
os.remove(v)
def _get_ssl_config(configuration):
- ssl_config = {"sslmode": configuration.get("sslmode", "prefer")}
+ ssl_config = {'sslmode': configuration.get('sslmode', 'prefer')}
- _create_cert_file(configuration, "sslrootcert", ssl_config)
- _create_cert_file(configuration, "sslcert", ssl_config)
- _create_cert_file(configuration, "sslkey", ssl_config)
+ _create_cert_file(configuration, 'sslrootcert', ssl_config)
+ _create_cert_file(configuration, 'sslcert', ssl_config)
+ _create_cert_file(configuration, 'sslkey', ssl_config)
return ssl_config
class PostgreSQL(BaseSQLQueryRunner):
noop_query = "SELECT 1"
+ sample_query = "SELECT * FROM {table} LIMIT 1"
@classmethod
def configuration_schema(cls):
@@ -164,42 +163,43 @@ def configuration_schema(cls):
{"value": "verify-full", "name": "Verify Full"},
],
},
- "sslrootcertFile": {"type": "string", "title": "SSL Root Certificate"},
- "sslcertFile": {"type": "string", "title": "SSL Client Certificate"},
- "sslkeyFile": {"type": "string", "title": "SSL Client Key"},
+ "sslrootcertFile": {
+ "type": "string",
+ "title": "SSL Root Certificate"
+ },
+ "sslcertFile": {
+ "type": "string",
+ "title": "SSL Client Certificate"
+ },
+ "sslkeyFile": {
+ "type": "string",
+ "title": "SSL Client Key"
+ },
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ "samples": {"type": "boolean", "title": "Show Data Samples"},
},
"order": ["host", "port", "user", "password"],
"required": ["dbname"],
- "secret": ["password", "sslrootcertFile", "sslcertFile", "sslkeyFile"],
- "extra_options": [
- "sslmode",
- "sslrootcertFile",
- "sslcertFile",
- "sslkeyFile",
- ],
+ "secret": ["password"],
+ "extra_options": ["sslmode", "sslrootcertFile", "sslcertFile", "sslkeyFile"],
}
@classmethod
def type(cls):
return "pg"
- @classmethod
- def custom_json_encoder(cls, dec, o):
- if isinstance(o, Range):
- # From: https://github.com/psycopg/psycopg2/pull/779
- if o._bounds is None:
- return ""
-
- items = [o._bounds[0], str(o._lower), ", ", str(o._upper), o._bounds[1]]
-
- return "".join(items)
- return None
-
def _get_definitions(self, schema, query):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
build_schema(results, schema)
@@ -222,7 +222,7 @@ def _get_tables(self, schema):
SELECT s.nspname as table_schema,
c.relname as table_name,
a.attname as column_name,
- null as data_type
+ a.atttypid::regtype::varchar as column_type
FROM pg_class c
JOIN pg_namespace s
ON c.relnamespace = s.oid
@@ -231,16 +231,16 @@ def _get_tables(self, schema):
ON a.attrelid = c.oid
AND a.attnum > 0
AND NOT a.attisdropped
+ JOIN pg_type t
+ ON a.atttypid = t.oid
WHERE c.relkind IN ('m', 'f', 'p')
- AND has_table_privilege(s.nspname || '.' || c.relname, 'select')
- AND has_schema_privilege(s.nspname, 'usage')
UNION
SELECT table_schema,
table_name,
column_name,
- data_type
+ data_type as column_type
FROM information_schema.columns
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
"""
@@ -258,7 +258,7 @@ def _get_connection(self):
port=self.configuration.get("port"),
dbname=self.configuration.get("dbname"),
async_=True,
- **self.ssl_config,
+ **self.ssl_config
)
return connection
@@ -274,20 +274,26 @@ def run_query(self, query, user):
_wait(connection)
if cursor.description is not None:
- columns = self.fetch_columns([(i[0], types_map.get(i[1], None)) for i in cursor.description])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
+ columns = self.fetch_columns(
+ [(i[0], types_map.get(i[1], None)) for i in cursor.description]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row))
+ for row in cursor
+ ]
data = {"columns": columns, "rows": rows}
error = None
+ json_data = json_dumps(data, ignore_nan=True, cls=PostgreSQLJSONEncoder)
else:
error = "Query completed but it returned no data."
- data = None
- except (select.error, OSError):
+ json_data = None
+ except (select.error, OSError) as e:
error = "Query interrupted. Please retry."
- data = None
+ json_data = None
except psycopg2.DatabaseError as e:
error = str(e)
- data = None
+ json_data = None
except (KeyboardInterrupt, InterruptException, JobTimeoutException):
connection.cancel()
raise
@@ -295,10 +301,12 @@ def run_query(self, query, user):
connection.close()
_cleanup_ssl_certs(self.ssl_config)
- return data, error
+ return json_data, error
class Redshift(PostgreSQL):
+ sample_query = "SELECT * FROM {table} LIMIT 1"
+
@classmethod
def type(cls):
return "redshift"
@@ -310,7 +318,9 @@ def name(cls):
def _get_connection(self):
self.ssl_config = {}
- sslrootcert_path = os.path.join(os.path.dirname(__file__), "./files/redshift-ca-bundle.crt")
+ sslrootcert_path = os.path.join(
+ os.path.dirname(__file__), "./files/redshift-ca-bundle.crt"
+ )
connection = psycopg2.connect(
user=self.configuration.get("user"),
@@ -346,6 +356,13 @@ def configuration_schema(cls):
"title": "Query Group for Scheduled Queries",
"default": "default",
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ "samples": {"type": "boolean", "title": "Show Data Samples"},
},
"order": [
"host",
@@ -388,13 +405,13 @@ def _get_tables(self, schema):
SELECT DISTINCT table_name,
table_schema,
column_name,
- data_type,
+ data_type AS column_type,
ordinal_position AS pos
FROM svv_columns
WHERE table_schema NOT IN ('pg_internal','pg_catalog','information_schema')
AND table_schema NOT LIKE 'pg_temp_%'
)
- SELECT table_name, table_schema, column_name, data_type
+ SELECT table_name, table_schema, column_name, column_type
FROM tables
WHERE
HAS_SCHEMA_PRIVILEGE(table_schema, 'USAGE') AND
@@ -409,8 +426,8 @@ def _get_tables(self, schema):
return list(schema.values())
-
class RedshiftIAM(Redshift):
+
@classmethod
def type(cls):
return "redshift_iam"
@@ -442,10 +459,7 @@ def configuration_schema(cls):
"rolename": {"type": "string", "title": "IAM Role Name"},
"aws_region": {"type": "string", "title": "AWS Region"},
"aws_access_key_id": {"type": "string", "title": "AWS Access Key ID"},
- "aws_secret_access_key": {
- "type": "string",
- "title": "AWS Secret Access Key",
- },
+ "aws_secret_access_key": {"type": "string", "title": "AWS Secret Access Key"},
"clusterid": {"type": "string", "title": "Redshift Cluster ID"},
"user": {"type": "string"},
"host": {"type": "string"},
@@ -482,47 +496,46 @@ def configuration_schema(cls):
}
def _get_connection(self):
- self.ssl_config = {}
- sslrootcert_path = os.path.join(os.path.dirname(__file__), "./files/redshift-ca-bundle.crt")
+ sslrootcert_path = os.path.join(
+ os.path.dirname(__file__), "./files/redshift-ca-bundle.crt"
+ )
login_method = self._login_method_selection()
+
if login_method == "KEYS":
- client = boto3.client(
- "redshift",
- region_name=self.configuration.get("aws_region"),
- aws_access_key_id=self.configuration.get("aws_access_key_id"),
- aws_secret_access_key=self.configuration.get("aws_secret_access_key"),
- )
+ client = boto3.client("redshift",
+ region_name=self.configuration.get("aws_region"),
+ aws_access_key_id=self.configuration.get("aws_access_key_id"),
+ aws_secret_access_key=self.configuration.get("aws_secret_access_key"))
elif login_method == "ROLE":
- client = boto3.client("redshift", region_name=self.configuration.get("aws_region"))
+ client = boto3.client("redshift",
+ region_name=self.configuration.get("aws_region"))
else:
if login_method == "ASSUME_ROLE_KEYS":
- assume_client = client = boto3.client(
- "sts",
- region_name=self.configuration.get("aws_region"),
- aws_access_key_id=self.configuration.get("aws_access_key_id"),
- aws_secret_access_key=self.configuration.get("aws_secret_access_key"),
- )
+ assume_client = client = boto3.client('sts',
+ region_name=self.configuration.get("aws_region"),
+ aws_access_key_id=self.configuration.get("aws_access_key_id"),
+ aws_secret_access_key=self.configuration.get(
+ "aws_secret_access_key"))
else:
- assume_client = client = boto3.client("sts", region_name=self.configuration.get("aws_region"))
+ assume_client = client = boto3.client('sts',
+ region_name=self.configuration.get("aws_region"))
role_session = f"redash_{uuid4().hex}"
session_keys = assume_client.assume_role(
- RoleArn=self.configuration.get("rolename"), RoleSessionName=role_session
- )["Credentials"]
- client = boto3.client(
- "redshift",
- region_name=self.configuration.get("aws_region"),
- aws_access_key_id=session_keys["AccessKeyId"],
- aws_secret_access_key=session_keys["SecretAccessKey"],
- aws_session_token=session_keys["SessionToken"],
- )
+ RoleArn=self.configuration.get("rolename"),
+ RoleSessionName=role_session)["Credentials"]
+ client = boto3.client("redshift",
+ region_name=self.configuration.get("aws_region"),
+ aws_access_key_id=session_keys["AccessKeyId"],
+ aws_secret_access_key=session_keys["SecretAccessKey"],
+ aws_session_token=session_keys["SessionToken"]
+ )
credentials = client.get_cluster_credentials(
DbUser=self.configuration.get("user"),
DbName=self.configuration.get("dbname"),
- ClusterIdentifier=self.configuration.get("clusterid"),
- )
+ ClusterIdentifier=self.configuration.get("clusterid"))
db_user = credentials["DbUser"]
db_password = credentials["DbPassword"]
connection = psycopg2.connect(
diff --git a/redash/query_runner/phoenix.py b/redash/query_runner/phoenix.py
index e76b2f7d3c..c3e2f242dc 100644
--- a/redash/query_runner/phoenix.py
+++ b/redash/query_runner/phoenix.py
@@ -1,20 +1,13 @@
-import logging
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+import logging
logger = logging.getLogger(__name__)
try:
import phoenixdb
- from phoenixdb.errors import Error
+ from phoenixdb.errors import *
enabled = True
@@ -79,7 +72,9 @@ def get_schema(self, get_stats=False):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
table_name = "{}.{}".format(row["TABLE_SCHEM"], row["TABLE_NAME"])
@@ -92,26 +87,36 @@ def get_schema(self, get_stats=False):
return list(schema.values())
def run_query(self, query, user):
- connection = phoenixdb.connect(url=self.configuration.get("url", ""), autocommit=True)
+ connection = phoenixdb.connect(
+ url=self.configuration.get("url", ""), autocommit=True
+ )
cursor = connection.cursor()
try:
cursor.execute(query)
- column_tuples = [(i[0], TYPES_MAPPING.get(i[1], None)) for i in cursor.description]
+ column_tuples = [
+ (i[0], TYPES_MAPPING.get(i[1], None)) for i in cursor.description
+ ]
columns = self.fetch_columns(column_tuples)
- rows = [dict(zip(([column["name"] for column in columns]), r)) for i, r in enumerate(cursor.fetchall())]
+ rows = [
+ dict(zip(([column["name"] for column in columns]), r))
+ for i, r in enumerate(cursor.fetchall())
+ ]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
cursor.close()
except Error as e:
- data = None
- error = "code: {}, sql state:{}, message: {}".format(e.code, e.sqlstate, str(e))
+ json_data = None
+ error = "code: {}, sql state:{}, message: {}".format(
+ e.code, e.sqlstate, str(e)
+ )
finally:
if connection:
connection.close()
- return data, error
+ return json_data, error
register(Phoenix)
diff --git a/redash/query_runner/pinot.py b/redash/query_runner/pinot.py
deleted file mode 100644
index 0bcdcef9ed..0000000000
--- a/redash/query_runner/pinot.py
+++ /dev/null
@@ -1,143 +0,0 @@
-try:
- import pinotdb
-
- enabled = True
-except ImportError:
- enabled = False
-
-import logging
-
-import requests
-from requests.auth import HTTPBasicAuth
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-
-logger = logging.getLogger(__name__)
-
-PINOT_TYPES_MAPPING = {
- "BOOLEAN": TYPE_BOOLEAN,
- "INT": TYPE_INTEGER,
- "LONG": TYPE_INTEGER,
- "FLOAT": TYPE_FLOAT,
- "DOUBLE": TYPE_FLOAT,
- "STRING": TYPE_STRING,
- "BYTES": TYPE_STRING,
- "JSON": TYPE_STRING,
- "TIMESTAMP": TYPE_DATETIME,
-}
-
-
-class Pinot(BaseQueryRunner):
- noop_query = "SELECT 1"
- username = None
- password = None
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "brokerHost": {"type": "string", "default": ""},
- "brokerPort": {"type": "number", "default": 8099},
- "brokerScheme": {"type": "string", "default": "http"},
- "controllerURI": {"type": "string", "default": ""},
- "username": {"type": "string"},
- "password": {"type": "string"},
- },
- "order": ["brokerScheme", "brokerHost", "brokerPort", "controllerURI", "username", "password"],
- "required": ["brokerHost", "controllerURI"],
- "secret": ["password"],
- }
-
- @classmethod
- def enabled(cls):
- return enabled
-
- def __init__(self, configuration):
- super(Pinot, self).__init__(configuration)
- self.controller_uri = self.configuration.get("controllerURI")
- self.username = self.configuration.get("username") or None
- self.password = self.configuration.get("password") or None
-
- def run_query(self, query, user):
- logger.debug("Running query %s with username: %s", query, self.username)
- connection = pinotdb.connect(
- host=self.configuration["brokerHost"],
- port=self.configuration["brokerPort"],
- path="/query/sql",
- scheme=(self.configuration.get("brokerScheme") or "http"),
- verify_ssl=False,
- username=self.username,
- password=self.password,
- )
-
- cursor = connection.cursor()
-
- try:
- cursor.execute(query)
- logger.debug("cursor.schema = %s", cursor.schema)
- columns = self.fetch_columns(
- [(i["name"], PINOT_TYPES_MAPPING.get(i["type"], None)) for i in cursor.schema]
- )
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
-
- data = {"columns": columns, "rows": rows}
- error = None
- logger.debug("Pinot execute query [%s]", query)
- finally:
- connection.close()
-
- return data, error
-
- def get_schema(self, get_stats=False):
- schema = {}
- for schema_name in self.get_schema_names():
- for table_name in self.get_table_names():
- schema_table_name = "{}.{}".format(schema_name, table_name)
- if table_name not in schema:
- schema[schema_table_name] = {"name": schema_table_name, "columns": []}
- table_schema = self.get_pinot_table_schema(table_name)
-
- for column in (
- table_schema.get("dimensionFieldSpecs", [])
- + table_schema.get("metricFieldSpecs", [])
- + table_schema.get("dateTimeFieldSpecs", [])
- ):
- c = {
- "name": column["name"],
- "type": PINOT_TYPES_MAPPING[column["dataType"]],
- }
- schema[schema_table_name]["columns"].append(c)
- return list(schema.values())
-
- def get_schema_names(self):
- return ["default"]
-
- def get_pinot_table_schema(self, pinot_table_name):
- return self.get_metadata_from_controller("/tables/" + pinot_table_name + "/schema")
-
- def get_table_names(self):
- return self.get_metadata_from_controller("/tables")["tables"]
-
- def get_metadata_from_controller(self, path):
- url = self.controller_uri + path
- r = requests.get(url, headers={"Accept": "application/json"}, auth=HTTPBasicAuth(self.username, self.password))
- try:
- result = r.json()
- logger.debug("get_metadata_from_controller from path %s", path)
- except ValueError as e:
- raise pinotdb.exceptions.DatabaseError(
- f"Got invalid json response from {self.controller_uri}:{path}: {r.text}"
- ) from e
- return result
-
-
-register(Pinot)
diff --git a/redash/query_runner/presto.py b/redash/query_runner/presto.py
index e6e7bc785e..d16d0167b1 100644
--- a/redash/query_runner/presto.py
+++ b/redash/query_runner/presto.py
@@ -1,16 +1,8 @@
-import logging
+from collections import defaultdict
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- InterruptException,
- JobTimeoutException,
- register,
-)
+import logging
logger = logging.getLogger(__name__)
@@ -41,6 +33,7 @@
class Presto(BaseQueryRunner):
noop_query = "SHOW TABLES"
+ sample_query = "SELECT * FROM {table} TABLESAMPLE SYSTEM (1) LIMIT 1"
@classmethod
def configuration_schema(cls):
@@ -54,6 +47,13 @@ def configuration_schema(cls):
"catalog": {"type": "string"},
"username": {"type": "string"},
"password": {"type": "string"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ "samples": {"type": "boolean", "title": "Show Data Samples"},
},
"order": [
"host",
@@ -78,7 +78,7 @@ def type(cls):
def get_schema(self, get_stats=False):
schema = {}
query = """
- SELECT table_schema, table_name, column_name
+ SELECT table_schema, table_name, column_name, data_type AS column_type
FROM information_schema.columns
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
"""
@@ -86,15 +86,20 @@ def get_schema(self, get_stats=False):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
table_name = "{}.{}".format(row["table_schema"], row["table_name"])
if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
+ schema[table_name] = {"name": table_name, "columns": [], "metadata": []}
schema[table_name]["columns"].append(row["column_name"])
+ schema[table_name]["metadata"].append(
+ {"name": row["column_name"], "type": row["column_type"],}
+ )
return list(schema.values())
@@ -113,16 +118,24 @@ def run_query(self, query, user):
try:
cursor.execute(query)
- column_tuples = [(i[0], PRESTO_TYPES_MAPPING.get(i[1], None)) for i in cursor.description]
+ column_tuples = [
+ (i[0], PRESTO_TYPES_MAPPING.get(i[1], None)) for i in cursor.description
+ ]
columns = self.fetch_columns(column_tuples)
- rows = [dict(zip(([column["name"] for column in columns]), r)) for i, r in enumerate(cursor.fetchall())]
+ rows = [
+ dict(zip(([column["name"] for column in columns]), r))
+ for i, r in enumerate(cursor.fetchall())
+ ]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
except DatabaseError as db:
- data = None
+ json_data = None
default_message = "Unspecified DatabaseError: {0}".format(str(db))
if isinstance(db.args[0], dict):
- message = db.args[0].get("failureInfo", {"message", None}).get("message")
+ message = db.args[0].get("failureInfo", {"message", None}).get(
+ "message"
+ )
else:
message = None
error = default_message if message is None else message
@@ -130,7 +143,7 @@ def run_query(self, query, user):
cursor.cancel()
raise
- return data, error
+ return json_data, error
register(Presto)
diff --git a/redash/query_runner/prometheus.py b/redash/query_runner/prometheus.py
index 34b5aa94d5..ad6c3348ab 100644
--- a/redash/query_runner/prometheus.py
+++ b/redash/query_runner/prometheus.py
@@ -1,19 +1,10 @@
-import os
+import requests
import time
-from base64 import b64decode
from datetime import datetime
-from tempfile import NamedTemporaryFile
-from urllib.parse import parse_qs
-
-import requests
from dateutil import parser
-
-from redash.query_runner import (
- TYPE_DATETIME,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from urllib.parse import parse_qs
+from redash.query_runner import BaseQueryRunner, register, TYPE_DATETIME, TYPE_STRING
+from redash.utils import json_dumps
def get_instant_rows(metrics_data):
@@ -57,7 +48,7 @@ def convert_query_range(payload):
continue
value = payload[key][0]
- if isinstance(value, str):
+ if type(value) is str:
# Don't convert timestamp string
try:
int(value)
@@ -75,107 +66,29 @@ def convert_query_range(payload):
class Prometheus(BaseQueryRunner):
should_annotate_query = False
- def _get_datetime_now(self):
- return datetime.now()
-
- def _get_prometheus_kwargs(self):
- ca_cert_file = self._create_cert_file("ca_cert_File")
- if ca_cert_file is not None:
- verify = ca_cert_file
- else:
- verify = self.configuration.get("verify_ssl", True)
-
- cert_file = self._create_cert_file("cert_File")
- cert_key_file = self._create_cert_file("cert_key_File")
- if cert_file is not None and cert_key_file is not None:
- cert = (cert_file, cert_key_file)
- else:
- cert = ()
-
- return {
- "verify": verify,
- "cert": cert,
- }
-
- def _create_cert_file(self, key):
- cert_file_name = None
-
- if self.configuration.get(key, None) is not None:
- with NamedTemporaryFile(mode="w", delete=False) as cert_file:
- cert_bytes = b64decode(self.configuration[key])
- cert_file.write(cert_bytes.decode("utf-8"))
- cert_file_name = cert_file.name
-
- return cert_file_name
-
- def _cleanup_cert_files(self, promehteus_kwargs):
- verify = promehteus_kwargs.get("verify", True)
- if isinstance(verify, str) and os.path.exists(verify):
- os.remove(verify)
-
- cert = promehteus_kwargs.get("cert", ())
- for cert_file in cert:
- if os.path.exists(cert_file):
- os.remove(cert_file)
-
@classmethod
def configuration_schema(cls):
- # files has to end with "File" in name
return {
"type": "object",
- "properties": {
- "url": {"type": "string", "title": "Prometheus API URL"},
- "verify_ssl": {
- "type": "boolean",
- "title": "Verify SSL (Ignored, if SSL Root Certificate is given)",
- "default": True,
- },
- "cert_File": {"type": "string", "title": "SSL Client Certificate", "default": None},
- "cert_key_File": {"type": "string", "title": "SSL Client Key", "default": None},
- "ca_cert_File": {"type": "string", "title": "SSL Root Certificate", "default": None},
- },
+ "properties": {"url": {"type": "string", "title": "Prometheus API URL"}},
"required": ["url"],
- "secret": ["cert_File", "cert_key_File", "ca_cert_File"],
- "extra_options": ["verify_ssl", "cert_File", "cert_key_File", "ca_cert_File"],
}
def test_connection(self):
- result = False
- promehteus_kwargs = {}
- try:
- promehteus_kwargs = self._get_prometheus_kwargs()
- resp = requests.get(self.configuration.get("url", None), **promehteus_kwargs)
- result = resp.ok
- except Exception:
- raise
- finally:
- self._cleanup_cert_files(promehteus_kwargs)
-
- return result
+ resp = requests.get(self.configuration.get("url", None))
+ return resp.ok
def get_schema(self, get_stats=False):
- schema = []
- promehteus_kwargs = {}
- try:
- base_url = self.configuration["url"]
- metrics_path = "/api/v1/label/__name__/values"
- promehteus_kwargs = self._get_prometheus_kwargs()
-
- response = requests.get(base_url + metrics_path, **promehteus_kwargs)
-
- response.raise_for_status()
- data = response.json()["data"]
-
- schema = {}
- for name in data:
- schema[name] = {"name": name, "columns": []}
- schema = list(schema.values())
- except Exception:
- raise
- finally:
- self._cleanup_cert_files(promehteus_kwargs)
+ base_url = self.configuration["url"]
+ metrics_path = "/api/v1/label/__name__/values"
+ response = requests.get(base_url + metrics_path)
+ response.raise_for_status()
+ data = response.json()["data"]
- return schema
+ schema = {}
+ for name in data:
+ schema[name] = {"name": name, "columns": []}
+ return list(schema.values())
def run_query(self, query, user):
"""
@@ -200,29 +113,30 @@ def run_query(self, query, user):
{"friendly_name": "timestamp", "type": TYPE_DATETIME, "name": "timestamp"},
{"friendly_name": "value", "type": TYPE_STRING, "name": "value"},
]
- promehteus_kwargs = {}
try:
error = None
query = query.strip()
# for backward compatibility
- query = "query={}".format(query) if not query.startswith("query=") else query
+ query = (
+ "query={}".format(query) if not query.startswith("query=") else query
+ )
payload = parse_qs(query)
query_type = "query_range" if "step" in payload.keys() else "query"
# for the range of until now
- if query_type == "query_range" and ("end" not in payload.keys() or "now" in payload["end"]):
- date_now = self._get_datetime_now()
+ if query_type == "query_range" and (
+ "end" not in payload.keys() or "now" in payload["end"]
+ ):
+ date_now = datetime.now()
payload.update({"end": [date_now]})
convert_query_range(payload)
api_endpoint = base_url + "/api/v1/{}".format(query_type)
- promehteus_kwargs = self._get_prometheus_kwargs()
-
- response = requests.get(api_endpoint, params=payload, **promehteus_kwargs)
+ response = requests.get(api_endpoint, params=payload)
response.raise_for_status()
metrics = response.json()["data"]["result"]
@@ -246,16 +160,12 @@ def run_query(self, query, user):
else:
rows = get_instant_rows(metrics)
- data = {"rows": rows, "columns": columns}
+ json_data = json_dumps({"rows": rows, "columns": columns})
except requests.RequestException as e:
return None, str(e)
- except Exception:
- raise
- finally:
- self._cleanup_cert_files(promehteus_kwargs)
- return data, error
+ return json_data, error
register(Prometheus)
diff --git a/redash/query_runner/python.py b/redash/query_runner/python.py
index 27f1146ae5..41ea383574 100644
--- a/redash/query_runner/python.py
+++ b/redash/query_runner/python.py
@@ -3,42 +3,17 @@
import logging
import sys
-from RestrictedPython import compile_restricted
-from RestrictedPython.Guards import (
- guarded_iter_unpack_sequence,
- guarded_unpack_sequence,
- safe_builtins,
-)
-from RestrictedPython.transformer import IOPERATOR_TO_STR
-
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
from redash import models
-from redash.query_runner import (
- SUPPORTED_COLUMN_TYPES,
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
-from redash.utils.pandas import pandas_installed
-
-if pandas_installed:
- import pandas as pd
-
- from redash.utils.pandas import pandas_to_result
-
- enabled = True
-else:
- enabled = False
+from RestrictedPython import compile_restricted
+from RestrictedPython.Guards import safe_builtins, guarded_iter_unpack_sequence, guarded_unpack_sequence
logger = logging.getLogger(__name__)
-class CustomPrint:
+class CustomPrint(object):
"""CustomPrint redirect "print" calls to be sent as "log" on the result object."""
def __init__(self):
@@ -48,7 +23,9 @@ def __init__(self):
def write(self, text):
if self.enabled:
if text and text.strip():
- log_line = "[{0}] {1}".format(datetime.datetime.utcnow().isoformat(), text)
+ log_line = "[{0}] {1}".format(
+ datetime.datetime.utcnow().isoformat(), text
+ )
self.lines.append(log_line)
def enable(self):
@@ -68,31 +45,31 @@ class Python(BaseQueryRunner):
should_annotate_query = False
safe_builtins = (
- "abs",
- "all",
+ "sorted",
+ "reversed",
+ "map",
"any",
- "bool",
- "complex",
- "dict",
- "divmod",
- "enumerate",
+ "all",
+ "slice",
"filter",
- "float",
- "int",
"len",
- "list",
- "map",
- "max",
- "min",
"next",
- "reversed",
+ "enumerate",
+ "sum",
+ "abs",
+ "min",
+ "max",
"round",
- "set",
- "slice",
- "sorted",
+ "divmod",
"str",
- "sum",
+ "int",
+ "float",
+ "complex",
"tuple",
+ "set",
+ "list",
+ "dict",
+ "bool",
)
@classmethod
@@ -106,6 +83,12 @@ def configuration_schema(cls):
},
"additionalModulesPaths": {"type": "string"},
"additionalBuiltins": {"type": "string"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
}
@@ -135,7 +118,7 @@ def __init__(self, configuration):
if self.configuration.get("additionalBuiltins", None):
for b in self.configuration["additionalBuiltins"].split(","):
if b not in self.safe_builtins:
- self.safe_builtins += (b,)
+ self.safe_builtins += (b, )
def custom_import(self, name, globals=None, locals=None, fromlist=(), level=0):
if name in self._allowed_modules:
@@ -148,7 +131,9 @@ def custom_import(self, name, globals=None, locals=None, fromlist=(), level=0):
return m
- raise Exception("'{0}' is not configured as a supported import module".format(name))
+ raise Exception(
+ "'{0}' is not configured as a supported import module".format(name)
+ )
@staticmethod
def custom_write(obj):
@@ -166,14 +151,6 @@ def custom_get_item(obj, key):
def custom_get_iter(obj):
return iter(obj)
- @staticmethod
- def custom_inplacevar(op, x, y):
- if op not in IOPERATOR_TO_STR.values():
- raise Exception("'{} is not supported inplace variable'".format(op))
- glb = {"x": x, "y": y}
- exec("x" + op + "y", glb)
- return glb["x"]
-
@staticmethod
def add_result_column(result, column_name, friendly_name, column_type):
"""Helper function to add columns inside a Python script running in Redash in an easier way
@@ -190,7 +167,9 @@ def add_result_column(result, column_name, friendly_name, column_type):
if "columns" not in result:
result["columns"] = []
- result["columns"].append({"name": column_name, "friendly_name": friendly_name, "type": column_type})
+ result["columns"].append(
+ {"name": column_name, "friendly_name": friendly_name, "type": column_type}
+ )
@staticmethod
def add_result_row(result, values):
@@ -206,7 +185,7 @@ def add_result_row(result, values):
result["rows"].append(values)
@staticmethod
- def execute_query(data_source_name_or_id, query, result_type=None):
+ def execute_query(data_source_name_or_id, query):
"""Run query from specific data source.
Parameters:
@@ -214,7 +193,7 @@ def execute_query(data_source_name_or_id, query, result_type=None):
:query string: Query to run
"""
try:
- if isinstance(data_source_name_or_id, int):
+ if type(data_source_name_or_id) == int:
data_source = models.DataSource.get_by_id(data_source_name_or_id)
else:
data_source = models.DataSource.get_by_name(data_source_name_or_id)
@@ -227,12 +206,7 @@ def execute_query(data_source_name_or_id, query, result_type=None):
raise Exception(error)
# TODO: allow avoiding the JSON dumps/loads in same process
- query_result = data
-
- if result_type == "dataframe" and pandas_installed:
- return pd.DataFrame(query_result["rows"])
-
- return query_result
+ return json_loads(data)
@staticmethod
def get_source_schema(data_source_name_or_id):
@@ -242,7 +216,7 @@ def get_source_schema(data_source_name_or_id):
:return:
"""
try:
- if isinstance(data_source_name_or_id, int):
+ if type(data_source_name_or_id) == int:
data_source = models.DataSource.get_by_id(data_source_name_or_id)
else:
data_source = models.DataSource.get_by_name(data_source_name_or_id)
@@ -271,38 +245,12 @@ def get_query_result(query_id):
return query.latest_query_data.data
- def dataframe_to_result(self, result, df):
- converted_result = pandas_to_result(df)
-
- result["rows"] = converted_result["rows"]
- for column in converted_result["columns"]:
- self.add_result_column(result, column["name"], column["friendly_name"], column["type"])
-
def get_current_user(self):
return self._current_user.to_dict()
def test_connection(self):
pass
- def validate_result(self, result):
- """Validate the result after executing the query.
-
- Parameters:
- :result dict: The result dict.
- """
- if not result:
- raise Exception("local variable `result` should not be empty.")
- if not isinstance(result, dict):
- raise Exception("local variable `result` should be of type `dict`.")
- if "rows" not in result:
- raise Exception("Missing `rows` field in `result` dict.")
- if "columns" not in result:
- raise Exception("Missing `columns` field in `result` dict.")
- if not isinstance(result["rows"], list):
- raise Exception("`rows` field should be of type `list`.")
- if not isinstance(result["columns"], list):
- raise Exception("`columns` field should be of type `list`.")
-
def run_query(self, query, user):
self._current_user = user
@@ -323,7 +271,6 @@ def run_query(self, query, user):
builtins["_print_"] = self._custom_print
builtins["_unpack_sequence_"] = guarded_unpack_sequence
builtins["_iter_unpack_sequence_"] = guarded_iter_unpack_sequence
- builtins["_inplacevar_"] = self.custom_inplacevar
# Layer in our own additional set of builtins that we have
# considered safe.
@@ -336,8 +283,6 @@ def run_query(self, query, user):
restricted_globals["get_current_user"] = self.get_current_user
restricted_globals["execute_query"] = self.execute_query
restricted_globals["add_result_column"] = self.add_result_column
- if pandas_installed:
- restricted_globals["dataframe_to_result"] = self.dataframe_to_result
restricted_globals["add_result_row"] = self.add_result_row
restricted_globals["disable_print_log"] = self._custom_print.disable
restricted_globals["enable_print_log"] = self._custom_print.enable
@@ -356,14 +301,14 @@ def run_query(self, query, user):
exec(code, restricted_globals, self._script_locals)
- data = self._script_locals["result"]
- self.validate_result(data)
- data["log"] = self._custom_print.lines
+ result = self._script_locals["result"]
+ result["log"] = self._custom_print.lines
+ json_data = json_dumps(result)
except Exception as e:
error = str(type(e)) + " " + str(e)
- data = None
+ json_data = None
- return data, error
+ return json_data, error
register(Python)
diff --git a/redash/query_runner/qubole.py b/redash/query_runner/qubole.py
new file mode 100644
index 0000000000..7e91fb36f8
--- /dev/null
+++ b/redash/query_runner/qubole.py
@@ -0,0 +1,181 @@
+import time
+import requests
+import logging
+from io import StringIO
+
+from redash.query_runner import (
+ BaseQueryRunner,
+ register,
+ JobTimeoutException,
+ TYPE_STRING,
+)
+from redash.utils import json_dumps
+
+try:
+ import qds_sdk
+ from qds_sdk.qubole import Qubole as qbol
+ from qds_sdk.commands import Command, HiveCommand
+ from qds_sdk.commands import SqlCommand, PrestoCommand
+
+ enabled = True
+except ImportError:
+ enabled = False
+
+
+class Qubole(BaseQueryRunner):
+ should_annotate_query = False
+
+ @classmethod
+ def configuration_schema(cls):
+ return {
+ "type": "object",
+ "properties": {
+ "query_type": {
+ "type": "string",
+ "title": "Query Type (quantum / presto / hive)",
+ "default": "hive",
+ },
+ "endpoint": {
+ "type": "string",
+ "title": "API Endpoint",
+ "default": "https://api.qubole.com",
+ },
+ "token": {"type": "string", "title": "Auth Token"},
+ "cluster": {
+ "type": "string",
+ "title": "Cluster Label",
+ "default": "default",
+ },
+ },
+ "order": ["query_type", "endpoint", "token", "cluster"],
+ "required": ["endpoint", "token"],
+ "secret": ["token"],
+ }
+
+ @classmethod
+ def type(cls):
+ return "qubole"
+
+ @classmethod
+ def name(cls):
+ return "Qubole"
+
+ @classmethod
+ def enabled(cls):
+ return enabled
+
+ def test_connection(self):
+ headers = self._get_header()
+ r = requests.head(
+ "%s/api/latest/users" % self.configuration.get("endpoint"), headers=headers
+ )
+ r.status_code == 200
+
+ def run_query(self, query, user):
+ qbol.configure(
+ api_token=self.configuration.get("token"),
+ api_url="%s/api" % self.configuration.get("endpoint"),
+ )
+
+ try:
+ query_type = self.configuration.get("query_type", "hive")
+
+ if query_type == "quantum":
+ cmd = SqlCommand.create(query=query)
+ elif query_type == "hive":
+ cmd = HiveCommand.create(
+ query=query, label=self.configuration.get("cluster")
+ )
+ elif query_type == "presto":
+ cmd = PrestoCommand.create(
+ query=query, label=self.configuration.get("cluster")
+ )
+ else:
+ raise Exception(
+ "Invalid Query Type:%s.\
+ It must be : hive / presto / quantum."
+ % self.configuration.get("query_type")
+ )
+
+ logging.info(
+ "Qubole command created with Id: %s and Status: %s", cmd.id, cmd.status
+ )
+
+ while not Command.is_done(cmd.status):
+ time.sleep(qbol.poll_interval)
+ cmd = Command.find(cmd.id)
+ logging.info("Qubole command Id: %s and Status: %s", cmd.id, cmd.status)
+
+ rows = []
+ columns = []
+ error = None
+
+ if cmd.status == "done":
+ fp = StringIO()
+ cmd.get_results(
+ fp=fp,
+ inline=True,
+ delim="\t",
+ fetch=False,
+ qlog=None,
+ arguments=["true"],
+ )
+
+ results = fp.getvalue()
+ fp.close()
+
+ data = results.split("\r\n")
+ columns = self.fetch_columns(
+ [(i, TYPE_STRING) for i in data.pop(0).split("\t")]
+ )
+ rows = [
+ dict(zip((column["name"] for column in columns), row.split("\t")))
+ for row in data
+ ]
+
+ json_data = json_dumps({"columns": columns, "rows": rows})
+ except (KeyboardInterrupt, JobTimeoutException):
+ logging.info("Sending KILL signal to Qubole Command Id: %s", cmd.id)
+ cmd.cancel()
+ raise
+
+ return json_data, error
+
+ def get_schema(self, get_stats=False):
+ schemas = {}
+ try:
+ headers = self._get_header()
+ content = requests.get(
+ "%s/api/latest/hive?describe=true&per_page=10000"
+ % self.configuration.get("endpoint"),
+ headers=headers,
+ )
+ data = content.json()
+
+ for schema in data["schemas"]:
+ tables = data["schemas"][schema]
+ for table in tables:
+ table_name = list(table.keys())[0]
+ columns = [f["name"] for f in table[table_name]["columns"]]
+
+ if schema != "default":
+ table_name = "{}.{}".format(schema, table_name)
+
+ schemas[table_name] = {"name": table_name, "columns": columns}
+
+ except Exception as e:
+ logging.error(
+ "Failed to get schema information from Qubole. Error {}".format(str(e))
+ )
+
+ return list(schemas.values())
+
+ def _get_header(self):
+ return {
+ "Content-type": "application/json",
+ "Accept": "application/json",
+ "X-AUTH-TOKEN": self.configuration.get("token"),
+ }
+
+
+register(Qubole)
diff --git a/redash/query_runner/query_results.py b/redash/query_runner/query_results.py
index 3fdc40c294..7da4ff0afb 100644
--- a/redash/query_runner/query_results.py
+++ b/redash/query_runner/query_results.py
@@ -1,21 +1,17 @@
-import datetime
-import decimal
-import hashlib
import logging
import re
import sqlite3
-from urllib.parse import parse_qs
from redash import models
from redash.permissions import has_access, view_only
from redash.query_runner import (
- TYPE_STRING,
BaseQueryRunner,
- JobTimeoutException,
+ TYPE_STRING,
guess_type,
register,
+ JobTimeoutException,
)
-from redash.utils import json_dumps
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
@@ -28,10 +24,6 @@ class CreateTableError(Exception):
pass
-def extract_query_params(query):
- return re.findall(r"(?:join|from)\s+param_query_(\d+)_{([^}]+)}", query, re.IGNORECASE)
-
-
def extract_query_ids(query):
queries = re.findall(r"(?:join|from)\s+query_(\d+)", query, re.IGNORECASE)
return [int(q) for q in queries]
@@ -56,14 +48,7 @@ def _load_query(user, query_id):
return query
-def replace_query_parameters(query_text, params):
- qs = parse_qs(params)
- for key, value in qs.items():
- query_text = query_text.replace("{{{{{my_key}}}}}".format(my_key=key), value[0])
- return query_text
-
-
-def get_query_results(user, query_id, bring_from_cache, params=None):
+def get_query_results(user, query_id, bring_from_cache):
query = _load_query(user, query_id)
if bring_from_cache:
if query.latest_query_data_id is not None:
@@ -71,31 +56,23 @@ def get_query_results(user, query_id, bring_from_cache, params=None):
else:
raise Exception("No cached result available for query {}.".format(query.id))
else:
- query_text = query.query_text
- if params is not None:
- query_text = replace_query_parameters(query_text, params)
-
- results, error = query.data_source.query_runner.run_query(query_text, user)
+ results, error = query.data_source.query_runner.run_query(
+ query.query_text, user
+ )
if error:
raise Exception("Failed loading results for query id {}.".format(query.id))
+ else:
+ results = json_loads(results)
return results
-def create_tables_from_query_ids(user, connection, query_ids, query_params, cached_query_ids=[]):
+def create_tables_from_query_ids(user, connection, query_ids, cached_query_ids=[]):
for query_id in set(cached_query_ids):
results = get_query_results(user, query_id, True)
table_name = "cached_query_{query_id}".format(query_id=query_id)
create_table(connection, table_name, results)
- for query in set(query_params):
- results = get_query_results(user, query[0], False, query[1])
- table_hash = hashlib.md5(
- "query_{query}_{hash}".format(query=query[0], hash=query[1]).encode(), usedforsecurity=False
- ).hexdigest()
- table_name = "query_{query_id}_{param_hash}".format(query_id=query[0], param_hash=table_hash)
- create_table(connection, table_name, results)
-
for query_id in set(query_ids):
results = get_query_results(user, query_id, False)
table_name = "query_{query_id}".format(query_id=query_id)
@@ -103,16 +80,12 @@ def create_tables_from_query_ids(user, connection, query_ids, query_params, cach
def fix_column_name(name):
- return '"{}"'.format(re.sub(r'[:."\s]', "_", name, flags=re.UNICODE))
+ return '"{}"'.format(re.sub('[:."\s]', "_", name, flags=re.UNICODE))
def flatten(value):
if isinstance(value, (list, dict)):
return json_dumps(value)
- elif isinstance(value, decimal.Decimal):
- return float(value)
- elif isinstance(value, datetime.timedelta):
- return str(value)
else:
return value
@@ -129,7 +102,9 @@ def create_table(connection, table_name, query_results):
logger.debug("CREATE TABLE query: %s", create_table)
connection.execute(create_table)
except sqlite3.OperationalError as exc:
- raise CreateTableError("Error creating table {}: {}".format(table_name, str(exc)))
+ raise CreateTableError(
+ "Error creating table {}: {}".format(table_name, str(exc))
+ )
insert_template = "insert into {table_name} ({column_list}) values ({place_holders})".format(
table_name=table_name,
@@ -142,17 +117,6 @@ def create_table(connection, table_name, query_results):
connection.execute(insert_template, values)
-def prepare_parameterized_query(query, query_params):
- for params in query_params:
- table_hash = hashlib.md5(
- "query_{query}_{hash}".format(query=params[0], hash=params[1]).encode(), usedforsecurity=False
- ).hexdigest()
- key = "param_query_{query_id}_{{{param_string}}}".format(query_id=params[0], param_string=params[1])
- value = "query_{query_id}_{param_hash}".format(query_id=params[0], param_hash=table_hash)
- query = query.replace(key, value)
- return query
-
-
class Results(BaseQueryRunner):
should_annotate_query = False
noop_query = "SELECT 1"
@@ -169,17 +133,11 @@ def run_query(self, query, user):
connection = sqlite3.connect(":memory:")
query_ids = extract_query_ids(query)
-
- query_params = extract_query_params(query)
-
cached_query_ids = extract_cached_query_ids(query)
- create_tables_from_query_ids(user, connection, query_ids, query_params, cached_query_ids)
+ create_tables_from_query_ids(user, connection, query_ids, cached_query_ids)
cursor = connection.cursor()
- if query_params is not None:
- query = prepare_parameterized_query(query, query_params)
-
try:
cursor.execute(query)
@@ -202,15 +160,16 @@ def run_query(self, query, user):
data = {"columns": columns, "rows": rows}
error = None
+ json_data = json_dumps(data)
else:
error = "Query completed but it returned no data."
- data = None
+ json_data = None
except (KeyboardInterrupt, JobTimeoutException):
connection.cancel()
raise
finally:
connection.close()
- return data, error
+ return json_data, error
register(Results)
diff --git a/redash/query_runner/risingwave.py b/redash/query_runner/risingwave.py
deleted file mode 100644
index 2cc241d095..0000000000
--- a/redash/query_runner/risingwave.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from redash.query_runner import register
-from redash.query_runner.pg import PostgreSQL
-
-
-class RisingWave(PostgreSQL):
- @classmethod
- def type(cls):
- return "risingwave"
-
- @classmethod
- def name(cls):
- return "RisingWave"
-
- def _get_tables(self, schema):
- query = """
- SELECT s.nspname as table_schema,
- c.relname as table_name,
- a.attname as column_name,
- null as data_type
- FROM pg_class c
- JOIN pg_namespace s
- ON c.relnamespace = s.oid
- AND s.nspname NOT IN ('pg_catalog', 'information_schema', 'rw_catalog')
- JOIN pg_attribute a
- ON a.attrelid = c.oid
- AND a.attnum > 0
- AND NOT a.attisdropped
- WHERE c.relkind IN ('m', 'f', 'p')
-
- UNION
-
- SELECT table_schema,
- table_name,
- column_name,
- data_type
- FROM information_schema.columns
- WHERE table_schema NOT IN ('pg_catalog', 'information_schema', 'rw_catalog');
- """
-
- self._get_definitions(schema, query)
-
- return list(schema.values())
-
-
-register(RisingWave)
diff --git a/redash/query_runner/rockset.py b/redash/query_runner/rockset.py
index 96910b8be9..a8e0f0eb6c 100644
--- a/redash/query_runner/rockset.py
+++ b/redash/query_runner/rockset.py
@@ -1,13 +1,6 @@
import requests
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
def _get_type(value):
@@ -24,15 +17,15 @@ def _get_type(value):
# The following is here, because Rockset's PyPi package is Python 3 only.
# Should be removed once we move to Python 3.
-class RocksetAPI:
- def __init__(self, api_key, api_server, vi_id):
+class RocksetAPI(object):
+ def __init__(self, api_key, api_server):
self.api_key = api_key
self.api_server = api_server
- self.vi_id = vi_id
- def _request(self, endpoint, method="GET", body=None):
- headers = {"Authorization": "ApiKey {}".format(self.api_key), "User-Agent": "rest:redash/1.0"}
- url = "{}/v1/orgs/self/{}".format(self.api_server, endpoint)
+ def _request(self, endpoint, method='GET', body=None):
+ headers = {'Authorization': 'ApiKey {}'.format(self.api_key),
+ 'User-Agent': 'rest:redash/1.0'}
+ url = '{}/v1/orgs/self/{}'.format(self.api_server, endpoint)
if method == "GET":
r = requests.get(url, headers=headers)
@@ -44,22 +37,19 @@ def _request(self, endpoint, method="GET", body=None):
raise "Unknown method: {}".format(method)
def list_workspaces(self):
- response = self._request("ws")
- return [x["name"] for x in response["data"] if x["collection_count"] > 0]
+ response = self._request('ws')
+ return [x['name'] for x in response['data'] if x['collection_count'] > 0]
- def list_collections(self, workspace="commons"):
- response = self._request("ws/{}/collections".format(workspace))
- return [x["name"] for x in response["data"]]
+ def list_collections(self, workspace='commons'):
+ response = self._request('ws/{}/collections'.format(workspace))
+ return [x['name'] for x in response['data']]
def collection_columns(self, workspace, collection):
response = self.query('DESCRIBE "{}"."{}" OPTION(max_field_depth=1)'.format(workspace, collection))
- return sorted(set([x["field"][0] for x in response["results"]]))
+ return sorted(set([x['field'][0] for x in response['results']]))
def query(self, sql):
- query_path = "queries"
- if self.vi_id is not None and self.vi_id != "":
- query_path = f"virtualinstances/{self.vi_id}/queries"
- return self._request(query_path, "POST", {"sql": {"query": sql}})
+ return self._request("queries", "POST", {"sql": {"query": sql}})
class Rockset(BaseSQLQueryRunner):
@@ -76,9 +66,8 @@ def configuration_schema(cls):
"default": "https://api.rs2.usw2.rockset.com",
},
"api_key": {"title": "API Key", "type": "string"},
- "vi_id": {"title": "Virtual Instance ID", "type": "string"},
},
- "order": ["api_key", "api_server", "vi_id"],
+ "order": ["api_key", "api_server"],
"required": ["api_server", "api_key"],
"secret": ["api_key"],
}
@@ -91,19 +80,18 @@ def __init__(self, configuration):
super(Rockset, self).__init__(configuration)
self.api = RocksetAPI(
self.configuration.get("api_key"),
- self.configuration.get("api_server", "https://api.usw2a1.rockset.com"),
- self.configuration.get("vi_id"),
+ self.configuration.get("api_server", "https://api.rs2.usw2.rockset.com"),
)
def _get_tables(self, schema):
for workspace in self.api.list_workspaces():
for collection in self.api.list_collections(workspace):
- table_name = collection if workspace == "commons" else "{}.{}".format(workspace, collection)
+ table_name = collection if workspace == 'commons' else '{}.{}'.format(workspace, collection)
schema[table_name] = {
- "name": table_name,
- "columns": self.api.collection_columns(workspace, collection),
+ 'name': table_name,
+ 'columns': self.api.collection_columns(workspace, collection)
}
- return sorted(schema.values(), key=lambda x: x["name"])
+ return sorted(schema.values(), key=lambda x: x['name'])
def run_query(self, query, user):
results = self.api.query(query)
@@ -119,8 +107,10 @@ def run_query(self, query, user):
if len(rows) > 0:
columns = []
for k in rows[0]:
- columns.append({"name": k, "friendly_name": k, "type": _get_type(rows[0][k])})
- data = {"columns": columns, "rows": rows}
+ columns.append(
+ {"name": k, "friendly_name": k, "type": _get_type(rows[0][k])}
+ )
+ data = json_dumps({"columns": columns, "rows": rows})
return data, None
diff --git a/redash/query_runner/salesforce.py b/redash/query_runner/salesforce.py
index 5e0e018c2e..3f1bd261f4 100644
--- a/redash/query_runner/salesforce.py
+++ b/redash/query_runner/salesforce.py
@@ -1,27 +1,25 @@
-import logging
import re
+import logging
from collections import OrderedDict
-
+from redash.query_runner import BaseQueryRunner, register
from redash.query_runner import (
- TYPE_BOOLEAN,
+ TYPE_STRING,
TYPE_DATE,
TYPE_DATETIME,
- TYPE_FLOAT,
TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
+ TYPE_FLOAT,
+ TYPE_BOOLEAN,
)
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
try:
- from simple_salesforce import Salesforce as SimpleSalesforce
- from simple_salesforce import SalesforceError
+ from simple_salesforce import Salesforce as SimpleSalesforce, SalesforceError
from simple_salesforce.api import DEFAULT_API_VERSION
enabled = True
-except ImportError:
+except ImportError as e:
enabled = False
# See https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/field_types.htm
@@ -79,8 +77,14 @@ def configuration_schema(cls):
"title": "Salesforce API Version",
"default": DEFAULT_API_VERSION,
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
- "required": ["username", "password"],
+ "required": ["username", "password", "token"],
"secret": ["password", "token"],
}
@@ -165,10 +169,11 @@ def run_query(self, query, user):
columns = self.fetch_columns(cols)
error = None
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
except SalesforceError as err:
error = err.content
- data = None
- return data, error
+ json_data = None
+ return json_data, error
def get_schema(self, get_stats=False):
sf = self._get_sf()
diff --git a/redash/query_runner/script.py b/redash/query_runner/script.py
index d64f1006fe..ae2a342442 100644
--- a/redash/query_runner/script.py
+++ b/redash/query_runner/script.py
@@ -1,7 +1,8 @@
import os
import subprocess
+import sys
-from redash.query_runner import BaseQueryRunner, register
+from redash.query_runner import *
def query_to_script_path(path, query):
@@ -44,6 +45,12 @@ def configuration_schema(cls):
"type": "boolean",
"title": "Execute command through the shell",
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["path"],
}
@@ -55,14 +62,15 @@ def type(cls):
def __init__(self, configuration):
super(Script, self).__init__(configuration)
- path = self.configuration.get("path", "")
# If path is * allow any execution path
- if path == "*":
+ if self.configuration["path"] == "*":
return
# Poor man's protection against running scripts from outside the scripts directory
- if path.find("../") > -1:
- raise ValueError("Scripts can only be run from the configured scripts directory")
+ if self.configuration["path"].find("../") > -1:
+ raise ValueError(
+ "Scripts can only be run from the configured scripts directory"
+ )
def test_connection(self):
pass
diff --git a/redash/query_runner/snowflake.py b/redash/query_runner/snowflake.py
index bb67c20d68..34556457c6 100644
--- a/redash/query_runner/snowflake.py
+++ b/redash/query_runner/snowflake.py
@@ -6,17 +6,16 @@
enabled = False
-from redash import __version__
+from redash.query_runner import BaseQueryRunner, register
from redash.query_runner import (
- TYPE_BOOLEAN,
+ TYPE_STRING,
TYPE_DATE,
TYPE_DATETIME,
- TYPE_FLOAT,
TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
+ TYPE_FLOAT,
+ TYPE_BOOLEAN,
)
+from redash.utils import json_dumps, json_loads
TYPES_MAP = {
0: TYPE_INTEGER,
@@ -32,7 +31,7 @@
}
-class Snowflake(BaseSQLQueryRunner):
+class Snowflake(BaseQueryRunner):
noop_query = "SELECT 1"
@classmethod
@@ -46,27 +45,16 @@ def configuration_schema(cls):
"warehouse": {"type": "string"},
"database": {"type": "string"},
"region": {"type": "string", "default": "us-west"},
- "lower_case_columns": {
- "type": "boolean",
- "title": "Lower Case Column Names in Results",
- "default": False,
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
},
- "host": {"type": "string"},
},
- "order": [
- "account",
- "user",
- "password",
- "warehouse",
- "database",
- "region",
- "host",
- ],
+ "order": ["account", "user", "password", "warehouse", "database", "region"],
"required": ["user", "password", "account", "database", "warehouse"],
"secret": ["password"],
- "extra_options": [
- "host",
- ],
}
@classmethod
@@ -82,42 +70,27 @@ def determine_type(cls, data_type, scale):
def _get_connection(self):
region = self.configuration.get("region")
- account = self.configuration["account"]
# for us-west we don't need to pass a region (and if we do, it fails to connect)
if region == "us-west":
region = None
- if self.configuration.__contains__("host"):
- host = self.configuration.get("host")
- else:
- if region:
- host = "{}.{}.snowflakecomputing.com".format(account, region)
- else:
- host = "{}.snowflakecomputing.com".format(account)
-
connection = snowflake.connector.connect(
user=self.configuration["user"],
password=self.configuration["password"],
- account=account,
+ account=self.configuration["account"],
region=region,
- host=host,
- application="Redash/{} (Snowflake)".format(__version__.split("-")[0]),
)
return connection
- def _column_name(self, column_name):
- if self.configuration.get("lower_case_columns", False):
- return column_name.lower()
-
- return column_name
-
def _parse_results(self, cursor):
columns = self.fetch_columns(
- [(self._column_name(i[0]), self.determine_type(i[1], i[5])) for i in cursor.description]
+ [(i[0], self.determine_type(i[1], i[5])) for i in cursor.description]
)
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
+ rows = [
+ dict(zip((column["name"] for column in columns), row)) for row in cursor
+ ]
data = {"columns": columns, "rows": rows}
return data
@@ -134,11 +107,12 @@ def run_query(self, query, user):
data = self._parse_results(cursor)
error = None
+ json_data = json_dumps(data)
finally:
cursor.close()
connection.close()
- return data, error
+ return json_data, error
def _run_query_without_warehouse(self, query):
connection = self._get_connection()
@@ -155,10 +129,10 @@ def _run_query_without_warehouse(self, query):
cursor.close()
connection.close()
- return data, error
-
+ return data, error
+
def _database_name_includes_schema(self):
- return "." in self.configuration.get("database")
+ return '.' in self.configuration.get('database')
def get_schema(self, get_stats=False):
if self._database_name_includes_schema():
@@ -169,7 +143,7 @@ def get_schema(self, get_stats=False):
results, error = self._run_query_without_warehouse(query)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
schema = {}
for row in results["rows"]:
diff --git a/redash/query_runner/sparql_endpoint.py b/redash/query_runner/sparql_endpoint.py
deleted file mode 100644
index 2f47c21db1..0000000000
--- a/redash/query_runner/sparql_endpoint.py
+++ /dev/null
@@ -1,217 +0,0 @@
-"""Provide the query runner for SPARQL Endpoints.
-
-seeAlso: https://www.w3.org/TR/rdf-sparql-query/
-"""
-
-import json
-import logging
-from os import environ
-
-from redash.query_runner import BaseQueryRunner
-
-from . import register
-
-try:
- import requests
- from cmem.cmempy.queries import SparqlQuery
- from rdflib.plugins.sparql import prepareQuery # noqa
-
- enabled = True
-except ImportError:
- enabled = False
-
-logger = logging.getLogger(__name__)
-
-
-class SPARQLEndpointQueryRunner(BaseQueryRunner):
- """Use SPARQL Endpoint as redash data source"""
-
- # These environment keys are used by cmempy
- KNOWN_CONFIG_KEYS = ("SPARQL_BASE_URI", "SSL_VERIFY")
-
- # These variables hold secret data and should NOT be logged
- KNOWN_SECRET_KEYS = ()
-
- # This allows for an easy connection test
- noop_query = "SELECT ?noop WHERE {BIND('noop' as ?noop)}"
-
- def __init__(self, configuration):
- """init the class and configuration"""
- super(SPARQLEndpointQueryRunner, self).__init__(configuration)
-
- self.configuration = configuration
-
- def _setup_environment(self):
- """provide environment for rdflib
-
- rdflib environment variables need to match key in the properties
- object of the configuration_schema
- """
- for key in self.KNOWN_CONFIG_KEYS:
- if key in environ:
- environ.pop(key)
- value = self.configuration.get(key, None)
- if value is not None:
- environ[key] = str(value)
- if key in self.KNOWN_SECRET_KEYS:
- logger.info("{} set by config".format(key))
- else:
- logger.info("{} set by config to {}".format(key, environ[key]))
-
- @staticmethod
- def _transform_sparql_results(results):
- """transforms a SPARQL query result to a redash query result
-
- source structure: SPARQL 1.1 Query Results JSON Format
- - seeAlso: https://www.w3.org/TR/sparql11-results-json/
-
- target structure: redash result set
- there is no good documentation available
- so here an example result set as needed for redash:
- data = {
- "columns": [ {"name": "name", "type": "string", "friendly_name": "friendly name"}],
- "rows": [
- {"name": "value 1"},
- {"name": "value 2"}
- ]}
-
- FEATURE?: During the sparql_row loop, we could check the data types of the
- values and, in case they are all the same, choose something better than
- just string.
- """
- logger.info("results are: {}".format(results))
- # Not sure why we do not use the json package here but all other
- # query runner do it the same way :-)
- sparql_results = results
- # transform all bindings to redash rows
- rows = []
- for sparql_row in sparql_results["results"]["bindings"]:
- row = {}
- for var in sparql_results["head"]["vars"]:
- try:
- row[var] = sparql_row[var]["value"]
- except KeyError:
- # not bound SPARQL variables are set as empty strings
- row[var] = ""
- rows.append(row)
- # transform all vars to redash columns
- columns = []
- for var in sparql_results["head"]["vars"]:
- columns.append({"name": var, "friendly_name": var, "type": "string"})
- # Not sure why we do not use the json package here but all other
- # query runner do it the same way :-)
- return {"columns": columns, "rows": rows}
-
- @classmethod
- def name(cls):
- return "SPARQL Endpoint"
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def type(cls):
- return "sparql_endpoint"
-
- def remove_comments(self, string):
- return string[string.index("*/") + 2 :].strip()
-
- def run_query(self, query, user):
- """send a query to a sparql endpoint"""
- logger.info("about to execute query (user='{}'): {}".format(user, query))
- query_text = self.remove_comments(query)
- query = SparqlQuery(query_text)
- query_type = query.get_query_type()
- if query_type not in ["SELECT", None]:
- raise ValueError("Queries of type {} can not be processed by redash.".format(query_type))
-
- self._setup_environment()
- try:
- endpoint = self.configuration.get("SPARQL_BASE_URI")
- r = requests.get(
- endpoint,
- params=dict(query=query_text),
- headers=dict(Accept="application/json"),
- )
- data = self._transform_sparql_results(r.text)
- except Exception as error:
- logger.info("Error: {}".format(error))
- try:
- # try to load Problem Details for HTTP API JSON
- details = json.loads(error.response.text)
- error = ""
- if "title" in details:
- error += details["title"] + ": "
- if "detail" in details:
- error += details["detail"]
- return None, error
- except Exception:
- pass
-
- return None, error
-
- error = None
- return data, error
-
- @classmethod
- def configuration_schema(cls):
- """provide the configuration of the data source as json schema"""
- return {
- "type": "object",
- "properties": {
- "SPARQL_BASE_URI": {"type": "string", "title": "Base URL"},
- "SSL_VERIFY": {
- "type": "boolean",
- "title": "Verify SSL certificates for API requests",
- "default": True,
- },
- },
- "required": ["SPARQL_BASE_URI"],
- "secret": [],
- "extra_options": ["SSL_VERIFY"],
- }
-
- def get_schema(self, get_stats=False):
- """Get the schema structure (prefixes, graphs)."""
- schema = dict()
- schema["1"] = {
- "name": "-> Common Prefixes <-",
- "columns": self._get_common_prefixes_schema(),
- }
- schema["2"] = {"name": "-> Graphs <-", "columns": self._get_graphs_schema()}
- # schema.update(self._get_query_schema())
- logger.info(f"Getting Schema Values: {schema.values()}")
- return schema.values()
-
- def _get_graphs_schema(self):
- """Get a list of readable graph FROM clause strings."""
- self._setup_environment()
- endpoint = self.configuration.get("SPARQL_BASE_URI")
- query_text = "SELECT DISTINCT ?g WHERE {GRAPH ?g {?s ?p ?o}}"
- r = requests.get(
- endpoint,
- params=dict(query=query_text),
- headers=dict(Accept="application/json"),
- ).json()
- graph_iris = [g.get("g").get("value") for g in r.get("results").get("bindings")]
- graphs = []
- for graph in graph_iris:
- graphs.append("FROM <{}>".format(graph))
- return graphs
-
- @staticmethod
- def _get_common_prefixes_schema():
- """Get a list of SPARQL prefix declarations."""
- common_prefixes = [
- "PREFIX rdf: ",
- "PREFIX rdfs: ",
- "PREFIX owl: ",
- "PREFIX schema: ",
- "PREFIX dct: ",
- "PREFIX skos: ",
- ]
- return common_prefixes
-
-
-register(SPARQLEndpointQueryRunner)
diff --git a/redash/query_runner/sqlite.py b/redash/query_runner/sqlite.py
index d5dcb8dfc0..f88bd06fbb 100644
--- a/redash/query_runner/sqlite.py
+++ b/redash/query_runner/sqlite.py
@@ -1,11 +1,8 @@
import logging
import sqlite3
-from redash.query_runner import (
- BaseSQLQueryRunner,
- JobTimeoutException,
- register,
-)
+from redash.query_runner import BaseSQLQueryRunner, register, JobTimeoutException
+from redash.utils import json_dumps, json_loads
logger = logging.getLogger(__name__)
@@ -17,7 +14,15 @@ class Sqlite(BaseSQLQueryRunner):
def configuration_schema(cls):
return {
"type": "object",
- "properties": {"dbpath": {"type": "string", "title": "Database Path"}},
+ "properties": {
+ "dbpath": {"type": "string", "title": "Database Path"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
+ },
"required": ["dbpath"],
}
@@ -28,24 +33,27 @@ def type(cls):
def __init__(self, configuration):
super(Sqlite, self).__init__(configuration)
- self._dbpath = self.configuration.get("dbpath", "")
+ self._dbpath = self.configuration["dbpath"]
def _get_tables(self, schema):
query_table = "select tbl_name from sqlite_master where type='table'"
- query_columns = 'PRAGMA table_info("%s")'
+ query_columns = "PRAGMA table_info(%s)"
results, error = self.run_query(query_table, None)
if error is not None:
raise Exception("Failed getting schema.")
+ results = json_loads(results)
+
for row in results["rows"]:
table_name = row["tbl_name"]
schema[table_name] = {"name": table_name, "columns": []}
results_table, error = self.run_query(query_columns % (table_name,), None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+ results_table = json_loads(results_table)
for row_column in results_table["rows"]:
schema[table_name]["columns"].append(row_column["name"])
@@ -61,19 +69,23 @@ def run_query(self, query, user):
if cursor.description is not None:
columns = self.fetch_columns([(i[0], None) for i in cursor.description])
- rows = [dict(zip((column["name"] for column in columns), row)) for row in cursor]
+ rows = [
+ dict(zip((column["name"] for column in columns), row))
+ for row in cursor
+ ]
data = {"columns": columns, "rows": rows}
error = None
+ json_data = json_dumps(data)
else:
error = "Query completed but it returned no data."
- data = None
+ json_data = None
except (KeyboardInterrupt, JobTimeoutException):
connection.cancel()
raise
finally:
connection.close()
- return data, error
+ return json_data, error
register(Sqlite)
diff --git a/redash/query_runner/tinybird.py b/redash/query_runner/tinybird.py
deleted file mode 100644
index f29a45a8cb..0000000000
--- a/redash/query_runner/tinybird.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import logging
-
-import requests
-
-from redash.query_runner import register
-from redash.query_runner.clickhouse import ClickHouse
-
-logger = logging.getLogger(__name__)
-
-
-class Tinybird(ClickHouse):
- noop_query = "SELECT count() FROM tinybird.pipe_stats LIMIT 1"
-
- DEFAULT_URL = "https://api.tinybird.co"
-
- SQL_ENDPOINT = "/v0/sql"
- DATASOURCES_ENDPOINT = "/v0/datasources"
- PIPES_ENDPOINT = "/v0/pipes"
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "url": {"type": "string", "default": cls.DEFAULT_URL},
- "token": {"type": "string", "title": "Auth Token"},
- "timeout": {
- "type": "number",
- "title": "Request Timeout",
- "default": 30,
- },
- "verify": {
- "type": "boolean",
- "title": "Verify SSL certificate",
- "default": True,
- },
- },
- "order": ["url", "token"],
- "required": ["token"],
- "extra_options": ["timeout", "verify"],
- "secret": ["token"],
- }
-
- def _get_tables(self, schema):
- self._collect_tinybird_schema(
- schema,
- self.DATASOURCES_ENDPOINT,
- "datasources",
- )
-
- self._collect_tinybird_schema(
- schema,
- self.PIPES_ENDPOINT,
- "pipes",
- )
-
- return list(schema.values())
-
- def _send_query(self, data, session_id=None, session_check=None):
- return self._get_from_tinybird(
- self.SQL_ENDPOINT,
- params={"q": data.encode("utf-8", "ignore")},
- )
-
- def _collect_tinybird_schema(self, schema, endpoint, resource_type):
- response = self._get_from_tinybird(endpoint)
- resources = response.get(resource_type, [])
-
- for r in resources:
- if r["name"] not in schema:
- schema[r["name"]] = {"name": r["name"], "columns": []}
-
- if resource_type == "pipes" and not r.get("endpoint"):
- continue
-
- query = f"SELECT * FROM {r['name']} LIMIT 1 FORMAT JSON"
- try:
- query_result = self._send_query(query)
- except Exception:
- logger.exception(f"error in schema {r['name']}")
- continue
-
- columns = [meta["name"] for meta in query_result["meta"]]
- schema[r["name"]]["columns"].extend(columns)
-
- return schema
-
- def _get_from_tinybird(self, endpoint, params=None):
- url = f"{self.configuration.get('url', self.DEFAULT_URL)}{endpoint}"
- authorization = f"Bearer {self.configuration.get('token')}"
-
- try:
- response = requests.get(
- url,
- timeout=self.configuration.get("timeout", 30),
- params=params,
- headers={"Authorization": authorization},
- verify=self.configuration.get("verify", True),
- )
- except requests.RequestException as e:
- if e.response:
- details = f"({e.__class__.__name__}, Status Code: {e.response.status_code})"
- else:
- details = f"({e.__class__.__name__})"
- raise Exception(f"Connection error to: {url} {details}.")
-
- if response.status_code >= 400:
- raise Exception(response.text)
-
- return response.json()
-
-
-register(Tinybird)
diff --git a/redash/query_runner/treasuredata.py b/redash/query_runner/treasuredata.py
index ddd1d50e46..d0541fc673 100644
--- a/redash/query_runner/treasuredata.py
+++ b/redash/query_runner/treasuredata.py
@@ -1,14 +1,7 @@
import logging
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -59,8 +52,13 @@ def configuration_schema(cls):
"title": "Auto Schema Retrieval",
"default": False,
},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
- "secret": ["apikey"],
"required": ["apikey", "db"],
}
@@ -76,17 +74,17 @@ def get_schema(self, get_stats=False):
schema = {}
if self.configuration.get("get_schema", False):
try:
- with tdclient.Client(
- self.configuration.get("apikey"), endpoint=self.configuration.get("endpoint")
- ) as client:
+ with tdclient.Client(self.configuration.get("apikey"),endpoint=self.configuration.get("endpoint")) as client:
for table in client.tables(self.configuration.get("db")):
- table_name = "{}.{}".format(self.configuration.get("db"), table.name)
+ table_name = "{}.{}".format(
+ self.configuration.get("db"), table.name
+ )
for table_schema in table.schema:
schema[table_name] = {
"name": table_name,
"columns": [column[0] for column in table.schema],
}
- except Exception:
+ except Exception as ex:
raise Exception("Failed getting schema")
return list(schema.values())
@@ -102,23 +100,30 @@ def run_query(self, query, user):
try:
cursor.execute(query)
columns_tuples = [
- (i[0], TD_TYPES_MAPPING.get(i[1], None)) for i in cursor.show_job()["hive_result_schema"]
+ (i[0], TD_TYPES_MAPPING.get(i[1], None))
+ for i in cursor.show_job()["hive_result_schema"]
]
columns = self.fetch_columns(columns_tuples)
if cursor.rowcount == 0:
rows = []
else:
- rows = [dict(zip(([column["name"] for column in columns]), r)) for r in cursor.fetchall()]
+ rows = [
+ dict(zip(([column["name"] for column in columns]), r))
+ for r in cursor.fetchall()
+ ]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
except errors.InternalError as e:
- data = None
+ json_data = None
error = "%s: %s" % (
str(e),
- cursor.show_job().get("debug", {}).get("stderr", "No stderr message in the response"),
+ cursor.show_job()
+ .get("debug", {})
+ .get("stderr", "No stderr message in the response"),
)
- return data, error
+ return json_data, error
register(TreasureData)
diff --git a/redash/query_runner/trino.py b/redash/query_runner/trino.py
deleted file mode 100644
index fbbfab9bd7..0000000000
--- a/redash/query_runner/trino.py
+++ /dev/null
@@ -1,172 +0,0 @@
-import logging
-
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseQueryRunner,
- InterruptException,
- JobTimeoutException,
- register,
-)
-
-logger = logging.getLogger(__name__)
-
-try:
- import trino
- from trino.exceptions import DatabaseError
-
- enabled = True
-except ImportError:
- enabled = False
-
-TRINO_TYPES_MAPPING = {
- "boolean": TYPE_BOOLEAN,
- "tinyint": TYPE_INTEGER,
- "smallint": TYPE_INTEGER,
- "integer": TYPE_INTEGER,
- "long": TYPE_INTEGER,
- "bigint": TYPE_INTEGER,
- "float": TYPE_FLOAT,
- "real": TYPE_FLOAT,
- "double": TYPE_FLOAT,
- "decimal": TYPE_INTEGER,
- "varchar": TYPE_STRING,
- "char": TYPE_STRING,
- "string": TYPE_STRING,
- "json": TYPE_STRING,
- "date": TYPE_DATE,
- "timestamp": TYPE_DATETIME,
-}
-
-
-class Trino(BaseQueryRunner):
- noop_query = "SELECT 1"
- should_annotate_query = False
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "protocol": {"type": "string", "default": "http"},
- "host": {"type": "string"},
- "port": {"type": "number"},
- "username": {"type": "string"},
- "password": {"type": "string"},
- "catalog": {"type": "string"},
- "schema": {"type": "string"},
- },
- "order": [
- "protocol",
- "host",
- "port",
- "username",
- "password",
- "catalog",
- "schema",
- ],
- "required": ["host", "username"],
- "secret": ["password"],
- }
-
- @classmethod
- def enabled(cls):
- return enabled
-
- @classmethod
- def type(cls):
- return "trino"
-
- def get_schema(self, get_stats=False):
- if self.configuration.get("catalog"):
- catalogs = [self.configuration.get("catalog")]
- else:
- catalogs = self._get_catalogs()
-
- schema = {}
- for catalog in catalogs:
- query = f"""
- SELECT table_schema, table_name, column_name, data_type
- FROM {catalog}.information_schema.columns
- WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
- """
- results, error = self.run_query(query, None)
-
- if error is not None:
- self._handle_run_query_error(error)
-
- for row in results["rows"]:
- table_name = f'{catalog}.{row["table_schema"]}.{row["table_name"]}'
-
- if table_name not in schema:
- schema[table_name] = {"name": table_name, "columns": []}
-
- column = {"name": row["column_name"], "type": row["data_type"]}
- schema[table_name]["columns"].append(column)
-
- return list(schema.values())
-
- def _get_catalogs(self):
- query = """
- SHOW CATALOGS
- """
- results, error = self.run_query(query, None)
-
- if error is not None:
- self._handle_run_query_error(error)
-
- catalogs = []
- for row in results["rows"]:
- catalog = row["Catalog"]
- if "." in catalog:
- catalog = f'"{catalog}"'
- catalogs.append(catalog)
- return catalogs
-
- def run_query(self, query, user):
- if self.configuration.get("password"):
- auth = trino.auth.BasicAuthentication(
- username=self.configuration.get("username"), password=self.configuration.get("password")
- )
- else:
- auth = trino.constants.DEFAULT_AUTH
- connection = trino.dbapi.connect(
- http_scheme=self.configuration.get("protocol", "http"),
- host=self.configuration.get("host", ""),
- port=self.configuration.get("port", 8080),
- catalog=self.configuration.get("catalog", ""),
- schema=self.configuration.get("schema", ""),
- user=self.configuration.get("username"),
- auth=auth,
- )
-
- cursor = connection.cursor()
-
- try:
- cursor.execute(query)
- results = cursor.fetchall()
- description = cursor.description
- columns = self.fetch_columns([(c[0], TRINO_TYPES_MAPPING.get(c[1], None)) for c in description])
- rows = [dict(zip([c["name"] for c in columns], r)) for r in results]
- data = {"columns": columns, "rows": rows}
- error = None
- except DatabaseError as db:
- data = None
- default_message = "Unspecified DatabaseError: {0}".format(str(db))
- if isinstance(db.args[0], dict):
- message = db.args[0].get("failureInfo", {"message", None}).get("message")
- else:
- message = None
- error = default_message if message is None else message
- except (KeyboardInterrupt, InterruptException, JobTimeoutException):
- cursor.cancel()
- raise
-
- return data, error
-
-
-register(Trino)
diff --git a/redash/query_runner/uptycs.py b/redash/query_runner/uptycs.py
index 6e354181e5..15c9b30bc0 100644
--- a/redash/query_runner/uptycs.py
+++ b/redash/query_runner/uptycs.py
@@ -1,11 +1,10 @@
-import datetime
-import logging
+from redash.query_runner import *
+from redash.utils import json_dumps, json_loads
import jwt
+import datetime
import requests
-
-from redash.query_runner import BaseSQLQueryRunner, register
-from redash.utils import json_loads
+import logging
logger = logging.getLogger(__name__)
@@ -58,11 +57,14 @@ def transformed_to_redash_json(self, data):
if "items" in data:
rows = data["items"]
- return {"columns": transformed_columns, "rows": rows}
+ redash_json_data = {"columns": transformed_columns, "rows": rows}
+ return redash_json_data
def api_call(self, sql):
# JWT encoded header
- header = self.generate_header(self.configuration.get("key"), self.configuration.get("secret"))
+ header = self.generate_header(
+ self.configuration.get("key"), self.configuration.get("secret")
+ )
# URL form using API key file based on GLOBAL
url = "%s/public/api/customers/%s/query" % (
@@ -85,29 +87,34 @@ def api_call(self, sql):
else:
error = "status_code " + str(response.status_code) + "\n"
error = error + "failed to connect"
- data = {}
- return data, error
+ json_data = {}
+ return json_data, error
# if we get right status code then call transfored_to_redash
- data = self.transformed_to_redash_json(response_output)
+ json_data = self.transformed_to_redash_json(response_output)
error = None
# if we got error from Uptycs include error information
if "error" in response_output:
error = response_output["error"]["message"]["brief"]
error = error + "\n" + response_output["error"]["message"]["detail"]
- return data, error
+ return json_data, error
def run_query(self, query, user):
data, error = self.api_call(query)
- logger.debug("%s", data)
- return data, error
+ json_data = json_dumps(data)
+ logger.debug("%s", json_data)
+ return json_data, error
def get_schema(self, get_stats=False):
- header = self.generate_header(self.configuration.get("key"), self.configuration.get("secret"))
+ header = self.generate_header(
+ self.configuration.get("key"), self.configuration.get("secret")
+ )
url = "%s/public/api/customers/%s/schema/global" % (
self.configuration.get("url"),
self.configuration.get("customer_id"),
)
- response = requests.get(url, headers=header, verify=self.configuration.get("verify_ssl", True))
+ response = requests.get(
+ url, headers=header, verify=self.configuration.get("verify_ssl", True)
+ )
redash_json = []
schema = json_loads(response.content)
for each_def in schema["tables"]:
diff --git a/redash/query_runner/vertica.py b/redash/query_runner/vertica.py
index e178ed15f9..f7a61fe2b4 100644
--- a/redash/query_runner/vertica.py
+++ b/redash/query_runner/vertica.py
@@ -1,15 +1,8 @@
+import sys
import logging
-from redash.query_runner import (
- TYPE_BOOLEAN,
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_INTEGER,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
-)
+from redash.utils import json_loads, json_dumps
+from redash.query_runner import *
logger = logging.getLogger(__name__)
@@ -49,6 +42,12 @@ def configuration_schema(cls):
"port": {"type": "number"},
"read_timeout": {"type": "number", "title": "Read Timeout"},
"connection_timeout": {"type": "number", "title": "Connection Timeout"},
+ "toggle_table_string": {
+ "type": "string",
+ "title": "Toggle Table String",
+ "default": "_v",
+ "info": "This string will be used to toggle visibility of tables in the schema browser when editing a query in order to remove non-useful tables from sight.",
+ },
},
"required": ["database"],
"order": [
@@ -66,7 +65,7 @@ def configuration_schema(cls):
@classmethod
def enabled(cls):
try:
- import vertica_python # noqa: F401
+ import vertica_python
except ImportError:
return False
@@ -82,7 +81,9 @@ def _get_tables(self, schema):
results, error = self.run_query(query, None)
if error is not None:
- self._handle_run_query_error(error)
+ raise Exception("Failed getting schema.")
+
+ results = json_loads(results)
for row in results["rows"]:
table_name = "{}.{}".format(row["table_schema"], row["table_name"])
@@ -98,9 +99,9 @@ def run_query(self, query, user):
import vertica_python
if query == "":
- data = None
+ json_data = None
error = "Query is empty"
- return data, error
+ return json_data, error
connection = None
try:
@@ -114,7 +115,9 @@ def run_query(self, query, user):
}
if self.configuration.get("connection_timeout"):
- conn_info["connection_timeout"] = self.configuration.get("connection_timeout")
+ conn_info["connection_timeout"] = self.configuration.get(
+ "connection_timeout"
+ )
connection = vertica_python.connect(**conn_info)
cursor = connection.cursor()
@@ -122,15 +125,21 @@ def run_query(self, query, user):
cursor.execute(query)
if cursor.description is not None:
- columns_data = [(i[0], types_map.get(i[1], None)) for i in cursor.description]
+ columns_data = [
+ (i[0], types_map.get(i[1], None)) for i in cursor.description
+ ]
columns = self.fetch_columns(columns_data)
- rows = [dict(zip(([c["name"] for c in columns]), r)) for r in cursor.fetchall()]
+ rows = [
+ dict(zip(([c["name"] for c in columns]), r))
+ for r in cursor.fetchall()
+ ]
data = {"columns": columns, "rows": rows}
+ json_data = json_dumps(data)
error = None
else:
- data = None
+ json_data = None
error = "No data was returned."
cursor.close()
@@ -138,7 +147,7 @@ def run_query(self, query, user):
if connection:
connection.close()
- return data, error
+ return json_data, error
register(Vertica)
diff --git a/redash/query_runner/yandex_disk.py b/redash/query_runner/yandex_disk.py
deleted file mode 100644
index 5d305e23c8..0000000000
--- a/redash/query_runner/yandex_disk.py
+++ /dev/null
@@ -1,165 +0,0 @@
-import logging
-from importlib.util import find_spec
-
-import requests
-import yaml
-
-from redash.query_runner import BaseSQLQueryRunner, register
-from redash.utils.pandas import pandas_installed
-
-openpyxl_installed = find_spec("openpyxl")
-
-if pandas_installed and openpyxl_installed:
- import openpyxl # noqa: F401
- import pandas as pd
-
- from redash.utils.pandas import pandas_to_result
-
- enabled = True
-
- EXTENSIONS_READERS = {
- "csv": pd.read_csv,
- "tsv": pd.read_table,
- "xls": pd.read_excel,
- "xlsx": pd.read_excel,
- }
-else:
- enabled = False
-
-logger = logging.getLogger(__name__)
-
-
-class YandexDisk(BaseSQLQueryRunner):
- should_annotate_query = False
-
- @classmethod
- def type(cls):
- return "yandex_disk"
-
- @classmethod
- def name(cls):
- return "Yandex Disk"
-
- @classmethod
- def configuration_schema(cls):
- return {
- "type": "object",
- "properties": {
- "token": {"type": "string", "title": "OAuth Token"},
- },
- "secret": ["token"],
- "required": ["token"],
- }
-
- def __init__(self, configuration):
- super(YandexDisk, self).__init__(configuration)
- self.syntax = "yaml"
- self.base_url = "https://cloud-api.yandex.net/v1/disk"
- self.list_path = "counters"
-
- def _get_tables(self, schema):
- offset = 0
- limit = 100
-
- while True:
- tmp_response = self._send_query(
- "resources/public", media_type="spreadsheet,text", limit=limit, offset=offset
- )
-
- tmp_items = tmp_response["items"]
-
- for file_info in tmp_items:
- file_name = file_info["name"]
- file_path = file_info["path"].replace("disk:", "")
-
- file_extension = file_name.split(".")[-1].lower()
- if file_extension not in EXTENSIONS_READERS:
- continue
-
- schema[file_name] = {"name": file_name, "columns": [file_path]}
-
- if len(tmp_items) < limit:
- break
-
- offset += limit
-
- return list(schema.values())
-
- def test_connection(self):
- self._send_query()
-
- def _send_query(self, url_path="", **kwargs):
- token = kwargs.pop("oauth_token", self.configuration["token"])
- r = requests.get(
- f"{self.base_url}/{url_path}",
- headers={"Authorization": f"OAuth {token}"},
- params=kwargs,
- )
-
- response_data = r.json()
-
- if not r.ok:
- error_message = f"Code: {r.status_code}, message: {r.text}"
- raise Exception(error_message)
- return response_data
-
- def run_query(self, query, user):
- logger.debug("Yandex Disk is about to execute query: %s", query)
- data = None
-
- if not query:
- error = "Query is empty"
- return data, error
-
- try:
- params = yaml.safe_load(query)
- except (ValueError, AttributeError) as e:
- logger.exception(e)
- error = f"YAML read error: {str(e)}"
- return data, error
-
- if not isinstance(params, dict):
- error = "The query format must be JSON or YAML"
- return data, error
-
- if "path" not in params:
- error = "The query must contain path"
- return data, error
-
- file_extension = params["path"].split(".")[-1].lower()
-
- read_params = {}
- is_multiple_sheets = False
-
- if file_extension not in EXTENSIONS_READERS:
- error = f"Unsupported file extension: {file_extension}"
- return data, error
- elif file_extension in ("xls", "xlsx"):
- read_params["sheet_name"] = params.get("sheet_name", 0)
- if read_params["sheet_name"] is None:
- is_multiple_sheets = True
-
- file_url = self._send_query("resources/download", path=params["path"])["href"]
-
- try:
- df = EXTENSIONS_READERS[file_extension](file_url, **read_params)
- except Exception as e:
- logger.exception(e)
- error = f"Read file error: {str(e)}"
- return data, error
-
- if is_multiple_sheets:
- new_df = []
- for sheet_name, sheet_df in df.items():
- sheet_df["sheet_name"] = sheet_name
- new_df.append(sheet_df)
- new_df = pd.concat(new_df, ignore_index=True)
- df = new_df.copy()
-
- data = pandas_to_result(df)
- error = None
-
- return data, error
-
-
-register(YandexDisk)
diff --git a/redash/query_runner/yandex_metrica.py b/redash/query_runner/yandex_metrica.py
index f8c7156ca8..b263c5e345 100644
--- a/redash/query_runner/yandex_metrica.py
+++ b/redash/query_runner/yandex_metrica.py
@@ -1,18 +1,11 @@
import logging
+import yaml
from urllib.parse import parse_qs, urlparse
-import backoff
import requests
-import yaml
-from redash.query_runner import (
- TYPE_DATE,
- TYPE_DATETIME,
- TYPE_FLOAT,
- TYPE_STRING,
- BaseSQLQueryRunner,
- register,
-)
+from redash.query_runner import *
+from redash.utils import json_dumps
logger = logging.getLogger(__name__)
@@ -79,10 +72,6 @@ def parse_ym_response(response):
return {"columns": columns, "rows": rows}
-class QuotaException(Exception):
- pass
-
-
class YandexMetrica(BaseSQLQueryRunner):
should_annotate_query = False
@@ -100,7 +89,6 @@ def configuration_schema(cls):
return {
"type": "object",
"properties": {"token": {"type": "string", "title": "OAuth Token"}},
- "secret": ["token"],
"required": ["token"],
}
@@ -111,11 +99,14 @@ def __init__(self, configuration):
self.list_path = "counters"
def _get_tables(self, schema):
- counters = self._send_query(f"management/v1/{self.list_path}")
+
+ counters = self._send_query("management/v1/{0}".format(self.list_path))
for row in counters[self.list_path]:
owner = row.get("owner_login")
- counter = f"{row.get('name', 'Unknown')} | {row.get('id', 'Unknown')}"
+ counter = "{0} | {1}".format(
+ row.get("name", "Unknown"), row.get("id", "Unknown")
+ )
if owner not in schema:
schema[owner] = {"name": owner, "columns": []}
@@ -124,26 +115,18 @@ def _get_tables(self, schema):
return list(schema.values())
def test_connection(self):
- self._send_query(f"management/v1/{self.list_path}")
+ self._send_query("management/v1/{0}".format(self.list_path))
- @backoff.on_exception(backoff.fibo, QuotaException, max_tries=10)
def _send_query(self, path="stat/v1/data", **kwargs):
token = kwargs.pop("oauth_token", self.configuration["token"])
r = requests.get(
- f"{self.url}/{path}",
- headers={"Authorization": f"OAuth {token}"},
+ "{0}/{1}".format(self.url, path),
+ headers={"Authorization": "OAuth {}".format(token)},
params=kwargs,
)
-
- response_data = r.json()
-
- if not r.ok:
- error_message = f"Code: {r.status_code}, message: {r.text}"
- if r.status_code == 429:
- logger.warning("Warning: 429 status code on Yandex Metrica query")
- raise QuotaException(error_message)
- raise Exception(error_message)
- return response_data
+ if r.status_code != 200:
+ raise Exception(r.text)
+ return r.json()
def run_query(self, query, user):
logger.debug("Metrica is about to execute query: %s", query)
@@ -167,7 +150,7 @@ def run_query(self, query, user):
return data, error
try:
- data = parse_ym_response(self._send_query(**params))
+ data = json_dumps(parse_ym_response(self._send_query(**params)))
error = None
except Exception as e:
logging.exception(e)
diff --git a/redash/security.py b/redash/security.py
index f95c839fd6..dd14441385 100644
--- a/redash/security.py
+++ b/redash/security.py
@@ -1,14 +1,10 @@
import functools
-
-from flask import request, session
-from flask_login import current_user
from flask_talisman import talisman
-from flask_wtf.csrf import CSRFProtect, generate_csrf
from redash import settings
+
talisman = talisman.Talisman()
-csrf = CSRFProtect()
def csp_allows_embeding(fn):
@@ -17,36 +13,12 @@ def decorated(*args, **kwargs):
return fn(*args, **kwargs)
embedable_csp = talisman.content_security_policy + "frame-ancestors *;"
- return talisman(content_security_policy=embedable_csp, frame_options=None)(decorated)
+ return talisman(content_security_policy=embedable_csp, frame_options=None)(
+ decorated
+ )
def init_app(app):
- csrf.init_app(app)
- app.config["WTF_CSRF_CHECK_DEFAULT"] = False
- app.config["WTF_CSRF_SSL_STRICT"] = False
- app.config["WTF_CSRF_TIME_LIMIT"] = settings.CSRF_TIME_LIMIT
-
- @app.after_request
- def inject_csrf_token(response):
- response.set_cookie("csrf_token", generate_csrf())
- return response
-
- if settings.ENFORCE_CSRF:
-
- @app.before_request
- def check_csrf():
- # BEGIN workaround until https://github.com/lepture/flask-wtf/pull/419 is merged
- if request.blueprint in csrf._exempt_blueprints:
- return
-
- view = app.view_functions.get(request.endpoint)
- if view is not None and f"{view.__module__}.{view.__name__}" in csrf._exempt_views:
- return
- # END workaround
-
- if not current_user.is_authenticated or "user_id" in session:
- csrf.protect()
-
talisman.init_app(
app,
feature_policy=settings.FEATURE_POLICY,
diff --git a/redash/serializers/__init__.py b/redash/serializers/__init__.py
index 41a370e43e..ba7aa785d3 100644
--- a/redash/serializers/__init__.py
+++ b/redash/serializers/__init__.py
@@ -3,16 +3,19 @@
classes we have. This will ensure cleaner code and better
separation of concerns.
"""
+from funcy import project
from flask_login import current_user
-from funcy import project
from rq.job import JobStatus
from rq.timeouts import JobTimeoutException
from redash import models
-from redash.models.parameterized_query import ParameterizedQuery
from redash.permissions import has_access, view_only
-from redash.serializers.query_result import (
+from redash.utils import json_loads
+from redash.models.parameterized_query import ParameterizedQuery
+
+
+from .query_result import (
serialize_query_result,
serialize_query_result_to_dsv,
serialize_query_result_to_xlsx,
@@ -23,7 +26,7 @@ def public_widget(widget):
res = {
"id": widget.id,
"width": widget.width,
- "options": widget.options,
+ "options": json_loads(widget.options),
"text": widget.text,
"updated_at": widget.updated_at,
"created_at": widget.created_at,
@@ -35,7 +38,7 @@ def public_widget(widget):
"type": v.type,
"name": v.name,
"description": v.description,
- "options": v.options,
+ "options": json_loads(v.options),
"updated_at": v.updated_at,
"created_at": v.created_at,
"query": {
@@ -52,7 +55,7 @@ def public_widget(widget):
def public_dashboard(dashboard):
dashboard_dict = project(
serialize_dashboard(dashboard, with_favorite_state=False),
- ("name", "layout", "dashboard_filters_enabled", "updated_at", "created_at", "options"),
+ ("name", "layout", "dashboard_filters_enabled", "updated_at", "created_at"),
)
widget_list = (
@@ -65,30 +68,111 @@ def public_dashboard(dashboard):
return dashboard_dict
-class Serializer:
+class Serializer(object):
pass
class QuerySerializer(Serializer):
def __init__(self, object_or_list, **kwargs):
self.object_or_list = object_or_list
+ self.with_favorite_state = kwargs.pop("with_favorite_state", True)
self.options = kwargs
def serialize(self):
if isinstance(self.object_or_list, models.Query):
result = serialize_query(self.object_or_list, **self.options)
- if self.options.get("with_favorite_state", True) and not current_user.is_api_user():
- result["is_favorite"] = models.Favorite.is_favorite(current_user.id, self.object_or_list)
+ if self.with_favorite_state and not current_user.is_api_user():
+ result["is_favorite"] = models.Favorite.is_favorite(
+ current_user.id, self.object_or_list
+ )
else:
- result = [serialize_query(query, **self.options) for query in self.object_or_list]
- if self.options.get("with_favorite_state", True):
- favorite_ids = models.Favorite.are_favorites(current_user.id, self.object_or_list)
+ result = [
+ serialize_query(query, **self.options) for query in self.object_or_list
+ ]
+ if self.with_favorite_state:
+ favorite_ids = models.Favorite.are_favorites(
+ current_user.id, self.object_or_list
+ )
for query in result:
query["is_favorite"] = query["id"] in favorite_ids
return result
+class ColumnMetadataSerializer(Serializer):
+ def __init__(self, object_or_list):
+ self.object_or_list = object_or_list
+
+ def serialize(self):
+ if isinstance(self.object_or_list, models.ColumnMetadata):
+ result = serialize_column_metadata(self.object_or_list)
+ else:
+ result = [
+ serialize_column_metadata(column_metadata)
+ for column_metadata in self.object_or_list
+ ]
+ return result
+
+
+class TableMetadataSerializer(Serializer):
+ def __init__(self, object_or_list, **kwargs):
+ self.object_or_list = object_or_list
+ self.options = kwargs
+
+ def serialize(self):
+ if isinstance(self.object_or_list, models.TableMetadata):
+ result = serialize_table_metadata(self.object_or_list, self.options)
+ else:
+ result = [
+ serialize_table_metadata(column_metadata, self.options)
+ for column_metadata in self.object_or_list
+ ]
+ return result
+
+
+def serialize_table_metadata(table_metadata, options, include_columns=True):
+ sample_queries_dict = dict(
+ [
+ (v["id"], v)
+ for v in QuerySerializer(
+ table_metadata.sample_queries, **options
+ ).serialize()
+ ]
+ )
+ d = {
+ "id": table_metadata.id,
+ "org_id": table_metadata.org_id,
+ "data_source_id": table_metadata.data_source_id,
+ "exists": table_metadata.exists,
+ "visible": table_metadata.visible,
+ "name": table_metadata.name,
+ "description": table_metadata.description,
+ "column_metadata": table_metadata.column_metadata,
+ "sample_updated_at": table_metadata.sample_updated_at,
+ "sample_queries": sample_queries_dict,
+ }
+ if include_columns:
+ d["columns"] = [
+ ColumnMetadataSerializer(column).serialize()
+ for column in table_metadata.existing_columns
+ ]
+ return d
+
+
+def serialize_column_metadata(column_metadata):
+ d = {
+ "id": column_metadata.id,
+ "org_id": column_metadata.org_id,
+ "table_id": column_metadata.table_id,
+ "name": column_metadata.name,
+ "type": column_metadata.type,
+ "example": column_metadata.example,
+ "exists": column_metadata.exists,
+ "description": column_metadata.description,
+ }
+ return d
+
+
def serialize_query(
query,
with_stats=False,
@@ -122,7 +206,11 @@ def serialize_query(
d["user_id"] = query.user_id
if with_last_modified_by:
- d["last_modified_by"] = query.last_modified_by.to_dict() if query.last_modified_by is not None else None
+ d["last_modified_by"] = (
+ query.last_modified_by.to_dict()
+ if query.last_modified_by is not None
+ else None
+ )
else:
d["last_modified_by_id"] = query.last_modified_by_id
@@ -135,7 +223,10 @@ def serialize_query(
d["runtime"] = None
if with_visualizations:
- d["visualizations"] = [serialize_visualization(vis, with_query=False) for vis in query.visualizations]
+ d["visualizations"] = [
+ serialize_visualization(vis, with_query=False)
+ for vis in query.visualizations
+ ]
return d
@@ -146,7 +237,7 @@ def serialize_visualization(object, with_query=True):
"type": object.type,
"name": object.name,
"description": object.description,
- "options": object.options,
+ "options": json_loads(object.options),
"updated_at": object.updated_at,
"created_at": object.created_at,
}
@@ -161,7 +252,7 @@ def serialize_widget(object):
d = {
"id": object.id,
"width": object.width,
- "options": object.options,
+ "options": json_loads(object.options),
"dashboard_id": object.dashboard_id,
"text": object.text,
"updated_at": object.updated_at,
@@ -197,7 +288,7 @@ def serialize_alert(alert, full=True):
def serialize_dashboard(obj, with_widgets=False, user=None, with_favorite_state=True):
- layout = obj.layout
+ layout = json_loads(obj.layout)
widgets = []
@@ -226,7 +317,7 @@ def serialize_dashboard(obj, with_widgets=False, user=None, with_favorite_state=
d = {
"id": obj.id,
- "slug": obj.name_as_slug,
+ "slug": obj.slug,
"name": obj.name,
"user_id": obj.user_id,
"user": {
@@ -238,7 +329,6 @@ def serialize_dashboard(obj, with_widgets=False, user=None, with_favorite_state=
"layout": layout,
"dashboard_filters_enabled": obj.dashboard_filters_enabled,
"widgets": widgets,
- "options": obj.options,
"is_archived": obj.is_archived,
"is_draft": obj.is_draft,
"tags": obj.tags or [],
@@ -258,12 +348,21 @@ def __init__(self, object_or_list, **kwargs):
def serialize(self):
if isinstance(self.object_or_list, models.Dashboard):
result = serialize_dashboard(self.object_or_list, **self.options)
- if self.options.get("with_favorite_state", True) and not current_user.is_api_user():
- result["is_favorite"] = models.Favorite.is_favorite(current_user.id, self.object_or_list)
+ if (
+ self.options.get("with_favorite_state", True)
+ and not current_user.is_api_user()
+ ):
+ result["is_favorite"] = models.Favorite.is_favorite(
+ current_user.id, self.object_or_list
+ )
else:
- result = [serialize_dashboard(obj, **self.options) for obj in self.object_or_list]
+ result = [
+ serialize_dashboard(obj, **self.options) for obj in self.object_or_list
+ ]
if self.options.get("with_favorite_state", True):
- favorite_ids = models.Favorite.are_favorites(current_user.id, self.object_or_list)
+ favorite_ids = models.Favorite.are_favorites(
+ current_user.id, self.object_or_list
+ )
for obj in result:
obj["is_favorite"] = obj["id"] in favorite_ids
@@ -277,9 +376,6 @@ def serialize_job(job):
JobStatus.STARTED: 2,
JobStatus.FINISHED: 3,
JobStatus.FAILED: 4,
- JobStatus.CANCELED: 5,
- JobStatus.DEFERRED: 6,
- JobStatus.SCHEDULED: 7,
}
job_status = job.get_status()
diff --git a/redash/serializers/query_result.py b/redash/serializers/query_result.py
index 1944d4cf8f..9eab2a1a42 100644
--- a/redash/serializers/query_result.py
+++ b/redash/serializers/query_result.py
@@ -1,12 +1,11 @@
-import csv
import io
-
+import csv
import xlsxwriter
+from funcy import rpartial, project
from dateutil.parser import isoparse as parse_date
-from funcy import project, rpartial
-
-from redash.authentication.org_resolving import current_org
+from redash.utils import json_loads, UnicodeWriter
from redash.query_runner import TYPE_BOOLEAN, TYPE_DATE, TYPE_DATETIME
+from redash.authentication.org_resolving import current_org
def _convert_format(fmt):
diff --git a/redash/settings/__init__.py b/redash/settings/__init__.py
index 973beffc55..ff02a2ff27 100644
--- a/redash/settings/__init__.py
+++ b/redash/settings/__init__.py
@@ -1,23 +1,23 @@
-import importlib
import os
+import importlib
import ssl
-
-from flask_talisman import talisman
from funcy import distinct, remove
+from flask_talisman import talisman
-from redash.settings.helpers import (
- add_decode_responses_to_redis_url,
- array_from_string,
- cast_int_or_default,
+from .helpers import (
fix_assets_path,
- int_or_none,
+ array_from_string,
parse_boolean,
+ int_or_none,
set_from_string,
+ add_decode_responses_to_redis_url,
)
-from redash.settings.organization import DATE_FORMAT, TIME_FORMAT # noqa
+from .organization import DATE_FORMAT, TIME_FORMAT # noqa
# _REDIS_URL is the unchanged REDIS_URL we get from env vars, to be used later with RQ
-_REDIS_URL = os.environ.get("REDASH_REDIS_URL", os.environ.get("REDIS_URL", "redis://localhost:6379/0"))
+_REDIS_URL = os.environ.get(
+ "REDASH_REDIS_URL", os.environ.get("REDIS_URL", "redis://localhost:6379/0")
+)
# This is the one to use for Redash' own connection:
REDIS_URL = add_decode_responses_to_redis_url(_REDIS_URL)
PROXIES_COUNT = int(os.environ.get("REDASH_PROXIES_COUNT", "1"))
@@ -33,62 +33,77 @@
)
SQLALCHEMY_MAX_OVERFLOW = int_or_none(os.environ.get("SQLALCHEMY_MAX_OVERFLOW"))
SQLALCHEMY_POOL_SIZE = int_or_none(os.environ.get("SQLALCHEMY_POOL_SIZE"))
-SQLALCHEMY_DISABLE_POOL = parse_boolean(os.environ.get("SQLALCHEMY_DISABLE_POOL", "false"))
-SQLALCHEMY_ENABLE_POOL_PRE_PING = parse_boolean(os.environ.get("SQLALCHEMY_ENABLE_POOL_PRE_PING", "false"))
+SQLALCHEMY_DISABLE_POOL = parse_boolean(
+ os.environ.get("SQLALCHEMY_DISABLE_POOL", "false")
+)
+SQLALCHEMY_ENABLE_POOL_PRE_PING = parse_boolean(
+ os.environ.get("SQLALCHEMY_ENABLE_POOL_PRE_PING", "false")
+)
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ECHO = False
RQ_REDIS_URL = os.environ.get("RQ_REDIS_URL", _REDIS_URL)
# The following enables periodic job (every 5 minutes) of removing unused query results.
-QUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_ENABLED", "true"))
-QUERY_RESULTS_CLEANUP_COUNT = int(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_COUNT", "100"))
-QUERY_RESULTS_CLEANUP_MAX_AGE = int(os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_MAX_AGE", "7"))
-
-QUERY_RESULTS_EXPIRED_TTL_ENABLED = parse_boolean(os.environ.get("REDASH_QUERY_RESULTS_EXPIRED_TTL_ENABLED", "false"))
-# default set query results expired ttl 86400 seconds
-QUERY_RESULTS_EXPIRED_TTL = int(os.environ.get("REDASH_QUERY_RESULTS_EXPIRED_TTL", "86400"))
+QUERY_RESULTS_CLEANUP_ENABLED = parse_boolean(
+ os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_ENABLED", "true")
+)
+QUERY_RESULTS_CLEANUP_COUNT = int(
+ os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_COUNT", "100")
+)
+QUERY_RESULTS_CLEANUP_MAX_AGE = int(
+ os.environ.get("REDASH_QUERY_RESULTS_CLEANUP_MAX_AGE", "7")
+)
-SCHEMAS_REFRESH_SCHEDULE = int(os.environ.get("REDASH_SCHEMAS_REFRESH_SCHEDULE", 30))
-SCHEMAS_REFRESH_TIMEOUT = int(os.environ.get("REDASH_SCHEMAS_REFRESH_TIMEOUT", 300))
+SCHEMAS_REFRESH_SCHEDULE = int(os.environ.get("REDASH_SCHEMAS_REFRESH_SCHEDULE", 360))
+SCHEMAS_REFRESH_QUEUE = os.environ.get("REDASH_SCHEMAS_REFRESH_QUEUE", "schemas")
+SCHEMA_REFRESH_TIME_LIMIT = int(
+ os.environ.get("REDASH_SCHEMA_REFRESH_TIME_LIMIT", 3600)
+)
AUTH_TYPE = os.environ.get("REDASH_AUTH_TYPE", "api_key")
-INVITATION_TOKEN_MAX_AGE = int(os.environ.get("REDASH_INVITATION_TOKEN_MAX_AGE", 60 * 60 * 24 * 7))
+INVITATION_TOKEN_MAX_AGE = int(
+ os.environ.get("REDASH_INVITATION_TOKEN_MAX_AGE", 60 * 60 * 24 * 7)
+)
# The secret key to use in the Flask app for various cryptographic features
-SECRET_KEY = os.environ.get("REDASH_COOKIE_SECRET")
-
-if SECRET_KEY is None:
- raise Exception(
- "You must set the REDASH_COOKIE_SECRET environment variable. Visit http://redash.io/help/open-source/admin-guide/secrets for more information."
- )
-
+SECRET_KEY = os.environ.get("REDASH_COOKIE_SECRET", "c292a0a3aa32397cdb050e233733900f")
# The secret key to use when encrypting data source options
DATASOURCE_SECRET_KEY = os.environ.get("REDASH_SECRET_KEY", SECRET_KEY)
# Whether and how to redirect non-HTTP requests to HTTPS. Disabled by default.
ENFORCE_HTTPS = parse_boolean(os.environ.get("REDASH_ENFORCE_HTTPS", "false"))
-ENFORCE_HTTPS_PERMANENT = parse_boolean(os.environ.get("REDASH_ENFORCE_HTTPS_PERMANENT", "false"))
+ENFORCE_HTTPS_PERMANENT = parse_boolean(
+ os.environ.get("REDASH_ENFORCE_HTTPS_PERMANENT", "false")
+)
# Whether file downloads are enforced or not.
ENFORCE_FILE_SAVE = parse_boolean(os.environ.get("REDASH_ENFORCE_FILE_SAVE", "true"))
# Whether api calls using the json query runner will block private addresses
-ENFORCE_PRIVATE_ADDRESS_BLOCK = parse_boolean(os.environ.get("REDASH_ENFORCE_PRIVATE_IP_BLOCK", "true"))
+ENFORCE_PRIVATE_ADDRESS_BLOCK = parse_boolean(
+ os.environ.get("REDASH_ENFORCE_PRIVATE_IP_BLOCK", "true")
+)
# Whether to use secure cookies by default.
-COOKIES_SECURE = parse_boolean(os.environ.get("REDASH_COOKIES_SECURE", str(ENFORCE_HTTPS)))
+COOKIES_SECURE = parse_boolean(
+ os.environ.get("REDASH_COOKIES_SECURE", str(ENFORCE_HTTPS))
+)
# Whether the session cookie is set to secure.
-SESSION_COOKIE_SECURE = parse_boolean(os.environ.get("REDASH_SESSION_COOKIE_SECURE") or str(COOKIES_SECURE))
+SESSION_COOKIE_SECURE = parse_boolean(
+ os.environ.get("REDASH_SESSION_COOKIE_SECURE") or str(COOKIES_SECURE)
+)
# Whether the session cookie is set HttpOnly.
-SESSION_COOKIE_HTTPONLY = parse_boolean(os.environ.get("REDASH_SESSION_COOKIE_HTTPONLY", "true"))
-SESSION_EXPIRY_TIME = int(os.environ.get("REDASH_SESSION_EXPIRY_TIME", 60 * 60 * 6))
-
+SESSION_COOKIE_HTTPONLY = parse_boolean(
+ os.environ.get("REDASH_SESSION_COOKIE_HTTPONLY", "true")
+)
# Whether the session cookie is set to secure.
-REMEMBER_COOKIE_SECURE = parse_boolean(os.environ.get("REDASH_REMEMBER_COOKIE_SECURE") or str(COOKIES_SECURE))
+REMEMBER_COOKIE_SECURE = parse_boolean(
+ os.environ.get("REDASH_REMEMBER_COOKIE_SECURE") or str(COOKIES_SECURE)
+)
# Whether the remember cookie is set HttpOnly.
-REMEMBER_COOKIE_HTTPONLY = parse_boolean(os.environ.get("REDASH_REMEMBER_COOKIE_HTTPONLY", "true"))
-# The amount of time before the remember cookie expires.
-REMEMBER_COOKIE_DURATION = int(os.environ.get("REDASH_REMEMBER_COOKIE_DURATION", 60 * 60 * 24 * 31))
+REMEMBER_COOKIE_HTTPONLY = parse_boolean(
+ os.environ.get("REDASH_REMEMBER_COOKIE_HTTPONLY", "true")
+)
# Doesn't set X-Frame-Options by default since it's highly dependent
# on the specific deployment.
@@ -100,10 +115,14 @@
# Whether and how to send Strict-Transport-Security response headers.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
# for more information.
-HSTS_ENABLED = parse_boolean(os.environ.get("REDASH_HSTS_ENABLED") or str(ENFORCE_HTTPS))
+HSTS_ENABLED = parse_boolean(
+ os.environ.get("REDASH_HSTS_ENABLED") or str(ENFORCE_HTTPS)
+)
HSTS_PRELOAD = parse_boolean(os.environ.get("REDASH_HSTS_PRELOAD", "false"))
HSTS_MAX_AGE = int(os.environ.get("REDASH_HSTS_MAX_AGE", talisman.ONE_YEAR_IN_SECS))
-HSTS_INCLUDE_SUBDOMAINS = parse_boolean(os.environ.get("REDASH_HSTS_INCLUDE_SUBDOMAINS", "false"))
+HSTS_INCLUDE_SUBDOMAINS = parse_boolean(
+ os.environ.get("REDASH_HSTS_INCLUDE_SUBDOMAINS", "false")
+)
# Whether and how to send Content-Security-Policy response headers.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
@@ -116,22 +135,28 @@
"REDASH_CONTENT_SECURITY_POLICY",
"default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; font-src 'self' data:; img-src 'self' http: https: data: blob:; object-src 'none'; frame-ancestors 'none'; frame-src redash.io;",
)
-CONTENT_SECURITY_POLICY_REPORT_URI = os.environ.get("REDASH_CONTENT_SECURITY_POLICY_REPORT_URI", "")
+CONTENT_SECURITY_POLICY_REPORT_URI = os.environ.get(
+ "REDASH_CONTENT_SECURITY_POLICY_REPORT_URI", ""
+)
CONTENT_SECURITY_POLICY_REPORT_ONLY = parse_boolean(
os.environ.get("REDASH_CONTENT_SECURITY_POLICY_REPORT_ONLY", "false")
)
-CONTENT_SECURITY_POLICY_NONCE_IN = array_from_string(os.environ.get("REDASH_CONTENT_SECURITY_POLICY_NONCE_IN", ""))
+CONTENT_SECURITY_POLICY_NONCE_IN = array_from_string(
+ os.environ.get("REDASH_CONTENT_SECURITY_POLICY_NONCE_IN", "")
+)
# Whether and how to send Referrer-Policy response headers. Defaults to
# 'strict-origin-when-cross-origin'.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
# for more information.
-REFERRER_POLICY = os.environ.get("REDASH_REFERRER_POLICY", "strict-origin-when-cross-origin")
+REFERRER_POLICY = os.environ.get(
+ "REDASH_REFERRER_POLICY", "strict-origin-when-cross-origin"
+)
# Whether and how to send Feature-Policy response headers. Defaults to
# an empty value.
# See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Feature-Policy
# for more information.
-FEATURE_POLICY = os.environ.get("REDASH_FEATURE_POLICY", "")
+FEATURE_POLICY = os.environ.get("REDASH_REFERRER_POLICY", "")
MULTI_ORG = parse_boolean(os.environ.get("REDASH_MULTI_ORG", "false"))
@@ -146,10 +171,6 @@
# This setting will force the URL scheme.
SAML_SCHEME_OVERRIDE = os.environ.get("REDASH_SAML_SCHEME_OVERRIDE", "")
-SAML_ENCRYPTION_PEM_PATH = os.environ.get("REDASH_SAML_ENCRYPTION_PEM_PATH", "")
-SAML_ENCRYPTION_CERT_PATH = os.environ.get("REDASH_SAML_ENCRYPTION_CERT_PATH", "")
-SAML_ENCRYPTION_ENABLED = SAML_ENCRYPTION_PEM_PATH != "" and SAML_ENCRYPTION_CERT_PATH != ""
-
# Enables the use of an externally-provided and trusted remote user via an HTTP
# header. The "user" must be an email address.
#
@@ -173,8 +194,12 @@
# If you also set the organization setting auth_password_login_enabled to false,
# then your authentication will be seamless. Otherwise a link will be presented
# on the login page to trigger remote user auth.
-REMOTE_USER_LOGIN_ENABLED = parse_boolean(os.environ.get("REDASH_REMOTE_USER_LOGIN_ENABLED", "false"))
-REMOTE_USER_HEADER = os.environ.get("REDASH_REMOTE_USER_HEADER", "X-Forwarded-Remote-User")
+REMOTE_USER_LOGIN_ENABLED = parse_boolean(
+ os.environ.get("REDASH_REMOTE_USER_LOGIN_ENABLED", "false")
+)
+REMOTE_USER_HEADER = os.environ.get(
+ "REDASH_REMOTE_USER_HEADER", "X-Forwarded-Remote-User"
+)
# If the organization setting auth_password_login_enabled is not false, then users will still be
# able to login through Redash instead of the LDAP server
@@ -193,22 +218,34 @@
LDAP_DISPLAY_NAME_KEY = os.environ.get("REDASH_LDAP_DISPLAY_NAME_KEY", "displayName")
LDAP_EMAIL_KEY = os.environ.get("REDASH_LDAP_EMAIL_KEY", "mail")
# Prompt that should be shown above username/email field.
-LDAP_CUSTOM_USERNAME_PROMPT = os.environ.get("REDASH_LDAP_CUSTOM_USERNAME_PROMPT", "LDAP/AD/SSO username:")
+LDAP_CUSTOM_USERNAME_PROMPT = os.environ.get(
+ "REDASH_LDAP_CUSTOM_USERNAME_PROMPT", "LDAP/AD/SSO username:"
+)
# LDAP Search DN TEMPLATE (for AD this should be "(sAMAccountName=%(username)s)"")
-LDAP_SEARCH_TEMPLATE = os.environ.get("REDASH_LDAP_SEARCH_TEMPLATE", "(cn=%(username)s)")
+LDAP_SEARCH_TEMPLATE = os.environ.get(
+ "REDASH_LDAP_SEARCH_TEMPLATE", "(cn=%(username)s)"
+)
# The schema to bind to (ex. cn=users,dc=ORG,dc=local)
-LDAP_SEARCH_DN = os.environ.get("REDASH_LDAP_SEARCH_DN", os.environ.get("REDASH_SEARCH_DN"))
+LDAP_SEARCH_DN = os.environ.get(
+ "REDASH_LDAP_SEARCH_DN", os.environ.get("REDASH_SEARCH_DN")
+)
+
+STATIC_ASSETS_PATH = fix_assets_path(
+ os.environ.get("REDASH_STATIC_ASSETS_PATH", "../client/dist/")
+)
-STATIC_ASSETS_PATH = fix_assets_path(os.environ.get("REDASH_STATIC_ASSETS_PATH", "../client/dist/"))
-FLASK_TEMPLATE_PATH = fix_assets_path(os.environ.get("REDASH_FLASK_TEMPLATE_PATH", STATIC_ASSETS_PATH))
# Time limit (in seconds) for scheduled queries. Set this to -1 to execute without a time limit.
-SCHEDULED_QUERY_TIME_LIMIT = int(os.environ.get("REDASH_SCHEDULED_QUERY_TIME_LIMIT", -1))
+SCHEDULED_QUERY_TIME_LIMIT = int(
+ os.environ.get("REDASH_SCHEDULED_QUERY_TIME_LIMIT", -1)
+)
# Time limit (in seconds) for adhoc queries. Set this to -1 to execute without a time limit.
ADHOC_QUERY_TIME_LIMIT = int(os.environ.get("REDASH_ADHOC_QUERY_TIME_LIMIT", -1))
JOB_EXPIRY_TIME = int(os.environ.get("REDASH_JOB_EXPIRY_TIME", 3600 * 12))
-JOB_DEFAULT_FAILURE_TTL = int(os.environ.get("REDASH_JOB_DEFAULT_FAILURE_TTL", 7 * 24 * 60 * 60))
+JOB_DEFAULT_FAILURE_TTL = int(
+ os.environ.get("REDASH_JOB_DEFAULT_FAILURE_TTL", 7 * 24 * 60 * 60)
+)
LOG_LEVEL = os.environ.get("REDASH_LOG_LEVEL", "INFO")
LOG_STDOUT = parse_boolean(os.environ.get("REDASH_LOG_STDOUT", "false"))
@@ -235,7 +272,9 @@
MAIL_PASSWORD = os.environ.get("REDASH_MAIL_PASSWORD", None)
MAIL_DEFAULT_SENDER = os.environ.get("REDASH_MAIL_DEFAULT_SENDER", None)
MAIL_MAX_EMAILS = os.environ.get("REDASH_MAIL_MAX_EMAILS", None)
-MAIL_ASCII_ATTACHMENTS = parse_boolean(os.environ.get("REDASH_MAIL_ASCII_ATTACHMENTS", "false"))
+MAIL_ASCII_ATTACHMENTS = parse_boolean(
+ os.environ.get("REDASH_MAIL_ASCII_ATTACHMENTS", "false")
+)
def email_server_is_configured():
@@ -244,15 +283,15 @@ def email_server_is_configured():
HOST = os.environ.get("REDASH_HOST", "")
-SEND_FAILURE_EMAIL_INTERVAL = int(os.environ.get("REDASH_SEND_FAILURE_EMAIL_INTERVAL", 60))
-MAX_FAILURE_REPORTS_PER_QUERY = int(os.environ.get("REDASH_MAX_FAILURE_REPORTS_PER_QUERY", 100))
-
-ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE = os.environ.get(
- "REDASH_ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE", "Alert: {alert_name} changed status to {state}"
+SEND_FAILURE_EMAIL_INTERVAL = int(
+ os.environ.get("REDASH_SEND_FAILURE_EMAIL_INTERVAL", 60)
+)
+MAX_FAILURE_REPORTS_PER_QUERY = int(
+ os.environ.get("REDASH_MAX_FAILURE_REPORTS_PER_QUERY", 100)
)
-REDASH_ALERTS_DEFAULT_MAIL_BODY_TEMPLATE_FILE = os.environ.get(
- "REDASH_ALERTS_DEFAULT_MAIL_BODY_TEMPLATE_FILE", fix_assets_path("templates/emails/alert.html")
+ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE = os.environ.get(
+ "REDASH_ALERTS_DEFAULT_MAIL_SUBJECT_TEMPLATE", "({state}) {alert_name}"
)
# How many requests are allowed per IP to the login page before
@@ -262,17 +301,22 @@ def email_server_is_configured():
RATELIMIT_ENABLED = parse_boolean(os.environ.get("REDASH_RATELIMIT_ENABLED", "true"))
THROTTLE_LOGIN_PATTERN = os.environ.get("REDASH_THROTTLE_LOGIN_PATTERN", "50/hour")
LIMITER_STORAGE = os.environ.get("REDASH_LIMITER_STORAGE", REDIS_URL)
-THROTTLE_PASS_RESET_PATTERN = os.environ.get("REDASH_THROTTLE_PASS_RESET_PATTERN", "10/hour")
-# CORS settings for the Query Result API (and possibly future external APIs).
+# CORS settings for the Query Result API (and possbily future external APIs).
# In most cases all you need to do is set REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN
# to the calling domain (or domains in a comma separated list).
-ACCESS_CONTROL_ALLOW_ORIGIN = set_from_string(os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN", ""))
+ACCESS_CONTROL_ALLOW_ORIGIN = set_from_string(
+ os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_ORIGIN", "")
+)
ACCESS_CONTROL_ALLOW_CREDENTIALS = parse_boolean(
os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_CREDENTIALS", "false")
)
-ACCESS_CONTROL_REQUEST_METHOD = os.environ.get("REDASH_CORS_ACCESS_CONTROL_REQUEST_METHOD", "GET, POST, PUT")
-ACCESS_CONTROL_ALLOW_HEADERS = os.environ.get("REDASH_CORS_ACCESS_CONTROL_ALLOW_HEADERS", "Content-Type")
+ACCESS_CONTROL_REQUEST_METHOD = os.environ.get(
+ "REDASH_CORS_ACCESS_CONTROL_REQUEST_METHOD", "GET, POST, PUT"
+)
+ACCESS_CONTROL_ALLOW_HEADERS = os.environ.get(
+ "REDASH_CORS_ACCESS_CONTROL_ALLOW_HEADERS", "Content-Type"
+)
# Query Runners
default_query_runners = [
@@ -286,33 +330,30 @@ def email_server_is_configured():
"redash.query_runner.pg",
"redash.query_runner.url",
"redash.query_runner.influx_db",
- "redash.query_runner.influx_db_v2",
"redash.query_runner.elasticsearch",
- "redash.query_runner.elasticsearch2",
"redash.query_runner.amazon_elasticsearch",
- "redash.query_runner.trino",
"redash.query_runner.presto",
- "redash.query_runner.pinot",
"redash.query_runner.databricks",
"redash.query_runner.hive_ds",
"redash.query_runner.impala_ds",
"redash.query_runner.vertica",
"redash.query_runner.clickhouse",
- "redash.query_runner.tinybird",
"redash.query_runner.yandex_metrica",
- "redash.query_runner.yandex_disk",
"redash.query_runner.rockset",
"redash.query_runner.treasuredata",
"redash.query_runner.sqlite",
+ "redash.query_runner.dynamodb_sql",
"redash.query_runner.mssql",
"redash.query_runner.mssql_odbc",
"redash.query_runner.memsql_ds",
+ "redash.query_runner.mapd",
"redash.query_runner.jql",
"redash.query_runner.google_analytics",
"redash.query_runner.axibase_tsd",
"redash.query_runner.salesforce",
"redash.query_runner.query_results",
"redash.query_runner.prometheus",
+ "redash.query_runner.qubole",
"redash.query_runner.db2",
"redash.query_runner.druid",
"redash.query_runner.kylin",
@@ -327,26 +368,17 @@ def email_server_is_configured():
"redash.query_runner.exasol",
"redash.query_runner.cloudwatch",
"redash.query_runner.cloudwatch_insights",
- "redash.query_runner.corporate_memory",
- "redash.query_runner.sparql_endpoint",
- "redash.query_runner.excel",
- "redash.query_runner.csv",
- "redash.query_runner.databend",
- "redash.query_runner.nz",
- "redash.query_runner.arango",
- "redash.query_runner.google_analytics4",
- "redash.query_runner.google_search_console",
- "redash.query_runner.ignite",
- "redash.query_runner.oracle",
- "redash.query_runner.e6data",
- "redash.query_runner.risingwave",
]
enabled_query_runners = array_from_string(
os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join(default_query_runners))
)
-additional_query_runners = array_from_string(os.environ.get("REDASH_ADDITIONAL_QUERY_RUNNERS", ""))
-disabled_query_runners = array_from_string(os.environ.get("REDASH_DISABLED_QUERY_RUNNERS", ""))
+additional_query_runners = array_from_string(
+ os.environ.get("REDASH_ADDITIONAL_QUERY_RUNNERS", "")
+)
+disabled_query_runners = array_from_string(
+ os.environ.get("REDASH_DISABLED_QUERY_RUNNERS", "")
+)
QUERY_RUNNERS = remove(
set(disabled_query_runners),
@@ -362,34 +394,42 @@ def email_server_is_configured():
"redash.destinations.email",
"redash.destinations.slack",
"redash.destinations.webhook",
- "redash.destinations.discord",
+ "redash.destinations.hipchat",
"redash.destinations.mattermost",
"redash.destinations.chatwork",
"redash.destinations.pagerduty",
"redash.destinations.hangoutschat",
- "redash.destinations.microsoft_teams_webhook",
- "redash.destinations.asana",
- "redash.destinations.webex",
- "redash.destinations.datadog",
]
-enabled_destinations = array_from_string(os.environ.get("REDASH_ENABLED_DESTINATIONS", ",".join(default_destinations)))
-additional_destinations = array_from_string(os.environ.get("REDASH_ADDITIONAL_DESTINATIONS", ""))
+enabled_destinations = array_from_string(
+ os.environ.get("REDASH_ENABLED_DESTINATIONS", ",".join(default_destinations))
+)
+additional_destinations = array_from_string(
+ os.environ.get("REDASH_ADDITIONAL_DESTINATIONS", "")
+)
DESTINATIONS = distinct(enabled_destinations + additional_destinations)
-EVENT_REPORTING_WEBHOOKS = array_from_string(os.environ.get("REDASH_EVENT_REPORTING_WEBHOOKS", ""))
+EVENT_REPORTING_WEBHOOKS = array_from_string(
+ os.environ.get("REDASH_EVENT_REPORTING_WEBHOOKS", "")
+)
# Support for Sentry (https://getsentry.com/). Just set your Sentry DSN to enable it:
SENTRY_DSN = os.environ.get("REDASH_SENTRY_DSN", "")
SENTRY_ENVIRONMENT = os.environ.get("REDASH_SENTRY_ENVIRONMENT")
# Client side toggles:
-ALLOW_SCRIPTS_IN_USER_INPUT = parse_boolean(os.environ.get("REDASH_ALLOW_SCRIPTS_IN_USER_INPUT", "false"))
+ALLOW_SCRIPTS_IN_USER_INPUT = parse_boolean(
+ os.environ.get("REDASH_ALLOW_SCRIPTS_IN_USER_INPUT", "false")
+)
DASHBOARD_REFRESH_INTERVALS = list(
map(
int,
- array_from_string(os.environ.get("REDASH_DASHBOARD_REFRESH_INTERVALS", "60,300,600,1800,3600,43200,86400")),
+ array_from_string(
+ os.environ.get(
+ "REDASH_DASHBOARD_REFRESH_INTERVALS", "60,300,600,1800,3600,43200,86400"
+ )
+ ),
)
)
QUERY_REFRESH_INTERVALS = list(
@@ -414,13 +454,21 @@ def email_server_is_configured():
# Features:
VERSION_CHECK = parse_boolean(os.environ.get("REDASH_VERSION_CHECK", "true"))
-FEATURE_DISABLE_REFRESH_QUERIES = parse_boolean(os.environ.get("REDASH_FEATURE_DISABLE_REFRESH_QUERIES", "false"))
-FEATURE_SHOW_QUERY_RESULTS_COUNT = parse_boolean(os.environ.get("REDASH_FEATURE_SHOW_QUERY_RESULTS_COUNT", "true"))
+FEATURE_DISABLE_REFRESH_QUERIES = parse_boolean(
+ os.environ.get("REDASH_FEATURE_DISABLE_REFRESH_QUERIES", "false")
+)
+FEATURE_SHOW_QUERY_RESULTS_COUNT = parse_boolean(
+ os.environ.get("REDASH_FEATURE_SHOW_QUERY_RESULTS_COUNT", "true")
+)
FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS = parse_boolean(
- os.environ.get("REDASH_FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS", "true")
+ os.environ.get("REDASH_FEATURE_ALLOW_CUSTOM_JS_VISUALIZATIONS", "false")
+)
+FEATURE_AUTO_PUBLISH_NAMED_QUERIES = parse_boolean(
+ os.environ.get("REDASH_FEATURE_AUTO_PUBLISH_NAMED_QUERIES", "true")
+)
+FEATURE_EXTENDED_ALERT_OPTIONS = parse_boolean(
+ os.environ.get("REDASH_FEATURE_EXTENDED_ALERT_OPTIONS", "false")
)
-FEATURE_AUTO_PUBLISH_NAMED_QUERIES = parse_boolean(os.environ.get("REDASH_FEATURE_AUTO_PUBLISH_NAMED_QUERIES", "true"))
-FEATURE_EXTENDED_ALERT_OPTIONS = parse_boolean(os.environ.get("REDASH_FEATURE_EXTENDED_ALERT_OPTIONS", "false"))
# BigQuery
BIGQUERY_HTTP_TIMEOUT = int(os.environ.get("REDASH_BIGQUERY_HTTP_TIMEOUT", "600"))
@@ -428,17 +476,37 @@ def email_server_is_configured():
# Allow Parameters in Embeds
# WARNING: Deprecated!
# See https://discuss.redash.io/t/support-for-parameters-in-embedded-visualizations/3337 for more details.
-ALLOW_PARAMETERS_IN_EMBEDS = parse_boolean(os.environ.get("REDASH_ALLOW_PARAMETERS_IN_EMBEDS", "false"))
+ALLOW_PARAMETERS_IN_EMBEDS = parse_boolean(
+ os.environ.get("REDASH_ALLOW_PARAMETERS_IN_EMBEDS", "false")
+)
# Enhance schema fetching
SCHEMA_RUN_TABLE_SIZE_CALCULATIONS = parse_boolean(
os.environ.get("REDASH_SCHEMA_RUN_TABLE_SIZE_CALCULATIONS", "false")
)
+# Frequency of clearing out old schema metadata.
+SCHEMA_METADATA_TTL_DAYS = int(os.environ.get("REDASH_SCHEMA_METADATA_TTL_DAYS", 60))
+
+# Frequency of schema samples updates
+SCHEMA_SAMPLE_UPDATE_FREQUENCY_DAYS = int(
+ os.environ.get("REDASH_SCHEMA_SAMPLE_UPDATE_FREQUENCY_DAYS", 14)
+)
+SCHEMA_SAMPLE_UPDATE_TIMEOUT = int(
+ os.environ.get("REDASH_SCHEMA_SAMPLE_UPDATE_TIMEOUT", SCHEMA_REFRESH_TIME_LIMIT)
+)
+
+# Frequency of schema samples refresh when no samples are stored
+SCHEMA_SAMPLE_REFRESH_FREQUENCY_DAYS = int(
+ os.environ.get("REDASH_SCHEMA_SAMPLE_REFRESH_FREQUENCY_DAYS", 2)
+)
+
# kylin
KYLIN_OFFSET = int(os.environ.get("REDASH_KYLIN_OFFSET", 0))
KYLIN_LIMIT = int(os.environ.get("REDASH_KYLIN_LIMIT", 50000))
-KYLIN_ACCEPT_PARTIAL = parse_boolean(os.environ.get("REDASH_KYLIN_ACCEPT_PARTIAL", "false"))
+KYLIN_ACCEPT_PARTIAL = parse_boolean(
+ os.environ.get("REDASH_KYLIN_ACCEPT_PARTIAL", "false")
+)
# sqlparse
SQLPARSE_FORMAT_OPTIONS = {
@@ -447,15 +515,6 @@ def email_server_is_configured():
}
# requests
-REQUESTS_ALLOW_REDIRECTS = parse_boolean(os.environ.get("REDASH_REQUESTS_ALLOW_REDIRECTS", "false"))
-
-# Enforces CSRF token validation on API requests.
-# This is turned off by default to avoid breaking any existing deployments but it is highly recommended to turn this toggle on to prevent CSRF attacks.
-ENFORCE_CSRF = parse_boolean(os.environ.get("REDASH_ENFORCE_CSRF", "false"))
-
-# Databricks
-
-CSRF_TIME_LIMIT = int(os.environ.get("REDASH_CSRF_TIME_LIMIT", 3600 * 6))
-
-# Email blocked domains, use delimiter comma to separated multiple domains
-BLOCKED_DOMAINS = set_from_string(os.environ.get("REDASH_BLOCKED_DOMAINS", "qq.com"))
+REQUESTS_ALLOW_REDIRECTS = parse_boolean(
+ os.environ.get("REDASH_REQUESTS_ALLOW_REDIRECTS", "false")
+)
diff --git a/redash/settings/dynamic_settings.py b/redash/settings/dynamic_settings.py
index 706cf17531..836153ce36 100644
--- a/redash/settings/dynamic_settings.py
+++ b/redash/settings/dynamic_settings.py
@@ -1,6 +1,5 @@
from collections import defaultdict
-
# Replace this method with your own implementation in case you want to limit the time limit on certain queries or users.
def query_time_limit(is_scheduled, user_id, org_id):
from redash import settings
@@ -58,8 +57,3 @@ def database_key_definitions(default):
)
return definitions
-
-
-# Since you can define custom primary key types using `database_key_definitions`, you may want to load certain extensions when creating the database.
-# To do so, simply add the name of the extension you'd like to load to this list.
-database_extensions = []
diff --git a/redash/settings/helpers.py b/redash/settings/helpers.py
index 1b5a5693c9..c69f326f98 100644
--- a/redash/settings/helpers.py
+++ b/redash/settings/helpers.py
@@ -30,13 +30,6 @@ def parse_boolean(s):
raise ValueError("Invalid boolean value %r" % s)
-def cast_int_or_default(val, default=None):
- try:
- return int(val)
- except (ValueError, TypeError):
- return default
-
-
def int_or_none(value):
if value is None:
return value
diff --git a/redash/settings/organization.py b/redash/settings/organization.py
index 87a2269a8a..782802a46b 100644
--- a/redash/settings/organization.py
+++ b/redash/settings/organization.py
@@ -1,60 +1,59 @@
import os
-
from .helpers import parse_boolean
if os.environ.get("REDASH_SAML_LOCAL_METADATA_PATH") is not None:
print("DEPRECATION NOTICE:\n")
- print("SAML_LOCAL_METADATA_PATH is no longer supported. Only URL metadata is supported now, please update")
+ print(
+ "SAML_LOCAL_METADATA_PATH is no longer supported. Only URL metadata is supported now, please update"
+ )
print("your configuration and reload.")
raise SystemExit(1)
-PASSWORD_LOGIN_ENABLED = parse_boolean(os.environ.get("REDASH_PASSWORD_LOGIN_ENABLED", "true"))
+PASSWORD_LOGIN_ENABLED = parse_boolean(
+ os.environ.get("REDASH_PASSWORD_LOGIN_ENABLED", "true")
+)
-SAML_LOGIN_TYPE = os.environ.get("REDASH_SAML_AUTH_TYPE", "")
SAML_METADATA_URL = os.environ.get("REDASH_SAML_METADATA_URL", "")
SAML_ENTITY_ID = os.environ.get("REDASH_SAML_ENTITY_ID", "")
SAML_NAMEID_FORMAT = os.environ.get("REDASH_SAML_NAMEID_FORMAT", "")
-SAML_SSO_URL = os.environ.get("REDASH_SAML_SSO_URL", "")
-SAML_X509_CERT = os.environ.get("REDASH_SAML_X509_CERT", "")
-SAML_SP_SETTINGS = os.environ.get("REDASH_SAML_SP_SETTINGS", "")
-if SAML_LOGIN_TYPE == "static":
- SAML_LOGIN_ENABLED = SAML_SSO_URL != "" and SAML_METADATA_URL != ""
-else:
- SAML_LOGIN_ENABLED = SAML_METADATA_URL != ""
+SAML_LOGIN_ENABLED = SAML_METADATA_URL != ""
DATE_FORMAT = os.environ.get("REDASH_DATE_FORMAT", "DD/MM/YY")
TIME_FORMAT = os.environ.get("REDASH_TIME_FORMAT", "HH:mm")
INTEGER_FORMAT = os.environ.get("REDASH_INTEGER_FORMAT", "0,0")
FLOAT_FORMAT = os.environ.get("REDASH_FLOAT_FORMAT", "0,0.00")
-MULTI_BYTE_SEARCH_ENABLED = parse_boolean(os.environ.get("MULTI_BYTE_SEARCH_ENABLED", "false"))
+MULTI_BYTE_SEARCH_ENABLED = parse_boolean(
+ os.environ.get("MULTI_BYTE_SEARCH_ENABLED", "false")
+)
JWT_LOGIN_ENABLED = parse_boolean(os.environ.get("REDASH_JWT_LOGIN_ENABLED", "false"))
JWT_AUTH_ISSUER = os.environ.get("REDASH_JWT_AUTH_ISSUER", "")
JWT_AUTH_PUBLIC_CERTS_URL = os.environ.get("REDASH_JWT_AUTH_PUBLIC_CERTS_URL", "")
JWT_AUTH_AUDIENCE = os.environ.get("REDASH_JWT_AUTH_AUDIENCE", "")
-JWT_AUTH_ALGORITHMS = os.environ.get("REDASH_JWT_AUTH_ALGORITHMS", "HS256,RS256,ES256").split(",")
+JWT_AUTH_ALGORITHMS = os.environ.get(
+ "REDASH_JWT_AUTH_ALGORITHMS", "HS256,RS256,ES256"
+).split(",")
JWT_AUTH_COOKIE_NAME = os.environ.get("REDASH_JWT_AUTH_COOKIE_NAME", "")
JWT_AUTH_HEADER_NAME = os.environ.get("REDASH_JWT_AUTH_HEADER_NAME", "")
-FEATURE_SHOW_PERMISSIONS_CONTROL = parse_boolean(os.environ.get("REDASH_FEATURE_SHOW_PERMISSIONS_CONTROL", "false"))
+FEATURE_SHOW_PERMISSIONS_CONTROL = parse_boolean(
+ os.environ.get("REDASH_FEATURE_SHOW_PERMISSIONS_CONTROL", "false")
+)
SEND_EMAIL_ON_FAILED_SCHEDULED_QUERIES = parse_boolean(
os.environ.get("REDASH_SEND_EMAIL_ON_FAILED_SCHEDULED_QUERIES", "false")
)
-HIDE_PLOTLY_MODE_BAR = parse_boolean(os.environ.get("HIDE_PLOTLY_MODE_BAR", "false"))
-DISABLE_PUBLIC_URLS = parse_boolean(os.environ.get("REDASH_DISABLE_PUBLIC_URLS", "false"))
+HIDE_PLOTLY_MODE_BAR = parse_boolean(
+ os.environ.get("HIDE_PLOTLY_MODE_BAR", "false")
+)
settings = {
"beacon_consent": None,
"auth_password_login_enabled": PASSWORD_LOGIN_ENABLED,
"auth_saml_enabled": SAML_LOGIN_ENABLED,
- "auth_saml_type": SAML_LOGIN_TYPE,
"auth_saml_entity_id": SAML_ENTITY_ID,
"auth_saml_metadata_url": SAML_METADATA_URL,
"auth_saml_nameid_format": SAML_NAMEID_FORMAT,
- "auth_saml_sso_url": SAML_SSO_URL,
- "auth_saml_x509_cert": SAML_X509_CERT,
- "auth_saml_sp_settings": SAML_SP_SETTINGS,
"date_format": DATE_FORMAT,
"time_format": TIME_FORMAT,
"integer_format": INTEGER_FORMAT,
@@ -70,5 +69,4 @@
"feature_show_permissions_control": FEATURE_SHOW_PERMISSIONS_CONTROL,
"send_email_on_failed_scheduled_queries": SEND_EMAIL_ON_FAILED_SCHEDULED_QUERIES,
"hide_plotly_mode_bar": HIDE_PLOTLY_MODE_BAR,
- "disable_public_urls": DISABLE_PUBLIC_URLS,
}
diff --git a/redash/tasks/__init__.py b/redash/tasks/__init__.py
index 4186d0e270..5d8b8023f4 100644
--- a/redash/tasks/__init__.py
+++ b/redash/tasks/__init__.py
@@ -1,31 +1,33 @@
-from rq.connections import pop_connection, push_connection
-
-from redash import rq_redis_connection
-from redash.tasks.alerts import check_alerts_for_query
-from redash.tasks.failure_report import send_aggregated_errors
-from redash.tasks.general import (
+from .general import (
record_event,
+ version_check,
send_mail,
sync_user_details,
- version_check,
+ purge_failed_jobs,
)
-from redash.tasks.queries import (
- cleanup_query_results,
- empty_schedules,
+from .queries import (
enqueue_query,
execute_query,
refresh_queries,
refresh_schemas,
+ refresh_schema,
+ cleanup_query_results,
+ empty_schedules,
+ cleanup_schema_metadata,
+ refresh_samples,
+ update_sample,
remove_ghost_locks,
)
-from redash.tasks.schedule import (
- periodic_job_definitions,
- rq_scheduler,
- schedule_periodic_jobs,
-)
-from redash.tasks.worker import Job, Queue, Worker
+from .alerts import check_alerts_for_query
+from .failure_report import send_aggregated_errors
+from .worker import Worker, Queue, Job
+from .schedule import rq_scheduler, schedule_periodic_jobs, periodic_job_definitions
+
+from redash import rq_redis_connection
+from rq.connections import push_connection, pop_connection
def init_app(app):
app.before_request(lambda: push_connection(rq_redis_connection))
app.teardown_request(lambda _: pop_connection())
+
diff --git a/redash/tasks/alerts.py b/redash/tasks/alerts.py
index 2e8093a0ac..9fea0e4e22 100644
--- a/redash/tasks/alerts.py
+++ b/redash/tasks/alerts.py
@@ -1,32 +1,38 @@
-import datetime
-
from flask import current_app
-
+import datetime
+from redash.worker import job, get_job_logger
from redash import models, utils
-from redash.worker import get_job_logger, job
+
logger = get_job_logger(__name__)
-def notify_subscriptions(alert, new_state, metadata):
+def notify_subscriptions(alert, new_state):
host = utils.base_url(alert.query_rel.org)
for subscription in alert.subscriptions:
try:
- subscription.notify(alert, alert.query_rel, subscription.user, new_state, current_app, host, metadata)
- except Exception:
+ subscription.notify(
+ alert, alert.query_rel, subscription.user, new_state, current_app, host
+ )
+ except Exception as e:
logger.exception("Error with processing destination")
def should_notify(alert, new_state):
passed_rearm_threshold = False
if alert.rearm and alert.last_triggered_at:
- passed_rearm_threshold = alert.last_triggered_at + datetime.timedelta(seconds=alert.rearm) < utils.utcnow()
+ passed_rearm_threshold = (
+ alert.last_triggered_at + datetime.timedelta(seconds=alert.rearm)
+ < utils.utcnow()
+ )
- return new_state != alert.state or (alert.state == models.Alert.TRIGGERED_STATE and passed_rearm_threshold)
+ return new_state != alert.state or (
+ alert.state == models.Alert.TRIGGERED_STATE and passed_rearm_threshold
+ )
@job("default", timeout=300)
-def check_alerts_for_query(query_id, metadata):
+def check_alerts_for_query(query_id):
logger.debug("Checking query %d for alerts", query_id)
query = models.Query.query.get(query_id)
@@ -43,12 +49,17 @@ def check_alerts_for_query(query_id, metadata):
alert.last_triggered_at = utils.utcnow()
models.db.session.commit()
- if old_state == models.Alert.UNKNOWN_STATE and new_state == models.Alert.OK_STATE:
- logger.debug("Skipping notification (previous state was unknown and now it's ok).")
+ if (
+ old_state == models.Alert.UNKNOWN_STATE
+ and new_state == models.Alert.OK_STATE
+ ):
+ logger.debug(
+ "Skipping notification (previous state was unknown and now it's ok)."
+ )
continue
if alert.muted:
logger.debug("Skipping notification (alert muted).")
continue
- notify_subscriptions(alert, new_state, metadata)
+ notify_subscriptions(alert, new_state)
diff --git a/redash/tasks/databricks.py b/redash/tasks/databricks.py
index a9051a3825..53e68d01f5 100644
--- a/redash/tasks/databricks.py
+++ b/redash/tasks/databricks.py
@@ -1,54 +1,22 @@
-from redash import models, redis_connection
-from redash.tasks.worker import Queue
-from redash.utils import json_dumps
+from rq.registry import FailedJobRegistry
+from redash import models
from redash.worker import job
-
-DATABRICKS_REDIS_EXPIRATION_TIME = 3600
-
-
-@job("schemas", queue_class=Queue, at_front=True, timeout=300, ttl=90)
-def get_databricks_databases(data_source_id, redis_key):
- try:
- data_source = models.DataSource.get_by_id(data_source_id)
- databases = data_source.query_runner.get_databases()
- redis_connection.set(redis_key, json_dumps(databases))
- redis_connection.expire(redis_key, DATABRICKS_REDIS_EXPIRATION_TIME)
- return databases
- except Exception:
- return {"error": {"code": 2, "message": "Error retrieving database list."}}
+from redash.tasks.worker import Queue
@job("schemas", queue_class=Queue, at_front=True, timeout=300, ttl=90)
-def get_database_tables_with_columns(data_source_id, database_name, redis_key):
+def get_databricks_databases(data_source_id):
try:
data_source = models.DataSource.get_by_id(data_source_id)
- tables = data_source.query_runner.get_database_tables_with_columns(database_name)
- # check for tables since it doesn't return an error when the requested database doesn't exist
- if tables or redis_connection.exists(redis_key):
- redis_connection.set(redis_key, json_dumps(tables))
- redis_connection.expire(
- redis_key,
- DATABRICKS_REDIS_EXPIRATION_TIME,
- )
- return {"schema": tables, "has_columns": True}
+ return data_source.query_runner.get_databases()
except Exception:
return {"error": {"code": 2, "message": "Error retrieving schema."}}
@job("schemas", queue_class=Queue, at_front=True, timeout=300, ttl=90)
-def get_databricks_tables(data_source_id, database_name):
+def get_databricks_schema(data_source_id, database_name):
try:
data_source = models.DataSource.get_by_id(data_source_id)
- tables = data_source.query_runner.get_database_tables_with_columns(database_name)
- return {"schema": tables, "has_columns": False}
+ return data_source.query_runner.get_database_schema(database_name)
except Exception:
return {"error": {"code": 2, "message": "Error retrieving schema."}}
-
-
-@job("schemas", queue_class=Queue, at_front=True, timeout=300, ttl=90)
-def get_databricks_table_columns(data_source_id, database_name, table_name):
- try:
- data_source = models.DataSource.get_by_id(data_source_id)
- return data_source.query_runner.get_table_columns(database_name, table_name)
- except Exception:
- return {"error": {"code": 2, "message": "Error retrieving table columns."}}
diff --git a/redash/tasks/failure_report.py b/redash/tasks/failure_report.py
index 4b8de625d8..0339f3107e 100644
--- a/redash/tasks/failure_report.py
+++ b/redash/tasks/failure_report.py
@@ -1,10 +1,9 @@
import datetime
import re
from collections import Counter
-
-from redash import models, redis_connection, settings
from redash.tasks.general import send_mail
-from redash.utils import base_url, json_dumps, json_loads, render_template
+from redash import redis_connection, settings, models
+from redash.utils import json_dumps, json_loads, base_url, render_template
from redash.worker import get_job_logger
logger = get_job_logger(__name__)
@@ -54,11 +53,13 @@ def send_failure_report(user_id):
"base_url": base_url(user.org),
}
- subject = f"Redash failed to execute {len(unique_errors.keys())} of your scheduled queries"
+ subject = "Redash failed to execute {} of your scheduled queries".format(
+ len(unique_errors.keys())
+ )
html, text = [
render_template("emails/failures.{}".format(f), context)
for f in ["html", "txt"]
- ] # fmt: skip
+ ]
send_mail.delay([user.email], subject, html, text)
@@ -67,7 +68,9 @@ def send_failure_report(user_id):
def notify_of_failure(message, query):
subscribed = query.org.get_setting("send_email_on_failed_scheduled_queries")
- exceeded_threshold = query.schedule_failures >= settings.MAX_FAILURE_REPORTS_PER_QUERY
+ exceeded_threshold = (
+ query.schedule_failures >= settings.MAX_FAILURE_REPORTS_PER_QUERY
+ )
if subscribed and not query.user.is_disabled and not exceeded_threshold:
redis_connection.lpush(
@@ -78,7 +81,9 @@ def notify_of_failure(message, query):
"name": query.name,
"message": message,
"schedule_failures": query.schedule_failures,
- "failed_at": datetime.datetime.utcnow().strftime("%B %d, %Y %I:%M%p UTC"),
+ "failed_at": datetime.datetime.utcnow().strftime(
+ "%B %d, %Y %I:%M%p UTC"
+ ),
}
),
)
diff --git a/redash/tasks/general.py b/redash/tasks/general.py
index 6c888d8832..5b3fcca496 100644
--- a/redash/tasks/general.py
+++ b/redash/tasks/general.py
@@ -1,12 +1,16 @@
import requests
-from flask_mail import Message
+from datetime import datetime
-from redash import mail, models, settings
+from flask_mail import Message
+from rq import Connection, Queue
+from rq.registry import FailedJobRegistry
+from rq.job import Job
+from redash import mail, models, settings, rq_redis_connection
from redash.models import users
-from redash.query_runner import NotSupported
-from redash.tasks.worker import Queue
from redash.version_check import run_version_check
-from redash.worker import get_job_logger, job
+from redash.worker import job, get_job_logger, default_operational_queues
+from redash.tasks.worker import Queue
+from redash.query_runner import NotSupported
logger = get_job_logger(__name__)
@@ -48,7 +52,7 @@ def subscribe(form):
"security_notifications": form["security_notifications"],
"newsletter": form["newsletter"],
}
- requests.post("https://version.redash.io/subscribe", json=data)
+ requests.post("https://beacon.redash.io/subscribe", json=data)
@job("emails")
@@ -72,7 +76,7 @@ def test_connection(data_source_id):
return True
-@job("schemas", queue_class=Queue, at_front=True, timeout=settings.SCHEMAS_REFRESH_TIMEOUT, ttl=90)
+@job("schemas", queue_class=Queue, at_front=True, timeout=300, ttl=90)
def get_schema(data_source_id, refresh):
try:
data_source = models.DataSource.get_by_id(data_source_id)
@@ -84,9 +88,41 @@ def get_schema(data_source_id, refresh):
"message": "Data source type does not support retrieving schema",
}
}
- except Exception as e:
- return {"error": {"code": 2, "message": "Error retrieving schema", "details": str(e)}}
+ except Exception:
+ return {"error": {"code": 2, "message": "Error retrieving schema."}}
def sync_user_details():
users.sync_last_active_at()
+
+
+def purge_failed_jobs():
+ with Connection(rq_redis_connection):
+ queues = [q for q in Queue.all() if q.name not in default_operational_queues]
+ for queue in queues:
+ failed_job_ids = FailedJobRegistry(queue=queue).get_job_ids()
+ failed_jobs = Job.fetch_many(failed_job_ids, rq_redis_connection)
+ stale_jobs = []
+ for failed_job in failed_jobs:
+ # the job may not actually exist anymore in Redis
+ if not failed_job:
+ continue
+ # the job could have an empty ended_at value in case
+ # of a worker dying before it can save the ended_at value,
+ # in which case we also consider them stale
+ if not failed_job.ended_at:
+ stale_jobs.append(failed_job)
+ elif (
+ datetime.utcnow() - failed_job.ended_at
+ ).total_seconds() > settings.JOB_DEFAULT_FAILURE_TTL:
+ stale_jobs.append(failed_job)
+
+ for stale_job in stale_jobs:
+ stale_job.delete()
+
+ if stale_jobs:
+ logger.info(
+ "Purged %d old failed jobs from the %s queue.",
+ len(stale_jobs),
+ queue.name,
+ )
diff --git a/redash/tasks/queries/__init__.py b/redash/tasks/queries/__init__.py
index 6ca0132db2..255c9570d7 100644
--- a/redash/tasks/queries/__init__.py
+++ b/redash/tasks/queries/__init__.py
@@ -1,8 +1,10 @@
-from .execution import enqueue_query, execute_query
from .maintenance import (
- cleanup_query_results,
- empty_schedules,
refresh_queries,
+ refresh_schema,
refresh_schemas,
+ cleanup_query_results,
+ empty_schedules,
remove_ghost_locks,
)
+from .execution import execute_query, enqueue_query
+from .samples import cleanup_schema_metadata, refresh_samples, update_sample
diff --git a/redash/tasks/queries/execution.py b/redash/tasks/queries/execution.py
index a863903cdb..b3722438ae 100644
--- a/redash/tasks/queries/execution.py
+++ b/redash/tasks/queries/execution.py
@@ -1,20 +1,18 @@
import signal
-import sys
import time
-from collections import deque
-
import redis
+
from rq import get_current_job
-from rq.exceptions import NoSuchJobError
from rq.job import JobStatus
from rq.timeouts import JobTimeoutException
+from rq.exceptions import NoSuchJobError
from redash import models, redis_connection, settings
from redash.query_runner import InterruptException
+from redash.tasks.worker import Queue, Job
from redash.tasks.alerts import check_alerts_for_query
from redash.tasks.failure_report import track_failure
-from redash.tasks.worker import Job, Queue
-from redash.utils import gen_query_hash, utcnow
+from redash.utils import gen_query_hash, json_dumps, utcnow
from redash.worker import get_job_logger
logger = get_job_logger(__name__)
@@ -29,7 +27,9 @@ def _unlock(query_hash, data_source_id):
redis_connection.delete(_job_lock_id(query_hash, data_source_id))
-def enqueue_query(query, data_source, user_id, is_api_key=False, scheduled_query=None, metadata={}):
+def enqueue_query(
+ query, data_source, user_id, is_api_key=False, scheduled_query=None, metadata={}
+):
query_hash = gen_query_hash(query)
logger.info("Inserting job for %s with metadata=%s", query_hash, metadata)
try_count = 0
@@ -57,7 +57,7 @@ def enqueue_query(query, data_source, user_id, is_api_key=False, scheduled_query
if job_complete:
message = "job found is complete (%s)" % status
elif job_cancelled:
- message = "job found has been cancelled"
+ message = "job found has ben cancelled"
except NoSuchJobError:
message = "job found has expired"
job_exists = False
@@ -79,7 +79,9 @@ def enqueue_query(query, data_source, user_id, is_api_key=False, scheduled_query
queue_name = data_source.queue_name
scheduled_query_id = None
- time_limit = settings.dynamic_settings.query_time_limit(scheduled_query, user_id, data_source.org_id)
+ time_limit = settings.dynamic_settings.query_time_limit(
+ scheduled_query, user_id, data_source.org_id
+ )
metadata["Queue"] = queue_name
queue = Queue(queue_name)
@@ -88,12 +90,11 @@ def enqueue_query(query, data_source, user_id, is_api_key=False, scheduled_query
"scheduled_query_id": scheduled_query_id,
"is_api_key": is_api_key,
"job_timeout": time_limit,
- "failure_ttl": settings.JOB_DEFAULT_FAILURE_TTL,
"meta": {
"data_source_id": data_source.id,
"org_id": data_source.org_id,
"scheduled": scheduled_query_id is not None,
- "query_id": metadata.get("query_id"),
+ "query_id": metadata.get("Query ID"),
"user_id": user_id,
},
}
@@ -101,7 +102,9 @@ def enqueue_query(query, data_source, user_id, is_api_key=False, scheduled_query
if not scheduled_query:
enqueue_kwargs["result_ttl"] = settings.JOB_EXPIRY_TIME
- job = queue.enqueue(execute_query, query, data_source.id, metadata, **enqueue_kwargs)
+ job = queue.enqueue(
+ execute_query, query, data_source.id, metadata, **enqueue_kwargs
+ )
logger.info("[%s] Created new job: %s", query_hash, job.id)
pipe.set(
@@ -114,8 +117,6 @@ def enqueue_query(query, data_source, user_id, is_api_key=False, scheduled_query
except redis.WatchError:
continue
- finally:
- pipe.reset()
if not job:
logger.error("[Manager][%s] Failed adding job for query.", query_hash)
@@ -147,52 +148,24 @@ def _resolve_user(user_id, is_api_key, query_id):
return None
-def _get_size_iterative(dict_obj):
- """Iteratively finds size of objects in bytes"""
- seen = set()
- size = 0
- objects = deque([dict_obj])
-
- while objects:
- current = objects.popleft()
- if id(current) in seen:
- continue
- seen.add(id(current))
- size += sys.getsizeof(current)
-
- if isinstance(current, dict):
- objects.extend(current.keys())
- objects.extend(current.values())
- elif hasattr(current, "__dict__"):
- objects.append(current.__dict__)
- elif hasattr(current, "__iter__") and not isinstance(current, (str, bytes, bytearray)):
- objects.extend(current)
-
- return size
-
-
-class QueryExecutor:
- def __init__(self, query, data_source_id, user_id, is_api_key, metadata, is_scheduled_query):
+class QueryExecutor(object):
+ def __init__(
+ self, query, data_source_id, user_id, is_api_key, metadata, scheduled_query
+ ):
self.job = get_current_job()
self.query = query
self.data_source_id = data_source_id
self.metadata = metadata
self.data_source = self._load_data_source()
- self.query_id = metadata.get("query_id")
- self.user = _resolve_user(user_id, is_api_key, metadata.get("query_id"))
- self.query_model = (
- models.Query.query.get(self.query_id)
- if self.query_id and self.query_id != "adhoc"
- else None
- ) # fmt: skip
+ self.user = _resolve_user(user_id, is_api_key, metadata.get("Query ID"))
# Close DB connection to prevent holding a connection for a long time while the query is executing.
models.db.session.close()
self.query_hash = gen_query_hash(self.query)
- self.is_scheduled_query = is_scheduled_query
- if self.is_scheduled_query:
- # Load existing tracker or create a new one if the job was created before code update:
- models.scheduled_queries_executions.update(self.query_model.id)
+ self.scheduled_query = scheduled_query
+ # Load existing tracker or create a new one if the job was created before code update:
+ if scheduled_query:
+ models.scheduled_queries_executions.update(scheduled_query.id)
def run(self):
signal.signal(signal.SIGINT, signal_handler)
@@ -221,7 +194,7 @@ def run(self):
"job=execute_query query_hash=%s ds_id=%d data_length=%s error=[%s]",
self.query_hash,
self.data_source_id,
- data and _get_size_iterative(data),
+ data and len(data),
error,
)
@@ -229,16 +202,20 @@ def run(self):
if error is not None and data is None:
result = QueryExecutionError(error)
- if self.is_scheduled_query:
- self.query_model = models.db.session.merge(self.query_model, load=False)
- track_failure(self.query_model, error)
+ if self.scheduled_query is not None:
+ self.scheduled_query = models.db.session.merge(
+ self.scheduled_query, load=False
+ )
+ track_failure(self.scheduled_query, error)
raise result
else:
- if self.query_model and self.query_model.schedule_failures > 0:
- self.query_model = models.db.session.merge(self.query_model, load=False)
- self.query_model.schedule_failures = 0
- self.query_model.skip_updated_at = True
- models.db.session.add(self.query_model)
+ if self.scheduled_query and self.scheduled_query.schedule_failures > 0:
+ self.scheduled_query = models.db.session.merge(
+ self.scheduled_query, load=False
+ )
+ self.scheduled_query.schedule_failures = 0
+ self.scheduled_query.skip_updated_at = True
+ models.db.session.add(self.scheduled_query)
query_result = models.QueryResult.store_result(
self.data_source.org_id,
@@ -255,7 +232,7 @@ def run(self):
models.db.session.commit() # make sure that alert sees the latest query result
self._log_progress("checking_alerts")
for query_id in updated_query_ids:
- check_alerts_for_query.delay(query_id, self.metadata)
+ check_alerts_for_query.delay(query_id)
self._log_progress("finished")
result = query_result.id
@@ -265,21 +242,21 @@ def run(self):
def _annotate_query(self, query_runner):
self.metadata["Job ID"] = self.job.id
self.metadata["Query Hash"] = self.query_hash
- self.metadata["Scheduled"] = self.is_scheduled_query
+ self.metadata["Scheduled"] = self.scheduled_query is not None
return query_runner.annotate_query(self.query, self.metadata)
def _log_progress(self, state):
logger.info(
- "job=execute_query state=%s query_hash=%s type=%s ds_id=%d "
- "job_id=%s queue=%s query_id=%s username=%s", # fmt: skip
+ "job=execute_query state=%s query_hash=%s type=%s ds_id=%d "
+ "job_id=%s queue=%s query_id=%s username=%s",
state,
self.query_hash,
self.data_source.type,
self.data_source.id,
self.job.id,
self.metadata.get("Queue", "unknown"),
- self.metadata.get("query_id", "unknown"),
+ self.metadata.get("Query ID", "unknown"),
self.metadata.get("Username", "unknown"),
)
@@ -298,14 +275,14 @@ def execute_query(
scheduled_query_id=None,
is_api_key=False,
):
+ if scheduled_query_id is not None:
+ scheduled_query = models.Query.query.get(scheduled_query_id)
+ else:
+ scheduled_query = None
+
try:
return QueryExecutor(
- query,
- data_source_id,
- user_id,
- is_api_key,
- metadata,
- scheduled_query_id is not None,
+ query, data_source_id, user_id, is_api_key, metadata, scheduled_query
).run()
except QueryExecutionError as e:
models.db.session.rollback()
diff --git a/redash/tasks/queries/maintenance.py b/redash/tasks/queries/maintenance.py
index bca3168c38..fb32053add 100644
--- a/redash/tasks/queries/maintenance.py
+++ b/redash/tasks/queries/maintenance.py
@@ -2,18 +2,19 @@
import time
from rq.timeouts import JobTimeoutException
-
from redash import models, redis_connection, settings, statsd_client
from redash.models.parameterized_query import (
InvalidParameterError,
QueryDetachedFromDataSourceError,
)
-from redash.monitor import rq_job_ids
from redash.tasks.failure_report import track_failure
+from redash.tasks.queries.samples import refresh_samples
from redash.utils import json_dumps, sentry
from redash.worker import get_job_logger, job
+from redash.monitor import rq_job_ids
from .execution import enqueue_query
+from .samples import truncate_long_string
logger = get_job_logger(__name__)
@@ -57,14 +58,16 @@ def _apply_default_parameters(query):
try:
return query.parameterized.apply(parameters).query
except InvalidParameterError as e:
- error = f"Skipping refresh of {query.id} because of invalid parameters: {str(e)}"
+ error = u"Skipping refresh of {} because of invalid parameters: {}".format(
+ query.id, str(e)
+ )
track_failure(query, error)
raise
except QueryDetachedFromDataSourceError as e:
error = (
- f"Skipping refresh of {query.id} because a related dropdown "
- f"query ({e.query_id}) is unattached to any datasource."
- )
+ "Skipping refresh of {} because a related dropdown "
+ "query ({}) is unattached to any datasource."
+ ).format(query.id, e.query_id)
track_failure(query, error)
raise
else:
@@ -75,13 +78,7 @@ class RefreshQueriesError(Exception):
pass
-def _apply_auto_limit(query_text, query):
- should_apply_auto_limit = query.options.get("apply_auto_limit", False)
- return query.data_source.query_runner.apply_auto_limit(query_text, should_apply_auto_limit)
-
-
def refresh_queries():
- started_at = time.time()
logger.info("Refreshing queries...")
enqueued = []
for query in models.Query.outdated_queries():
@@ -89,14 +86,12 @@ def refresh_queries():
continue
try:
- query_text = _apply_default_parameters(query)
- query_text = _apply_auto_limit(query_text, query)
enqueue_query(
- query_text,
+ _apply_default_parameters(query),
query.data_source,
query.user_id,
scheduled_query=query,
- metadata={"query_id": query.id, "Username": query.user.get_actual_user()},
+ metadata={"Query ID": query.id, "Username": "Scheduled"},
)
enqueued.append(query)
except Exception as e:
@@ -106,20 +101,19 @@ def refresh_queries():
sentry.capture_exception(error)
status = {
- "started_at": started_at,
"outdated_queries_count": len(enqueued),
"last_refresh_at": time.time(),
"query_ids": json_dumps([q.id for q in enqueued]),
}
- redis_connection.hset("redash:status", mapping=status)
+ redis_connection.hmset("redash:status", status)
logger.info("Done refreshing queries: %s" % status)
def cleanup_query_results():
"""
Job to cleanup unused query results -- such that no query links to them anymore, and older than
- settings.QUERY_RESULTS_CLEANUP_MAX_AGE (a week by default, so it's less likely to be open in someone's browser and be used).
+ settings.QUERY_RESULTS_MAX_AGE (a week by default, so it's less likely to be open in someone's browser and be used).
Each time the job deletes only settings.QUERY_RESULTS_CLEANUP_COUNT (100 by default) query results so it won't choke
the database in case of many such results.
@@ -131,9 +125,13 @@ def cleanup_query_results():
settings.QUERY_RESULTS_CLEANUP_MAX_AGE,
)
- unused_query_results = models.QueryResult.unused(settings.QUERY_RESULTS_CLEANUP_MAX_AGE)
+ unused_query_results = models.QueryResult.unused(
+ settings.QUERY_RESULTS_CLEANUP_MAX_AGE
+ )
deleted_count = models.QueryResult.query.filter(
- models.QueryResult.id.in_(unused_query_results.limit(settings.QUERY_RESULTS_CLEANUP_COUNT).subquery())
+ models.QueryResult.id.in_(
+ unused_query_results.limit(settings.QUERY_RESULTS_CLEANUP_COUNT).subquery()
+ )
).delete(synchronize_session=False)
models.db.session.commit()
logger.info("Deleted %d unused query results.", deleted_count)
@@ -157,60 +155,167 @@ def remove_ghost_locks():
logger.info("Locks found: {}, Locks removed: {}".format(len(locks), count))
-@job("schemas", timeout=settings.SCHEMAS_REFRESH_TIMEOUT)
-def refresh_schema(data_source_id):
+@job(settings.SCHEMAS_REFRESH_QUEUE, timeout=settings.SCHEMA_REFRESH_TIME_LIMIT)
+def refresh_schema(data_source_id, max_type_string_length=250):
ds = models.DataSource.get_by_id(data_source_id)
logger.info("task=refresh_schema state=start ds_id=%s", ds.id)
+ lock_key = "data_source:schema:refresh:{}:lock".format(data_source_id)
+ lock = redis_connection.lock(lock_key, timeout=settings.SCHEMA_REFRESH_TIME_LIMIT)
+ acquired = lock.acquire(blocking=False)
start_time = time.time()
- try:
- ds.get_schema(refresh=True)
- logger.info(
- "task=refresh_schema state=finished ds_id=%s runtime=%.2f",
- ds.id,
- time.time() - start_time,
- )
- statsd_client.incr("refresh_schema.success")
- except JobTimeoutException:
- logger.info(
- "task=refresh_schema state=timeout ds_id=%s runtime=%.2f",
- ds.id,
- time.time() - start_time,
- )
- statsd_client.incr("refresh_schema.timeout")
- except Exception:
- logger.warning("Failed refreshing schema for the data source: %s", ds.name, exc_info=1)
- statsd_client.incr("refresh_schema.error")
- logger.info(
- "task=refresh_schema state=failed ds_id=%s runtime=%.2f",
- ds.id,
- time.time() - start_time,
- )
+
+ if acquired:
+ logger.info("task=refresh_schema state=locked ds_id=%s", ds.id)
+ try:
+ # Stores data from the updated schema that tells us which
+ # columns and which tables currently exist
+ existing_tables_set = set()
+ existing_columns_set = set()
+
+ # Stores data that will be inserted into postgres
+ table_data = {}
+ column_data = {}
+
+ new_column_names = {}
+ new_column_metadata = {}
+
+ for table in ds.query_runner.get_schema(get_stats=True):
+ table_name = table["name"]
+ existing_tables_set.add(table_name)
+
+ table_data[table_name] = {
+ "org_id": ds.org_id,
+ "name": table_name,
+ "data_source_id": ds.id,
+ "column_metadata": "metadata" in table,
+ "exists": True,
+ }
+ new_column_names[table_name] = table["columns"]
+ new_column_metadata[table_name] = table.get("metadata", None)
+
+ models.TableMetadata.store(ds, existing_tables_set, table_data)
+
+ all_existing_persisted_tables = models.TableMetadata.query.filter(
+ models.TableMetadata.exists.is_(True),
+ models.TableMetadata.data_source_id == ds.id,
+ ).all()
+
+ for table in all_existing_persisted_tables:
+ for i, column in enumerate(new_column_names.get(table.name, [])):
+ existing_columns_set.add(column)
+ column_data[column] = {
+ "org_id": ds.org_id,
+ "table_id": table.id,
+ "name": column,
+ "type": None,
+ "exists": True,
+ }
+
+ if table.column_metadata:
+ column_type = new_column_metadata[table.name][i]["type"]
+ column_type = truncate_long_string(
+ column_type, max_type_string_length
+ )
+ column_data[column]["type"] = column_type
+
+ models.ColumnMetadata.store(table, existing_columns_set, column_data)
+
+ existing_columns_list = list(existing_columns_set)
+
+ # If a column did not exist, set the 'column_exists' flag to false.
+ models.ColumnMetadata.query.filter(
+ models.ColumnMetadata.exists.is_(True),
+ models.ColumnMetadata.table_id == table.id,
+ ~models.ColumnMetadata.name.in_(existing_columns_list),
+ ).update(
+ {"exists": False, "updated_at": models.db.func.now()},
+ synchronize_session="fetch",
+ )
+
+ # Clear the set for the next round
+ existing_columns_set.clear()
+
+ # If a table did not exist in the get_schema() response above,
+ # set the 'exists' flag to false.
+ existing_tables_list = list(existing_tables_set)
+ models.TableMetadata.query.filter(
+ models.TableMetadata.exists.is_(True),
+ models.TableMetadata.data_source_id == ds.id,
+ ~models.TableMetadata.name.in_(existing_tables_list),
+ ).update(
+ {"exists": False, "updated_at": models.db.func.now()},
+ synchronize_session="fetch",
+ )
+
+ models.db.session.commit()
+
+ logger.info("task=refresh_schema state=caching ds_id=%s", ds.id)
+ ds.schema_cache.populate(forced=True)
+ logger.info("task=refresh_schema state=cached ds_id=%s", ds.id)
+
+ logger.info(
+ "task=refresh_schema state=finished ds_id=%s runtime=%.2f",
+ ds.id,
+ time.time() - start_time,
+ )
+ statsd_client.incr("refresh_schema.success")
+ except JobTimeoutException:
+ logger.info(
+ "task=refresh_schema state=timeout ds_id=%s runtime=%.2f",
+ ds.id,
+ time.time() - start_time,
+ )
+ statsd_client.incr("refresh_schema.timeout")
+ except Exception:
+ logger.warning(
+ "Failed refreshing schema for the data source: %s", ds.name, exc_info=1
+ )
+ statsd_client.incr("refresh_schema.error")
+ logger.info(
+ "task=refresh_schema state=failed ds_id=%s runtime=%.2f",
+ ds.id,
+ time.time() - start_time,
+ )
+ finally:
+ lock.release()
+ logger.info("task=refresh_schema state=unlocked ds_id=%s", ds.id)
+ else:
+ logger.info("task=refresh_schema state=alreadylocked ds_id=%s", ds.id)
def refresh_schemas():
"""
Refreshes the data sources schemas.
"""
- blacklist = [int(ds_id) for ds_id in redis_connection.smembers("data_sources:schema:blacklist") if ds_id]
+ blacklist = [
+ int(ds_id)
+ for ds_id in redis_connection.smembers("data_sources:schema:blacklist")
+ if ds_id
+ ]
global_start_time = time.time()
- logger.info("task=refresh_schemas state=start")
+ logger.info(u"task=refresh_schemas state=start")
for ds in models.DataSource.query:
if ds.paused:
logger.info(
- "task=refresh_schema state=skip ds_id=%s reason=paused(%s)",
+ u"task=refresh_schema state=skip ds_id=%s reason=paused(%s)",
ds.id,
ds.pause_reason,
)
elif ds.id in blacklist:
- logger.info("task=refresh_schema state=skip ds_id=%s reason=blacklist", ds.id)
+ logger.info(
+ u"task=refresh_schema state=skip ds_id=%s reason=blacklist", ds.id
+ )
elif ds.org.is_disabled:
- logger.info("task=refresh_schema state=skip ds_id=%s reason=org_disabled", ds.id)
+ logger.info(
+ u"task=refresh_schema state=skip ds_id=%s reason=org_disabled", ds.id
+ )
else:
refresh_schema.delay(ds.id)
+ refresh_samples.delay(ds.id, table_sample_limit=50)
logger.info(
- "task=refresh_schemas state=finish total_runtime=%.2f",
+ u"task=refresh_schemas state=finish total_runtime=%.2f",
time.time() - global_start_time,
)
diff --git a/redash/tasks/queries/samples.py b/redash/tasks/queries/samples.py
new file mode 100644
index 0000000000..0990739d77
--- /dev/null
+++ b/redash/tasks/queries/samples.py
@@ -0,0 +1,128 @@
+import datetime
+import time
+
+from redash import models, settings, utils
+from redash.query_runner import NotSupported
+from redash.worker import get_job_logger, job
+from sqlalchemy import or_
+from sqlalchemy.orm import load_only
+
+logger = get_job_logger(__name__)
+
+
+def truncate_long_string(original_str, max_length):
+ # Remove null characters so we can save as string to postgres
+ new_str = original_str.replace("\x00", "")
+
+ if new_str and len(new_str) > max_length:
+ new_str = "{}...".format(new_str[:max_length])
+ return new_str
+
+
+@job(settings.SCHEMAS_REFRESH_QUEUE, timeout=settings.SCHEMA_SAMPLE_UPDATE_TIMEOUT)
+def update_sample(data_source_id, table_name, table_id, sample_updated_at):
+ """
+ For a given table, look up a sample row for it and update
+ the "example" fields for it in the column_metadata table.
+ """
+ logger.info(u"task=update_sample state=start table_name=%s", table_name)
+ start_time = time.time()
+ ds = models.DataSource.get_by_id(data_source_id)
+
+ persisted_columns = models.ColumnMetadata.query.filter(
+ models.ColumnMetadata.exists.is_(True),
+ models.ColumnMetadata.table_id == table_id,
+ ).options(load_only("id", "name", "example"))
+
+ update_threshold = utils.utcnow() - datetime.timedelta(
+ days=settings.SCHEMA_SAMPLE_UPDATE_FREQUENCY_DAYS
+ )
+
+ first_column = persisted_columns.first()
+
+ if (
+ first_column
+ and sample_updated_at
+ and first_column.example
+ and sample_updated_at > update_threshold
+ ):
+ # Look at the first example in the persisted columns.
+ # If this is *not* empty AND sample_updated_at is recent, don't update sample
+ logger.info(
+ u"task=update_sample state=abort - recent sample exists table_name=%s",
+ table_name,
+ )
+ return
+
+ sample = None
+ try:
+ sample = ds.query_runner.get_table_sample(table_name)
+ except NotSupported:
+ logger.info(u"Unable to fetch samples for {}".format(table_name))
+
+ if not sample:
+ return
+
+ # If a column exists, add a sample to it.
+ for persisted_column in persisted_columns.all():
+ column_example = sample.get(persisted_column.name, None)
+ column_example = (
+ column_example if isinstance(column_example, str) else str(column_example)
+ ) # noqa: F821
+ persisted_column.example = truncate_long_string(column_example, 4000)
+ models.db.session.add(persisted_column)
+
+ models.db.session.commit()
+ logger.info(
+ u"task=update_sample state=finished table_name=%s runtime=%.2f",
+ table_name,
+ time.time() - start_time,
+ )
+ return sample
+
+
+@job(settings.SCHEMAS_REFRESH_QUEUE, timeout=settings.SCHEMA_REFRESH_TIME_LIMIT)
+def refresh_samples(data_source_id, table_sample_limit):
+ """
+ For a given data source, refresh the data samples stored for each
+ table. This is done for tables with no samples or samples older
+ than DAYS_AGO
+ """
+ logger.info(u"task=refresh_samples state=start ds_id=%s", data_source_id)
+ ds = models.DataSource.get_by_id(data_source_id)
+
+ if not ds.query_runner.configuration.get("samples", False):
+ return
+
+ DAYS_AGO = utils.utcnow() - datetime.timedelta(
+ days=settings.SCHEMA_SAMPLE_REFRESH_FREQUENCY_DAYS
+ )
+
+ # Find all existing tables that have an empty or old sample_updated_at
+ tables_to_sample = (
+ models.TableMetadata.query.filter(
+ models.TableMetadata.exists.is_(True),
+ models.TableMetadata.data_source_id == data_source_id,
+ or_(
+ models.TableMetadata.sample_updated_at.is_(None),
+ models.TableMetadata.sample_updated_at < DAYS_AGO,
+ ),
+ )
+ .limit(table_sample_limit)
+ .all()
+ )
+
+ tasks = []
+ for table in tables_to_sample:
+ tasks.append((ds.id, table.name, table.id, table.sample_updated_at))
+ table.sample_updated_at = models.db.func.now()
+ models.db.session.add(table)
+ models.db.session.commit()
+
+ for task_args in tasks:
+ update_sample.delay(*task_args)
+
+
+def cleanup_schema_metadata():
+ models.cleanup_data_in_table(models.TableMetadata)
+ models.cleanup_data_in_table(models.ColumnMetadata)
diff --git a/redash/tasks/schedule.py b/redash/tasks/schedule.py
index 8de5261826..15e027942b 100644
--- a/redash/tasks/schedule.py
+++ b/redash/tasks/schedule.py
@@ -1,22 +1,26 @@
+from __future__ import absolute_import
+import logging
import hashlib
import json
-import logging
from datetime import datetime, timedelta
from rq.job import Job
from rq_scheduler import Scheduler
-from redash import rq_redis_connection, settings
-from redash.tasks.failure_report import send_aggregated_errors
-from redash.tasks.general import sync_user_details, version_check
-from redash.tasks.queries import (
- cleanup_query_results,
- empty_schedules,
+from redash import extensions, settings, rq_redis_connection, statsd_client
+from redash.tasks import (
+ sync_user_details,
refresh_queries,
- refresh_schemas,
remove_ghost_locks,
+ empty_schedules,
+ refresh_schemas,
+ cleanup_schema_metadata,
+ cleanup_query_results,
+ purge_failed_jobs,
+ version_check,
+ send_aggregated_errors,
+ Queue,
)
-from redash.tasks.worker import Queue
logger = logging.getLogger(__name__)
@@ -29,7 +33,9 @@ class StatsdRecordingScheduler(Scheduler):
queue_class = Queue
-rq_scheduler = StatsdRecordingScheduler(connection=rq_redis_connection, queue_name="periodic", interval=5)
+rq_scheduler = StatsdRecordingScheduler(
+ connection=rq_redis_connection, queue_name="periodic", interval=5
+)
def job_id(kwargs):
@@ -56,7 +62,7 @@ def schedule(kwargs):
def periodic_job_definitions():
jobs = [
- {"func": refresh_queries, "timeout": 600, "interval": 30, "result_ttl": 600},
+ {"func": refresh_queries, "interval": 30, "result_ttl": 600},
{
"func": remove_ghost_locks,
"interval": timedelta(minutes=1),
@@ -67,16 +73,13 @@ def periodic_job_definitions():
"func": refresh_schemas,
"interval": timedelta(minutes=settings.SCHEMAS_REFRESH_SCHEDULE),
},
- {
- "func": sync_user_details,
- "timeout": 60,
- "interval": timedelta(minutes=1),
- "result_ttl": 600,
- },
+ {"func": sync_user_details, "timeout": 60, "interval": timedelta(minutes=1),},
+ {"func": purge_failed_jobs, "timeout": 3600, "interval": timedelta(days=1)},
{
"func": send_aggregated_errors,
"interval": timedelta(minutes=settings.SEND_FAILURE_EMAIL_INTERVAL),
},
+ {"func": cleanup_schema_metadata, "interval": timedelta(days=3)},
]
if settings.VERSION_CHECK:
@@ -88,6 +91,10 @@ def periodic_job_definitions():
# Add your own custom periodic jobs in your dynamic_settings module.
jobs.extend(settings.dynamic_settings.periodic_jobs() or [])
+ # Add periodic jobs that are shipped as part of Redash extensions
+ extensions.load_periodic_jobs(logger)
+ jobs.extend(list(extensions.periodic_jobs.values()))
+
return jobs
@@ -95,11 +102,14 @@ def schedule_periodic_jobs(jobs):
job_definitions = [prep(job) for job in jobs]
jobs_to_clean_up = Job.fetch_many(
- set([job.id for job in rq_scheduler.get_jobs()]) - set([job_id(job) for job in job_definitions]),
+ set([job.id for job in rq_scheduler.get_jobs()])
+ - set([job_id(job) for job in job_definitions]),
rq_redis_connection,
)
- jobs_to_schedule = [job for job in job_definitions if job_id(job) not in rq_scheduler]
+ jobs_to_schedule = [
+ job for job in job_definitions if job_id(job) not in rq_scheduler
+ ]
for job in jobs_to_clean_up:
logger.info("Removing %s (%s) from schedule.", job.id, job.func_name)
diff --git a/redash/tasks/worker.py b/redash/tasks/worker.py
index 004a30be8b..b1597f26ea 100644
--- a/redash/tasks/worker.py
+++ b/redash/tasks/worker.py
@@ -1,31 +1,18 @@
import errno
import os
-import signal
-import sys
-
-from rq import Queue as BaseQueue
-from rq.job import Job as BaseJob
-from rq.job import JobStatus
-from rq.timeouts import HorseMonitorTimeoutException
-from rq.utils import utcnow
-from rq.worker import (
- HerokuWorker, # HerokuWorker implements graceful shutdown on SIGTERM
- Worker,
-)
-
from redash import statsd_client
-
-# HerokuWorker does not work in OSX https://github.com/getredash/redash/issues/5413
-if sys.platform == "darwin":
- BaseWorker = Worker
-else:
- BaseWorker = HerokuWorker
+from rq import Worker as BaseWorker, Queue as BaseQueue
+from rq.utils import utcnow
+from rq.timeouts import UnixSignalDeathPenalty, HorseMonitorTimeoutException
+from rq.job import Job as BaseJob, JobStatus
class CancellableJob(BaseJob):
def cancel(self, pipeline=None):
- self.meta["cancelled"] = True
- self.save_meta()
+ # TODO - add tests that verify that queued jobs are removed from queue and running jobs are actively cancelled
+ if self.is_started:
+ self.meta["cancelled"] = True
+ self.save_meta()
super().cancel(pipeline=pipeline)
@@ -87,11 +74,11 @@ class HardLimitingWorker(BaseWorker):
"""
grace_period = 15
- queue_class = RedashQueue
+ queue_class = CancellableQueue
job_class = CancellableJob
def stop_executing_job(self, job):
- os.kill(self.horse_pid, signal.SIGINT)
+ self.kill_horse()
self.log.warning("Job %s has been cancelled.", job.id)
def soft_limit_exceeded(self, job):
@@ -113,44 +100,31 @@ def enforce_hard_limit(self, job):
)
self.kill_horse()
- def monitor_work_horse(self, job: "Job", queue: "Queue"):
+ def monitor_work_horse(self, job):
"""The worker will monitor the work horse and make sure that it
either executes successfully or the status of the job is set to
failed
-
- Args:
- job (Job): _description_
- queue (Queue): _description_
"""
self.monitor_started = utcnow()
- retpid = ret_val = rusage = None
- job.started_at = utcnow()
while True:
try:
- with self.death_penalty_class(self.job_monitoring_interval, HorseMonitorTimeoutException):
- retpid, ret_val, rusage = self.wait_for_horse()
+ with UnixSignalDeathPenalty(
+ self.job_monitoring_interval, HorseMonitorTimeoutException
+ ):
+ retpid, ret_val = os.waitpid(self._horse_pid, 0)
break
except HorseMonitorTimeoutException:
# Horse has not exited yet and is still running.
# Send a heartbeat to keep the worker alive.
- self.set_current_job_working_time((utcnow() - job.started_at).total_seconds())
+ self.heartbeat(self.job_monitoring_interval + 5)
job.refresh()
- # Kill the job from this side if something is really wrong (interpreter lock/etc).
- if job.timeout != -1 and self.current_job_working_time > (job.timeout + 60): # type: ignore
- self.heartbeat(self.job_monitoring_interval + 60)
- self.kill_horse()
- self.wait_for_horse()
- break
-
- self.maintain_heartbeats(job)
if job.is_cancelled:
self.stop_executing_job(job)
if self.soft_limit_exceeded(job):
self.enforce_hard_limit(job)
-
except OSError as e:
# In case we encountered an OSError due to EINTR (which is
# caused by a SIGINT or SIGTERM signal during
@@ -163,32 +137,29 @@ def monitor_work_horse(self, job: "Job", queue: "Queue"):
# Send a heartbeat to keep the worker alive.
self.heartbeat()
- self.set_current_job_working_time(0)
- self._horse_pid = 0 # Set horse PID to 0, horse has finished working
if ret_val == os.EX_OK: # The process exited normally.
return
-
job_status = job.get_status()
-
if job_status is None: # Job completed and its ttl has expired
return
- elif self._stopped_job_id == job.id:
- # Work-horse killed deliberately
- self.log.warning("Job stopped by user, moving job to FailedJobRegistry")
- if job.stopped_callback:
- job.execute_stopped_callback(self.death_penalty_class)
- self.handle_job_failure(job, queue=queue, exc_string="Job stopped by user, work-horse terminated.")
- elif job_status not in [JobStatus.FINISHED, JobStatus.FAILED]:
+ if job_status not in [JobStatus.FINISHED, JobStatus.FAILED]:
+
if not job.ended_at:
job.ended_at = utcnow()
# Unhandled failure: move the job to the failed queue
- signal_msg = f" (signal {os.WTERMSIG(ret_val)})" if ret_val and os.WIFSIGNALED(ret_val) else ""
- exc_string = f"Work-horse terminated unexpectedly; waitpid returned {ret_val}{signal_msg}; "
- self.log.warning("Moving job to FailedJobRegistry (%s)", exc_string)
-
- self.handle_work_horse_killed(job, retpid, ret_val, rusage)
- self.handle_job_failure(job, queue=queue, exc_string=exc_string)
+ self.log.warning(
+ (
+ "Moving job to FailedJobRegistry "
+ "(work-horse terminated unexpectedly; waitpid returned {})"
+ ).format(ret_val)
+ )
+
+ self.handle_job_failure(
+ job,
+ exc_string="Work-horse process was terminated unexpectedly "
+ "(waitpid returned %s)" % ret_val,
+ )
class RedashWorker(StatsdRecordingWorker, HardLimitingWorker):
diff --git a/redash/templates/emails/alert.html b/redash/templates/emails/alert.html
deleted file mode 100644
index a3e1ebd19f..0000000000
--- a/redash/templates/emails/alert.html
+++ /dev/null
@@ -1,92 +0,0 @@
-
-
-
-
-
-
-
- STATUS: {{ALERT_STATUS}}
-
-
-
- CONDITION:
-
- {{QUERY_RESULT_VALUE}} {{ALERT_CONDITION}} {{ALERT_THRESHOLD}}
-
-
-
-
- QUERY:
- {{QUERY_NAME}}
-
-
-
-
-
- {{#QUERY_RESULT_COLS}}
- | {{friendly_name}} |
- {{/QUERY_RESULT_COLS}}
-
-
-
-{{#QUERY_RESULT_TABLE}}
-
- {{#.}}
- | {{.}} |
- {{/.}}
-
-{{/QUERY_RESULT_TABLE}}
-
-
-
-
-
-
diff --git a/redash/templates/emails/layout.html b/redash/templates/emails/layout.html
index fef66310e1..4df65e084a 100644
--- a/redash/templates/emails/layout.html
+++ b/redash/templates/emails/layout.html
@@ -546,7 +546,7 @@
-
+
|
@@ -565,4 +565,4 @@