private _handleOnDidSashReset(): void {
		// Calculate ideal size of widget to display all text based on its contents
		let idealWidth = DEFAULT_TABS_WIDGET_WIDTH;
		const offscreenCanvas = new OffscreenCanvas(1, 1);
		const ctx = offscreenCanvas.getContext('2d');
		if (ctx) {
			const style = window.getComputedStyle(this._terminalTabTree);
			ctx.font = `${style.fontStyle} ${style.fontSize} ${style.fontFamily}`;
			const maxTextSize = this._terminalService.terminalInstances.reduce((p, c) => {
				return Math.max(p, ctx.measureText(c.title).width);
			}, 0);
			// Size to include padding + icon + a little more
			idealWidth = Math.ceil(Math.max(maxTextSize + 40, DEFAULT_TABS_WIDGET_WIDTH));
		}
		// If the size is already ideal, toggle to collapsed
		const currentWidth = Math.ceil(this._splitView.getViewSize(this._tabTreeIndex));
		if (currentWidth === idealWidth) {
			idealWidth = MIN_TABS_WIDGET_WIDTH;
		}
		this._splitView.resizeView(this._tabTreeIndex, idealWidth);
		this._updateWidgetWidth(idealWidth);
	}


private _handleOnDidSashChange(): void {
		let widgetWidth = this._splitView.getViewSize(this._tabTreeIndex);
		if (!this._width || widgetWidth <= 0) {
			return;
		}
		this._updateWidgetWidth(widgetWidth);
	}


private _updateWidgetWidth(width: number): void {
		if (width < MIDPOINT_WIDGET_WIDTH && width > MIN_TABS_WIDGET_WIDTH) {
			width = MIN_TABS_WIDGET_WIDTH;
			this._splitView.resizeView(this._tabTreeIndex, width);
		} else if (width >= MIDPOINT_WIDGET_WIDTH && width < DEFAULT_TABS_WIDGET_WIDTH) {
			width = DEFAULT_TABS_WIDGET_WIDTH;
			this._splitView.resizeView(this._tabTreeIndex, width);
		}
		this._refreshHasTextClass();
		this._rerenderTabs();
		this._storageService.store(TABS_WIDGET_WIDTH_KEY, width, StorageScope.WORKSPACE, StorageTarget.USER);
	}


private _setupSplitView(): void {
		this._register(this._splitView.onDidSashReset(() => this._handleOnDidSashReset()));
		this._register(this._splitView.onDidSashChange(() => this._handleOnDidSashChange()));

		if (this._showTabs) {
			this._addTabTree();
		}
		this._splitView.addView({
			element: this._terminalContainer,
			layout: width => this._terminalService.terminalTabs.forEach(tab => tab.layout(width, this._height || 0)),
			minimumSize: 120,
			maximumSize: Number.POSITIVE_INFINITY,
			onDidChange: () => Disposable.None,
			priority: LayoutPriority.High
		}, Sizing.Distribute, this._terminalContainerIndex);

		if (this._showTabs) {
			this._addSashListener();
		}
	}


private _addTabTree() {
		this._splitView.addView({
			element: this._tabTreeContainer,
			layout: width => this._tabsWidget.layout(this._height ? this._height - 28 : 0, width),
			minimumSize: MIN_TABS_WIDGET_WIDTH,
			maximumSize: MAX_TABS_WIDGET_WIDTH,
			onDidChange: () => Disposable.None,
			priority: LayoutPriority.Low
		}, Sizing.Distribute, this._tabTreeIndex);
		this._createButton();
		this._refreshHasTextClass();
		this._rerenderTabs();
	}


private _rerenderTabs() {
		for (const instance of this._terminalService.terminalInstances) {
			try {
				this._tabsWidget.rerender(instance);
			} catch (e) {
				this._logService.warn('Exception when rerendering new tab widget', e);
			}
		}
	}


private _addSashListener() {
		let interval: number;
		this._sashDisposables = [
			this._splitView.sashes[0].onDidStart(e => {
				interval = window.setInterval(() => {
					this._refreshHasTextClass();
					this._rerenderTabs();
				}, 100);
			}),
			this._splitView.sashes[0].onDidEnd(e => {
				window.clearInterval(interval);
				interval = 0;
			})
		];
	}


private _removeSashListener() {
		if (this._sashDisposables) {
			dispose(this._sashDisposables);
			this._sashDisposables = undefined;
		}
	}


layout(width: number, height: number): void {
		this._height = height;
		this._width = width;
		this._refreshHasTextClass();
		this._splitView.layout(width);
		if (this._showTabs) {
			this._splitView.resizeView(this._tabTreeIndex, this._getLastWidgetWidth());
		}
	}


private _refreshHasTextClass() {
		this._tabTreeContainer.classList.toggle('has-text', this._tabTreeContainer.clientWidth >= MIDPOINT_WIDGET_WIDTH);
	}


private _createButton(): void {
		const toolBar = new ToolBar(this._tabTreeContainer, this._contextMenuService);
		toolBar.setActions([
			this._instantiationService.createInstance(MenuItemAction, { id: TERMINAL_COMMAND_ID.NEW, title: nls.localize('terminal.new', "New Terminal"), icon: Codicon.plus }, undefined, undefined),
			// TODO: Bring back context menu: await this._openTabsContextMenu(e);
			this._instantiationService.createInstance(MenuItemAction, { id: TERMINAL_COMMAND_ID.NEW_WITH_PROFILE, title: nls.localize('terminal.newWithProfile', "New Terminal With Profile"), icon: Codicon.chevronDown }, undefined, undefined)
		]);
	}


private _updateTheme(theme?: IColorTheme): void {
		if (!theme) {
			theme = this._themeService.getColorTheme();
		}

		this._findWidget?.updateTheme(theme);
	}


private _attachEventListeners(parentDomElement: HTMLElement, terminalContainer: HTMLElement): void {
		this._register(dom.addDisposableListener(parentDomElement, 'mousedown', async (event: MouseEvent) => {
			if (this._terminalService.terminalInstances.length === 0) {
				return;
			}

			if (event.which === 2 && isLinux) {
				// Drop selection and focus terminal on Linux to enable middle button paste when click
				// occurs on the selection itself.
				const terminal = this._terminalService.getActiveInstance();
				if (terminal) {
					terminal.focus();
				}
			} else if (event.which === 3) {
				const rightClickBehavior = this._terminalService.configHelper.config.rightClickBehavior;
				if (rightClickBehavior === 'copyPaste' || rightClickBehavior === 'paste') {
					const terminal = this._terminalService.getActiveInstance();
					if (!terminal) {
						return;
					}

					// copyPaste: Shift+right click should open context menu
					if (rightClickBehavior === 'copyPaste' && event.shiftKey) {
						this._openContextMenu(event);
						return;
					}

					if (rightClickBehavior === 'copyPaste' && terminal.hasSelection()) {
						await terminal.copySelection();
						terminal.clearSelection();
					} else {
						if (BrowserFeatures.clipboard.readText) {
							terminal.paste();
						} else {
							this._notificationService.info(`This browser doesn't support the clipboard.readText API needed to trigger a paste, try ${isMacintosh ? '⌘' : 'Ctrl'}+V instead.`);
						}
					}
					// Clear selection after all click event bubbling is finished on Mac to prevent
					// right-click selecting a word which is seemed cannot be disabled. There is a
					// flicker when pasting but this appears to give the best experience if the
					// setting is enabled.
					if (isMacintosh) {
						setTimeout(() => {
							terminal.clearSelection();
						}, 0);
					}
					this._cancelContextMenu = true;
				}
			}
		}));
		this._register(dom.addDisposableListener(parentDomElement, 'contextmenu', (event: MouseEvent) => {
			if (!this._cancelContextMenu) {
				this._openContextMenu(event);
			}
			event.preventDefault();
			event.stopImmediatePropagation();
			this._cancelContextMenu = false;
		}));
		this._register(dom.addDisposableListener(document, 'keydown', (event: KeyboardEvent) => {
			terminalContainer.classList.toggle('alt-active', !!event.altKey);
		}));
		this._register(dom.addDisposableListener(document, 'keyup', (event: KeyboardEvent) => {
			terminalContainer.classList.toggle('alt-active', !!event.altKey);
		}));
		this._register(dom.addDisposableListener(parentDomElement, 'keyup', (event: KeyboardEvent) => {
			if (event.keyCode === 27) {
				// Keep terminal open on escape
				event.stopPropagation();
			}
		}));
		this._register(dom.addDisposableListener(parentDomElement, dom.EventType.DROP, async (e: DragEvent) => {
			if (e.target === this._parentElement || dom.isAncestor(e.target as HTMLElement, parentDomElement)) {
				if (!e.dataTransfer) {
					return;
				}

				// Check if files were dragged from the tree explorer
				let path: string | undefined;
				const resources = e.dataTransfer.getData(DataTransfers.RESOURCES);
				if (resources) {
					path = URI.parse(JSON.parse(resources)[0]).fsPath;
				} else if (e.dataTransfer.files.length > 0 && e.dataTransfer.files[0].path /* Electron only */) {
					// Check if the file was dragged from the filesystem
					path = URI.file(e.dataTransfer.files[0].path).fsPath;
				}

				if (!path) {
					return;
				}

				const terminal = this._terminalService.getActiveInstance();
				if (terminal) {
					const preparedPath = await this._terminalService.preparePathForTerminalAsync(path, terminal.shellLaunchConfig.executable, terminal.title, terminal.shellType);
					terminal.sendText(preparedPath, false);
					terminal.focus();
				}
			}
		}));
	}


private _openContextMenu(event: MouseEvent): void {
		const standardEvent = new StandardMouseEvent(event);
		const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy };

		const actions: IAction[] = [];
		const actionsDisposable = createAndFillInContextMenuActions(this._instanceMenu, undefined, actions);

		this._contextMenuService.showContextMenu({
			getAnchor: () => anchor,
			getActions: () => actions,
			getActionsContext: () => this._parentElement,
			onHide: () => actionsDisposable.dispose()
		});
	}


// private async _openTabsContextMenu(event: MouseEvent): Promise<void> {
// 	const standardEvent = new StandardMouseEvent(event);
// 	const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy };
// 	const actions: IAction[] = [];
// 	const profiles = await this._terminalService.getAvailableProfiles().filter(p => this._terminalService.configHelper.checkIsProcessLaunchSafe(undefined, p));
// 	const tabsMenu = this._dropdownMenu;
// 	for (const p of profiles) {
// 		const action = new MenuItemAction({ id: TERMINAL_COMMAND_ID.NEW_WITH_PROFILE, title: p.profileName, category: ContextMenuTabsGroup.Profile }, undefined, { arg: p, shouldForwardArgs: true }, this._contextKeyService, this._commandService);
// 		actions.push(action);
// 	}
// 	const actionsDisposable = createAndFillInContextMenuActions(tabsMenu, undefined, actions);
// 	this._contextMenuService.showContextMenu({
// 		getAnchor: () => anchor,
// 		getActions: () => actions,
// 		getActionsContext: () => this._parentElement,
// 		onHide: () => actionsDisposable.dispose()
// 	});
// }
public focusFindWidget() {
		this._findWidgetVisible.set(true);
		const activeInstance = this._terminalService.getActiveInstance();
		if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) {
			this._findWidget!.reveal(activeInstance.selection);
		} else {
			this._findWidget!.reveal();
		}
	}


public hideFindWidget() {
		this._findWidgetVisible.reset();
		this.focus();
		this._findWidget!.hide();
	}


public showFindWidget() {
		const activeInstance = this._terminalService.getActiveInstance();
		if (activeInstance && activeInstance.hasSelection() && activeInstance.selection!.indexOf('\n') === -1) {
			this._findWidget!.show(activeInstance.selection);
		} else {
			this._findWidget!.show();
		}
	}


public getFindWidget(): TerminalFindWidget {
		return this._findWidget!;
	}


public focus() {
		if (this._terminalService.connectionState === TerminalConnectionState.Connecting) {
			// If the terminal is waiting to reconnect to remote terminals, then there is no TerminalInstance yet that can
			// be focused. So wait for connection to finish, then focus.
			const activeElement = document.activeElement;
			this._register(this._terminalService.onDidChangeConnectionState(() => {
				// Only focus the terminal if the activeElement has not changed since focus() was called
				// TODO hack
				if (document.activeElement === activeElement) {
					this._focus();
				}
			}));

			return;
		}
		this._focus();
	}


private _focus() {
		this._terminalService.getActiveInstance()?.focusWhenReady();
	}


async (hre: HardhatRuntimeEnvironment) => {
  const { network } = hre

  if (network.name == 'bsc_mainnet') {
    // deploy only on bsc_mainnet
    return false
  }
  return true
}


/**
 * Database class, contains settings, key and functions
*/
export default class DataBase {
    key: string[];
    config: ConfigUser;
    static json: object;
    static globalPath: string;
    static globalPathToTheModule: string;
    static checkedFiles: Array<string>;
    static configModel: ConfigModel[];
    static config: any;
    /**
     * ! You can omit the arguments, but keep in mind that you have to specify the keys in the functions!
     * @param { string }  key
     * @param { ConfigUser } config
     */
    constructor(key?: string, config?: ConfigUser);
    constructor(config?: ConfigUser);
    private _checkKey;
    private _splitKey;
    setConfig(config: ConfigUser, local?: boolean): void;
    private _keyHandling;
    /**
      * Installing the key.
      * @param { string } key
      * @examle ```js
      * const db = require("mouse-db.json");
      * const table = new db("key");
      * table.setKey("key.me"); // key.me;
      * ```
      * @returns { string }
    */
    setKey(key: string): string | never;
    /**
      * Add key. Example: "key" => "key.me"
      * @param { string } key
      * @examle ```js
      * const db = require("mouse-db.json");
      * const table = new db("key");
      * table.addKey("me"); // key.me;for (let i = 0; i < key.length; i++) {
            // Получаем все индексы массивов в ключе.
            const keyIndices = printIndices(key[i], { functionName: "set", value, key });

            // Обрезаем индексы массивов.
            if (keyIndices.length) key[i] = key[i].slice(0, key[i].indexOf("[" + keyIndices[0] + "]"));

            // Если юзер введёт просто "[0]" вместо "key[0]"
            if (key[i].length === 0) throw new DBError("You didn't specify a key, just an array index!", { functionName: "set", value });
      * ```
      * @returns { string }
    */
    addKey(key: string): string | never;
    /**
      * Remove a certain number of keys (from the end); Example: "user.1552.profile" => "user.1552"
      * @param { number } num - How many keys to cut?
      * @examle ```js
      * const db = require("mouse-db.json");
      * const table = new db("user.1552.profile");
      * table.removeKey(2); // user
      * ```
      * @returns { string }
    */
    removeKey(num?: number): string | never;
    /**
      * Save changes to base
      * @param { any } value - The value must be supported in JSON!
      * @example ```js
      * const db = require("mouse-db.json");
      * const table = new db("user.1552.age");
      * table.set(24); // 24
    * table.setKey("user.1552.name");
    * table.set("Иван"); // Иван.
    * ```
    * @returns { any }
    */
    set(key: string, value: JSONType): any;
    set(value: JSONType): any;
    /**
        * Get data from the base.
        * @returns { any }
    */
    get(keyString?: string): JSONType | undefined;
    /**
        * Check if an item is in the database by this key.
        * @param { object } settings
        * @param { boolean } settings.checkNull - With this setting enabled, this will also be checked for null. If the database is null, it will return false.
        * @returns { Boolean }
    */
    has(): any;
    has(key: string, checkNull: boolean): any;
    has(key: string): any;
    has(checkNull: boolean): any;
    /**
        * Remove an element from the base.
        * @returns { boolean }
    */
    delete(keyString?: string): boolean;
    /**
        * Add number
        * @param { number } value
        * @returns { number }
    */
    add(key: string, ...value: Array<number>): any;
    add(...value: Array<number>): any;
    /**
        * Minus the number
        * @param { number } value
        * @returns
    */
    subtract(key: string, ...value: Array<number>): any;
    subtract(...value: Array<number>): any;
    /**
        * Add an item to the array.
        * @param { any } value
        * @returns { array<any> }
    */
    push(key: string, ...value: Array<JSONType>): any;
    push(...value: Array<JSONType>): any;
    /**
        * Alilas db.get();
        * @returns { any }
    */
    fetch(key?: string): JSONType | undefined;
    /**
        * Alilas db.has();
        * @returns { boolean }
    */
    exists(key: string, checkNull: boolean): any;
    exists(key: string): any;
    exists(checkNull: boolean): any;
}


interface ConfigModel {
    name: string;
    type: string;
    default: any;
    check: (data: any) => void;
}


interface ConfigUser {
    fileName?: string;
}


declare type ValidScalars = null | string | boolean | number;


declare type JSONType = ValidScalars | ValidScalars[] | {
    [key: string]: ValidScalars | JSONType;
} | JSONType[];


setConfig(config: ConfigUser, local?: boolean): void;


/**
      * Installing the key.
      * @param { string } key
      * @examle ```js
      * const db = require("mouse-db.json");
      * const table = new db("key");
      * table.setKey("key.me"); // key.me;
      * ```
      * @returns { string }
    */
setKey(key: string): string | never;


/**
      * Add key. Example: "key" => "key.me"
      * @param { string } key
      * @examle ```js
      * const db = require("mouse-db.json");
      * const table = new db("key");
      * table.addKey("me"); // key.me;for (let i = 0; i < key.length; i++) {
            // Получаем все индексы массивов в ключе.
            const keyIndices = printIndices(key[i], { functionName: "set", value, key });

            // Обрезаем индексы массивов.
            if (keyIndices.length) key[i] = key[i].slice(0, key[i].indexOf("[" + keyIndices[0] + "]"));

            // Если юзер введёт просто "[0]" вместо "key[0]"
            if (key[i].length === 0) throw new DBError("You didn't specify a key, just an array index!", { functionName: "set", value });
      * ```
      * @returns { string }
    */
addKey(key: string): string | never;


/**
      * Remove a certain number of keys (from the end); Example: "user.1552.profile" => "user.1552"
      * @param { number } num - How many keys to cut?
      * @examle ```js
      * const db = require("mouse-db.json");
      * const table = new db("user.1552.profile");
      * table.removeKey(2); // user
      * ```
      * @returns { string }
    */
removeKey(num?: number): string | never;


/**
      * Save changes to base
      * @param { any } value - The value must be supported in JSON!
      * @example ```js
      * const db = require("mouse-db.json");
      * const table = new db("user.1552.age");
      * table.set(24); // 24
    * table.setKey("user.1552.name");
    * table.set("Иван"); // Иван.
    * ```
    * @returns { any }
    */
set(key: string, value: JSONType): any;


set(value: JSONType): any;


/**
        * Get data from the base.
        * @returns { any }
    */
get(keyString?: string): JSONType | undefined;


/**
        * Check if an item is in the database by this key.
        * @param { object } settings
        * @param { boolean } settings.checkNull - With this setting enabled, this will also be checked for null. If the database is null, it will return false.
        * @returns { Boolean }
    */
has(): any;


has(key: string, checkNull: boolean): any;


has(key: string): any;


has(checkNull: boolean): any;


/**
        * Remove an element from the base.
        * @returns { boolean }
    */
delete(keyString?: string): boolean;


/**
        * Add number
        * @param { number } value
        * @returns { number }
    */
add(key: string, ...value: Array<number>): any;


add(...value: Array<number>): any;


/**
        * Minus the number
        * @param { number } value
        * @returns
    */
subtract(key: string, ...value: Array<number>): any;


subtract(...value: Array<number>): any;


/**
        * Add an item to the array.
        * @param { any } value
        * @returns { array<any> }
    */
push(key: string, ...value: Array<JSONType>): any;


push(...value: Array<JSONType>): any;


/**
        * Alilas db.get();
        * @returns { any }
    */
fetch(key?: string): JSONType | undefined;


/**
        * Alilas db.has();
        * @returns { boolean }
    */
exists(key: string, checkNull: boolean): any;


exists(key: string): any;


exists(checkNull: boolean): any;


export class HistogramChartProxy extends CartesianChartProxy<HistogramSeriesOptions> {

    public constructor(params: ChartProxyParams) {
        super(params);

        this.initChartOptions();
        this.recreateChart();
    }

    protected getDefaultOptionsFromTheme(theme: ChartTheme): CartesianChartOptions<HistogramSeriesOptions> {
        const options = super.getDefaultOptionsFromTheme(theme);

        const seriesDefaults = theme.getConfig<AgHistogramSeriesOptions>('histogram.series.histogram');
        options.seriesDefaults = {
            shadow: this.getDefaultDropShadowOptions(),
            label: seriesDefaults.label as BarSeriesLabelOptions,
            tooltip: {
                enabled: seriesDefaults.tooltipEnabled,
                renderer: seriesDefaults.tooltipRenderer
            },
            fill: {
                colors: theme.palette.fills,
                opacity: seriesDefaults.fillOpacity
            },
            stroke: {
                colors: theme.palette.strokes,
                opacity: seriesDefaults.strokeOpacity,
                width: seriesDefaults.strokeWidth
            },
            highlightStyle: seriesDefaults.highlightStyle as HighlightOptions
        } as HistogramSeriesOptions;

        return options;
    }

    protected createChart(options?: CartesianChartOptions<HistogramSeriesOptions>): CartesianChart {
        const { parentElement } = this.chartProxyParams;
        const seriesDefaults = this.getSeriesDefaults();

        options = options || this.chartOptions;
        const agChartOptions = options as AgCartesianChartOptions;
        agChartOptions.autoSize = true;
        agChartOptions.axes = [{
            type: 'number',
            position: 'bottom',
            ...options.xAxis
        }, {
            type: 'number',
            position: 'left',
            ...options.yAxis
        }];
        agChartOptions.series = [{
            ...seriesDefaults,
            fill: seriesDefaults.fill.colors[0],
            fillOpacity: seriesDefaults.fill.opacity,
            stroke: seriesDefaults.stroke.colors[0],
            strokeOpacity: seriesDefaults.stroke.opacity,
            strokeWidth: seriesDefaults.stroke.width,
            tooltipRenderer: seriesDefaults.tooltip && seriesDefaults.tooltip.enabled && seriesDefaults.tooltip.renderer,
            type: 'histogram'
        }];

        return AgChart.create(agChartOptions, parentElement);
    }

    public update(params: UpdateChartParams): void {
        const [xField] = params.fields;

        const chart = this.chart;
        const series = chart.series[0] as HistogramSeries;

        series.data = params.data;
        series.xKey = xField.colId;
        series.xName = xField.displayName;

        // for now, only constant width is supported via integrated charts
        series.areaPlot = false;

        const { fills, strokes } = this.getPalette();
        series.fill = fills[0];
        series.stroke = strokes[0];
    }

    protected getDefaultOptions(): CartesianChartOptions<HistogramSeriesOptions> {

        const fontOptions = this.getDefaultFontOptions();
        const options = this.getDefaultCartesianChartOptions() as CartesianChartOptions<HistogramSeriesOptions>;

        options.xAxis.label.rotation = 0;
        options.yAxis.label.rotation = 0;

        options.seriesDefaults = {
            ...options.seriesDefaults,
            tooltip: {
                enabled: true,
            },
            label: {
                ...fontOptions,
                enabled: false,
            },
            shadow: this.getDefaultDropShadowOptions(),
            binCount: 10
        };

        return options;
    }

    private getSeriesDefaults(): HistogramSeriesOptions {
        return {
            ...this.chartOptions.seriesDefaults
        };
    }
}


protected getDefaultOptionsFromTheme(theme: ChartTheme): CartesianChartOptions<HistogramSeriesOptions> {
        const options = super.getDefaultOptionsFromTheme(theme);

        const seriesDefaults = theme.getConfig<AgHistogramSeriesOptions>('histogram.series.histogram');
        options.seriesDefaults = {
            shadow: this.getDefaultDropShadowOptions(),
            label: seriesDefaults.label as BarSeriesLabelOptions,
            tooltip: {
                enabled: seriesDefaults.tooltipEnabled,
                renderer: seriesDefaults.tooltipRenderer
            },
            fill: {
                colors: theme.palette.fills,
                opacity: seriesDefaults.fillOpacity
            },
            stroke: {
                colors: theme.palette.strokes,
                opacity: seriesDefaults.strokeOpacity,
                width: seriesDefaults.strokeWidth
            },
            highlightStyle: seriesDefaults.highlightStyle as HighlightOptions
        } as HistogramSeriesOptions;

        return options;
    }


protected createChart(options?: CartesianChartOptions<HistogramSeriesOptions>): CartesianChart {
        const { parentElement } = this.chartProxyParams;
        const seriesDefaults = this.getSeriesDefaults();

        options = options || this.chartOptions;
        const agChartOptions = options as AgCartesianChartOptions;
        agChartOptions.autoSize = true;
        agChartOptions.axes = [{
            type: 'number',
            position: 'bottom',
            ...options.xAxis
        }, {
            type: 'number',
            position: 'left',
            ...options.yAxis
        }];
        agChartOptions.series = [{
            ...seriesDefaults,
            fill: seriesDefaults.fill.colors[0],
            fillOpacity: seriesDefaults.fill.opacity,
            stroke: seriesDefaults.stroke.colors[0],
            strokeOpacity: seriesDefaults.stroke.opacity,
            strokeWidth: seriesDefaults.stroke.width,
            tooltipRenderer: seriesDefaults.tooltip && seriesDefaults.tooltip.enabled && seriesDefaults.tooltip.renderer,
            type: 'histogram'
        }];

        return AgChart.create(agChartOptions, parentElement);
    }


public update(params: UpdateChartParams): void {
        const [xField] = params.fields;

        const chart = this.chart;
        const series = chart.series[0] as HistogramSeries;

        series.data = params.data;
        series.xKey = xField.colId;
        series.xName = xField.displayName;

        // for now, only constant width is supported via integrated charts
        series.areaPlot = false;

        const { fills, strokes } = this.getPalette();
        series.fill = fills[0];
        series.stroke = strokes[0];
    }


protected getDefaultOptions(): CartesianChartOptions<HistogramSeriesOptions> {

        const fontOptions = this.getDefaultFontOptions();
        const options = this.getDefaultCartesianChartOptions() as CartesianChartOptions<HistogramSeriesOptions>;

        options.xAxis.label.rotation = 0;
        options.yAxis.label.rotation = 0;

        options.seriesDefaults = {
            ...options.seriesDefaults,
            tooltip: {
                enabled: true,
            },
            label: {
                ...fontOptions,
                enabled: false,
            },
            shadow: this.getDefaultDropShadowOptions(),
            binCount: 10
        };

        return options;
    }


private getSeriesDefaults(): HistogramSeriesOptions {
        return {
            ...this.chartOptions.seriesDefaults
        };
    }


() => import('./UserPage')


() => {
  const [isPending, startTransition] = useTransition() as any; // FIXME
  const [userId, setUserId] = useState(initialId);
  const [page, setPage] = useState(initialPage);
  const onClick = () => {
    startTransition(() => {
      const nextId = userId ? String(Number(userId) + 1) : '1';
      setUserId(nextId);
      setPage(<UserPage getUser={fetchUserData(nextId)} />);
    });
  };
  return (

      <button type="button" onClick={onClick}>Next User</button>
      {isPending && Pending...}
      {page}


() => {
    startTransition(() => {
      const nextId = userId ? String(Number(userId) + 1) : '1';
      setUserId(nextId);
      setPage(<UserPage getUser={fetchUserData(nextId)} />);
    });
  }


() => {
      const nextId = userId ? String(Number(userId) + 1) : '1';
      setUserId(nextId);
      setPage(<UserPage getUser={fetchUserData(nextId)} />);
    }


fetchUserData(nextId)


resolve => setTimeout(resolve, ms)


export default class TMDB implements ITMDB {
    private static BASE_URL_V3 = 'https://api.themoviedb.org/3';
    private token: string;
    public constructor(token: string) {
        this.token = token;
    }

    public async getPersonChanges(): Promise<TMDBCollection<IPersonChange>> {
        const url = TMDB.BASE_URL_V3 + '/person/changes';
        try {
            const response = await fetch(url, this.requestInit);
            const result: TMDBCollection<IPersonChange> = await response.json();
            return Promise.resolve(result);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getMovieChanges(): Promise<TMDBCollection<IMovieChange>> {
        const url = TMDB.BASE_URL_V3 + '/movie/changes';
        try {
            const response = await fetch(url, this.requestInit);
            const result: TMDBCollection<IMovieChange> = await response.json();
            return Promise.resolve(result);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getMoviesWithProvider(id: number, page = 1):Promise<TMDBCollection<TMDBMovie>> {
        const url = TMDB.BASE_URL_V3 + '/discover/movie?include_adult=false&language=da&watch_region=DK&page=' + page + '&with_watch_providers=' + id;
        try {
            const response = await fetch(url, this.requestInit);
            const result: TMDBCollection<TMDBMovie> = await response.json();
            return Promise.resolve(result);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getProviders(): Promise<Array<IProvider>> {
        const url = TMDB.BASE_URL_V3 + '/watch/providers/movie?language=da&watch_region=DK';
        try {
            const response = await fetch(url, this.requestInit);
            const result: Record<string, Array<IProvider>> = await response.json();
            return Promise.resolve(result.results);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getMovieProviders(id: number): Promise<IProviders> {
        const url = TMDB.BASE_URL_V3 + '/movie/' + id + '/watch/providers?language=da&watch_region=DK';
        try {
            const response = await fetch(url, this.requestInit);
            const providers = await response.json() as IProviders;
            return Promise.resolve(providers);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getPersonMovieCredits(id: number): Promise<IPersonMovieCredits> {
        const url = TMDB.BASE_URL_V3 + '/person/' + id + '/movie_credits?language=da';
        try {
            const response = await fetch(url, this.requestInit);
            const personMovieCredits = await response.json() as IPersonMovieCredits;
            return Promise.resolve(personMovieCredits);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getPerson(id: number): Promise<IPerson> {
        const url = TMDB.BASE_URL_V3 + '/person/' + id + '?language=da';
        try {
            const response = await fetch(url, this.requestInit);
            const person = await response.json() as IPerson;
            return Promise.resolve(person);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getMovieCredits(id: number): Promise<ICredits> {
        const url = TMDB.BASE_URL_V3 + '/movie/' + id + '/credits?language=da';
        try {
            const response = await fetch(url, this.requestInit);
            const credits = await response.json() as ICredits;
            return Promise.resolve(credits);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getMovieImages(id: number): Promise<IImages> {
        const url = TMDB.BASE_URL_V3 + '/movie/' + id + '/images?language=da&include_image_language=da,en';
        try {
            const response = await fetch(url, this.requestInit);
            const images = await response.json() as IImages;
            return Promise.resolve(images);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    public async getMovie(id: number): Promise<IMovie> {
        try {
            const movieUrl = TMDB.BASE_URL_V3 + '/movie/' + id + '?language=da';
            const movieResponse = await fetch(movieUrl, this.requestInit);
            if (!movieResponse.ok) {
                return Promise.reject(new Error('Movie with id [' + id + '] was NOT found.'));
            }
            const movie: IMovie = await movieResponse.json() as IMovie;
            await this.delay(500);
            const { cast, crew } = await this.getMovieCredits(id);
            movie.cast = cast;
            movie.crew = crew;
            await this.delay(500);
            const { backdrops, posters } = await this.getMovieImages(id);
            movie.backdrops = backdrops;
            movie.posters = posters;
            return Promise.resolve(movie);
        } catch (error) {
            return Promise.reject(error);
        }
    }

    private get requestInit(): RequestInit {
        return {
            method: 'GET',
            headers: this.headersInit
        }
    }

    private get headersInit(): HeadersInit {
        return {
            Authorization: 'Bearer ' + this.token,
            'Content-Type': 'application/json;charset=utf-8'
        }
    }

    private async delay(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
}


public async getPersonChanges(): Promise<TMDBCollection<IPersonChange>> {
        const url = TMDB.BASE_URL_V3 + '/person/changes';
        try {
            const response = await fetch(url, this.requestInit);
            const result: TMDBCollection<IPersonChange> = await response.json();
            return Promise.resolve(result);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getMovieChanges(): Promise<TMDBCollection<IMovieChange>> {
        const url = TMDB.BASE_URL_V3 + '/movie/changes';
        try {
            const response = await fetch(url, this.requestInit);
            const result: TMDBCollection<IMovieChange> = await response.json();
            return Promise.resolve(result);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getMoviesWithProvider(id: number, page = 1):Promise<TMDBCollection<TMDBMovie>> {
        const url = TMDB.BASE_URL_V3 + '/discover/movie?include_adult=false&language=da&watch_region=DK&page=' + page + '&with_watch_providers=' + id;
        try {
            const response = await fetch(url, this.requestInit);
            const result: TMDBCollection<TMDBMovie> = await response.json();
            return Promise.resolve(result);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getProviders(): Promise<Array<IProvider>> {
        const url = TMDB.BASE_URL_V3 + '/watch/providers/movie?language=da&watch_region=DK';
        try {
            const response = await fetch(url, this.requestInit);
            const result: Record<string, Array<IProvider>> = await response.json();
            return Promise.resolve(result.results);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getMovieProviders(id: number): Promise<IProviders> {
        const url = TMDB.BASE_URL_V3 + '/movie/' + id + '/watch/providers?language=da&watch_region=DK';
        try {
            const response = await fetch(url, this.requestInit);
            const providers = await response.json() as IProviders;
            return Promise.resolve(providers);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getPersonMovieCredits(id: number): Promise<IPersonMovieCredits> {
        const url = TMDB.BASE_URL_V3 + '/person/' + id + '/movie_credits?language=da';
        try {
            const response = await fetch(url, this.requestInit);
            const personMovieCredits = await response.json() as IPersonMovieCredits;
            return Promise.resolve(personMovieCredits);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getPerson(id: number): Promise<IPerson> {
        const url = TMDB.BASE_URL_V3 + '/person/' + id + '?language=da';
        try {
            const response = await fetch(url, this.requestInit);
            const person = await response.json() as IPerson;
            return Promise.resolve(person);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getMovieCredits(id: number): Promise<ICredits> {
        const url = TMDB.BASE_URL_V3 + '/movie/' + id + '/credits?language=da';
        try {
            const response = await fetch(url, this.requestInit);
            const credits = await response.json() as ICredits;
            return Promise.resolve(credits);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getMovieImages(id: number): Promise<IImages> {
        const url = TMDB.BASE_URL_V3 + '/movie/' + id + '/images?language=da&include_image_language=da,en';
        try {
            const response = await fetch(url, this.requestInit);
            const images = await response.json() as IImages;
            return Promise.resolve(images);
        } catch (error) {
            return Promise.reject(error);
        }
    }


public async getMovie(id: number): Promise<IMovie> {
        try {
            const movieUrl = TMDB.BASE_URL_V3 + '/movie/' + id + '?language=da';
            const movieResponse = await fetch(movieUrl, this.requestInit);
            if (!movieResponse.ok) {
                return Promise.reject(new Error('Movie with id [' + id + '] was NOT found.'));
            }
            const movie: IMovie = await movieResponse.json() as IMovie;
            await this.delay(500);
            const { cast, crew } = await this.getMovieCredits(id);
            movie.cast = cast;
            movie.crew = crew;
            await this.delay(500);
            const { backdrops, posters } = await this.getMovieImages(id);
            movie.backdrops = backdrops;
            movie.posters = posters;
            return Promise.resolve(movie);
        } catch (error) {
            return Promise.reject(error);
        }
    }


private async delay(ms: number): Promise<void> {
        return new Promise(resolve => setTimeout(resolve, ms));
    }


/**
 * A MySQL Server key.
 */
export class ServerKey extends pulumi.CustomResource {
    /**
     * Get an existing ServerKey resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param opts Optional settings to control the behavior of the CustomResource.
     */
    public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): ServerKey {
        return new ServerKey(name, undefined as any, { ...opts, id: id });
    }

    /** @internal */
    public static readonly __pulumiType = 'azure-nextgen:dbformysql/v20200101privatepreview:ServerKey';

    /**
     * Returns true if the given object is an instance of ServerKey.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
    public static isInstance(obj: any): obj is ServerKey {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === ServerKey.__pulumiType;
    }

    /**
     * The key creation date.
     */
    public /*out*/ readonly creationDate!: pulumi.Output<string>;
    /**
     * Kind of encryption protector. This is metadata used for the Azure portal experience.
     */
    public /*out*/ readonly kind!: pulumi.Output<string>;
    /**
     * The name of the resource
     */
    public /*out*/ readonly name!: pulumi.Output<string>;
    /**
     * The key type like 'AzureKeyVault'.
     */
    public readonly serverKeyType!: pulumi.Output<string>;
    /**
     * The type of the resource. E.g. "Microsoft.Compute/virtualMachines" or "Microsoft.Storage/storageAccounts"
     */
    public /*out*/ readonly type!: pulumi.Output<string>;
    /**
     * The URI of the key.
     */
    public readonly uri!: pulumi.Output<string | undefined>;

    /**
     * Create a ServerKey resource with the given unique name, arguments, and options.
     *
     * @param name The _unique_ name of the resource.
     * @param args The arguments to use to populate this resource's properties.
     * @param opts A bag of options that control this resource's behavior.
     */
    constructor(name: string, args: ServerKeyArgs, opts?: pulumi.CustomResourceOptions) {
        let inputs: pulumi.Inputs = {};
        if (!(opts && opts.id)) {
            if (!args || args.keyName === undefined) {
                throw new Error("Missing required property 'keyName'");
            }
            if (!args || args.resourceGroupName === undefined) {
                throw new Error("Missing required property 'resourceGroupName'");
            }
            if (!args || args.serverKeyType === undefined) {
                throw new Error("Missing required property 'serverKeyType'");
            }
            if (!args || args.serverName === undefined) {
                throw new Error("Missing required property 'serverName'");
            }
            inputs["keyName"] = args ? args.keyName : undefined;
            inputs["resourceGroupName"] = args ? args.resourceGroupName : undefined;
            inputs["serverKeyType"] = args ? args.serverKeyType : undefined;
            inputs["serverName"] = args ? args.serverName : undefined;
            inputs["uri"] = args ? args.uri : undefined;
            inputs["creationDate"] = undefined /*out*/;
            inputs["kind"] = undefined /*out*/;
            inputs["name"] = undefined /*out*/;
            inputs["type"] = undefined /*out*/;
        } else {
            inputs["creationDate"] = undefined /*out*/;
            inputs["kind"] = undefined /*out*/;
            inputs["name"] = undefined /*out*/;
            inputs["serverKeyType"] = undefined /*out*/;
            inputs["type"] = undefined /*out*/;
            inputs["uri"] = undefined /*out*/;
        }
        if (!opts) {
            opts = {}
        }

        if (!opts.version) {
            opts.version = utilities.getVersion();
        }
        const aliasOpts = { aliases: [{ type: "azure-nextgen:dbformysql/latest:ServerKey" }, { type: "azure-nextgen:dbformysql/v20200101:ServerKey" }] };
        opts = opts ? pulumi.mergeOptions(opts, aliasOpts) : aliasOpts;
        super(ServerKey.__pulumiType, name, inputs, opts);
    }
}


/**
 * The set of arguments for constructing a ServerKey resource.
 */
export interface ServerKeyArgs {
    /**
     * The name of the MySQL Server key to be operated on (updated or created).
     */
    readonly keyName: pulumi.Input<string>;
    /**
     * The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal.
     */
    readonly resourceGroupName: pulumi.Input<string>;
    /**
     * The key type like 'AzureKeyVault'.
     */
    readonly serverKeyType: pulumi.Input<string>;
    /**
     * The name of the server.
     */
    readonly serverName: pulumi.Input<string>;
    /**
     * The URI of the key.
     */
    readonly uri?: pulumi.Input<string>;
}


/**
     * Get an existing ServerKey resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param opts Optional settings to control the behavior of the CustomResource.
     */
public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): ServerKey {
        return new ServerKey(name, undefined as any, { ...opts, id: id });
    }


/**
     * Returns true if the given object is an instance of ServerKey.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
public static isInstance(obj: any): obj is ServerKey {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === ServerKey.__pulumiType;
    }


async (saveOpts: BeforeSaveMiddlewareParams) => saveOpts


async (runOpts: RunContext) => runOpts


(...args: any[]) => server.log(...args)


(instance: ConcreteTaskInstance) =>
      new TaskManagerRunner({
        logger,
        kbnServer: opts.kbnServer,
        instance,
        store,
        definitions: this.definitions,
        beforeRun: this.middleware.beforeRun,
      })


() => {
      // By this point, the plugins had their chance to register task definitions
      // and we're good to start doing CRUD actions
      this.isInitialized = true;
      const startPoller = async () => {
        await server.kibanaMigrator.awaitMigration();
        try {
          await poller.start();
        } catch (err) {
          // FIXME: check the type of error to make sure it's actually an ES error
          logger.warning(`PollError ${err.message}`);

          // rety again to initialize store and poller, using the timing of
          // task_manager's configurable poll interval
          const retryInterval = opts.config.get('xpack.task_manager.poll_interval');
          setTimeout(() => startPoller(), retryInterval);
        }
      };
      startPoller();
    }


async () => {
        await server.kibanaMigrator.awaitMigration();
        try {
          await poller.start();
        } catch (err) {
          // FIXME: check the type of error to make sure it's actually an ES error
          logger.warning(`PollError ${err.message}`);

          // rety again to initialize store and poller, using the timing of
          // task_manager's configurable poll interval
          const retryInterval = opts.config.get('xpack.task_manager.poll_interval');
          setTimeout(() => startPoller(), retryInterval);
        }
      }


() => startPoller()


k => !!this.definitions[k]


/*
 * The TaskManager is the public interface into the task manager system. This glues together
 * all of the disparate modules in one integration point. The task manager operates in two different ways:
 *
 * - pre-init, it allows middleware registration, but disallows task manipulation
 * - post-init, it disallows middleware registration, but allows task manipulation
 *
 * Due to its complexity, this is mostly tested by integration tests (see readme).
 */
/**
 * The public interface into the task manager system.
 */
export class TaskManager {
  private isInitialized = false;
  private maxWorkers: number;
  private overrideNumWorkers: { [taskType: string]: number };
  private definitions: TaskDictionary<SanitizedTaskDefinition>;
  private store: TaskStore;
  private poller: TaskPoller;
  private logger: Logger;
  private middleware = {
    beforeSave: async (saveOpts: BeforeSaveMiddlewareParams) => saveOpts,
    beforeRun: async (runOpts: RunContext) => runOpts,
  };

  /**
   * Initializes the task manager, preventing any further addition of middleware,
   * enabling the task manipulation methods, and beginning the background polling
   * mechanism.
   */
  constructor(opts: TaskManagerOpts) {
    const { server } = opts.kbnServer;
    this.maxWorkers = opts.config.get('xpack.task_manager.max_workers');
    this.overrideNumWorkers = opts.config.get('xpack.task_manager.override_num_workers');
    this.definitions = {};

    const logger = new TaskManagerLogger((...args: any[]) => server.log(...args));

    /* Kibana UUID needs to be pulled live (not cached), as it takes a long time
     * to initialize, and can change after startup */
    const store = new TaskStore({
      serializer: opts.serializer,
      savedObjectsRepository: opts.savedObjectsRepository,
      callCluster: server.plugins.elasticsearch.getCluster('admin').callWithInternalUser,
      maxAttempts: opts.config.get('xpack.task_manager.max_attempts'),
      definitions: this.definitions,
    });
    const pool = new TaskPool({
      logger,
      maxWorkers: this.maxWorkers,
    });
    const createRunner = (instance: ConcreteTaskInstance) =>
      new TaskManagerRunner({
        logger,
        kbnServer: opts.kbnServer,
        instance,
        store,
        definitions: this.definitions,
        beforeRun: this.middleware.beforeRun,
      });
    const poller = new TaskPoller({
      logger,
      pollInterval: opts.config.get('xpack.task_manager.poll_interval'),
      work(): Promise<void> {
        return fillPool(pool.run, store.fetchAvailableTasks, createRunner);
      },
    });

    this.logger = logger;
    this.store = store;
    this.poller = poller;

    opts.kbnServer.afterPluginsInit(() => {
      // By this point, the plugins had their chance to register task definitions
      // and we're good to start doing CRUD actions
      this.isInitialized = true;
      const startPoller = async () => {
        await server.kibanaMigrator.awaitMigration();
        try {
          await poller.start();
        } catch (err) {
          // FIXME: check the type of error to make sure it's actually an ES error
          logger.warning(`PollError ${err.message}`);

          // rety again to initialize store and poller, using the timing of
          // task_manager's configurable poll interval
          const retryInterval = opts.config.get('xpack.task_manager.poll_interval');
          setTimeout(() => startPoller(), retryInterval);
        }
      };
      startPoller();
    });
  }

  /**
   * Method for allowing consumers to register task definitions into the system.
   * @param taskDefinitions - The Kibana task definitions dictionary
   */
  public registerTaskDefinitions(taskDefinitions: TaskDictionary<TaskDefinition>) {
    this.assertUninitialized('register task definitions');
    const duplicate = Object.keys(taskDefinitions).find(k => !!this.definitions[k]);
    if (duplicate) {
      throw new Error(`Task ${duplicate} is already defined!`);
    }

    try {
      const sanitized = sanitizeTaskDefinitions(
        taskDefinitions,
        this.maxWorkers,
        this.overrideNumWorkers
      );

      Object.assign(this.definitions, sanitized);
    } catch (e) {
      this.logger.error('Could not sanitize task definitions');
    }
  }

  /**
   * Adds middleware to the task manager, such as adding security layers, loggers, etc.
   *
   * @param {Middleware} middleware - The middlware being added.
   */
  public addMiddleware(middleware: Middleware) {
    this.assertUninitialized('add middleware');
    const prevMiddleWare = this.middleware;
    this.middleware = addMiddlewareToChain(prevMiddleWare, middleware);
  }

  /**
   * Schedules a task.
   *
   * @param task - The task being scheduled.
   * @returns {Promise<ConcreteTaskInstance>}
   */
  public async schedule(taskInstance: TaskInstance, options?: any): Promise<ConcreteTaskInstance> {
    this.assertInitialized('Tasks cannot be scheduled until after task manager is initialized!');
    const { taskInstance: modifiedTask } = await this.middleware.beforeSave({
      ...options,
      taskInstance,
    });
    const result = await this.store.schedule(modifiedTask);
    this.poller.attemptWork();
    return result;
  }

  /**
   * Fetches a paginatable list of scheduled tasks.
   *
   * @param opts - The query options used to filter tasks
   * @returns {Promise<FetchResult>}
   */
  public async fetch(opts: FetchOpts): Promise<FetchResult> {
    this.assertInitialized('Tasks cannot be fetched before task manager is initialized!');
    return this.store.fetch(opts);
  }

  /**
   * Removes the specified task from the index.
   *
   * @param {string} id
   * @returns {Promise<RemoveResult>}
   */
  public async remove(id: string): Promise<void> {
    this.assertInitialized('Tasks cannot be removed before task manager is initialized!');
    return this.store.remove(id);
  }

  /**
   * Ensures task manager IS NOT already initialized
   *
   * @param {string} message shown if task manager is already initialized
   * @returns void
   */
  private assertUninitialized(message: string) {
    if (this.isInitialized) {
      throw new Error(`Cannot ${message} after the task manager is initialized!`);
    }
  }

  /**
   * Ensures task manager IS already initialized
   *
   * @param {string} message shown if task manager is not initialized
   * @returns void
   */
  private assertInitialized(message: string) {
    if (!this.isInitialized) {
      throw new Error(`NotInitialized: ${message}`);
    }
  }
}


export interface TaskManagerOpts {
  kbnServer: any;
  config: any;
  savedObjectsRepository: SavedObjectsClientContract;
  serializer: SavedObjectsSerializer;
}


work(): Promise<void> {
        return fillPool(pool.run, store.fetchAvailableTasks, createRunner);
      }


/**
   * Method for allowing consumers to register task definitions into the system.
   * @param taskDefinitions - The Kibana task definitions dictionary
   */
public registerTaskDefinitions(taskDefinitions: TaskDictionary<TaskDefinition>) {
    this.assertUninitialized('register task definitions');
    const duplicate = Object.keys(taskDefinitions).find(k => !!this.definitions[k]);
    if (duplicate) {
      throw new Error(`Task ${duplicate} is already defined!`);
    }

    try {
      const sanitized = sanitizeTaskDefinitions(
        taskDefinitions,
        this.maxWorkers,
        this.overrideNumWorkers
      );

      Object.assign(this.definitions, sanitized);
    } catch (e) {
      this.logger.error('Could not sanitize task definitions');
    }
  }


/**
   * Adds middleware to the task manager, such as adding security layers, loggers, etc.
   *
   * @param {Middleware} middleware - The middlware being added.
   */
public addMiddleware(middleware: Middleware) {
    this.assertUninitialized('add middleware');
    const prevMiddleWare = this.middleware;
    this.middleware = addMiddlewareToChain(prevMiddleWare, middleware);
  }


/**
   * Schedules a task.
   *
   * @param task - The task being scheduled.
   * @returns {Promise<ConcreteTaskInstance>}
   */
public async schedule(taskInstance: TaskInstance, options?: any): Promise<ConcreteTaskInstance> {
    this.assertInitialized('Tasks cannot be scheduled until after task manager is initialized!');
    const { taskInstance: modifiedTask } = await this.middleware.beforeSave({
      ...options,
      taskInstance,
    });
    const result = await this.store.schedule(modifiedTask);
    this.poller.attemptWork();
    return result;
  }


/**
   * Fetches a paginatable list of scheduled tasks.
   *
   * @param opts - The query options used to filter tasks
   * @returns {Promise<FetchResult>}
   */
public async fetch(opts: FetchOpts): Promise<FetchResult> {
    this.assertInitialized('Tasks cannot be fetched before task manager is initialized!');
    return this.store.fetch(opts);
  }


/**
   * Removes the specified task from the index.
   *
   * @param {string} id
   * @returns {Promise<RemoveResult>}
   */
public async remove(id: string): Promise<void> {
    this.assertInitialized('Tasks cannot be removed before task manager is initialized!');
    return this.store.remove(id);
  }


/**
   * Ensures task manager IS NOT already initialized
   *
   * @param {string} message shown if task manager is already initialized
   * @returns void
   */
private assertUninitialized(message: string) {
    if (this.isInitialized) {
      throw new Error(`Cannot ${message} after the task manager is initialized!`);
    }
  }


/**
   * Ensures task manager IS already initialized
   *
   * @param {string} message shown if task manager is not initialized
   * @returns void
   */
private assertInitialized(message: string) {
    if (!this.isInitialized) {
      throw new Error(`NotInitialized: ${message}`);
    }
  }


@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
  constructor(private authService: AuthService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {

    const url: string = state.url;
    return this.checkLogin(url);
  }

  canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return this.canActivate(route, state);
  }

  canLoad(route: Route): boolean {

    const url = `/${route.path}`;
    return this.checkLogin(url);
  }

  checkLogin(url: string): boolean {

    if (this.authService.isLoggedIn) {
      return true;
    }

    this.authService.loginRedirectUrl = url;
    this.router.navigate(['/login']);

    return false;
  }
}


canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {

    const url: string = state.url;
    return this.checkLogin(url);
  }


canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return this.canActivate(route, state);
  }


canLoad(route: Route): boolean {

    const url = `/${route.path}`;
    return this.checkLogin(url);
  }


checkLogin(url: string): boolean {

    if (this.authService.isLoggedIn) {
      return true;
    }

    this.authService.loginRedirectUrl = url;
    this.router.navigate(['/login']);

    return false;
  }


(req, resp) => {
      resp.writeHead(200, { "Content-Type": "text/plain" });
      resp.end();
    }


(req, clientSocket, head) => {
      const serverUrl = new URL(`http://${req.url}`);
      const serverSocket = net.connect(
        parseInt(serverUrl.port, 10),
        serverUrl.hostname,
        () => {
          clientSocket.write("HTTP/1.1 200 Connection Established\r\n" + "Proxy-agent: Node.js-Proxy\r\n" + "\r\n");
          serverSocket.write(head);
          serverSocket.pipe(clientSocket);
          clientSocket.pipe(serverSocket);
        }
      );
    }


() => {
          clientSocket.write("HTTP/1.1 200 Connection Established\r\n" + "Proxy-agent: Node.js-Proxy\r\n" + "\r\n");
          serverSocket.write(head);
          serverSocket.pipe(clientSocket);
          clientSocket.pipe(serverSocket);
        }


(resolve, reject) => {
        proxy.listen(proxyPort, "127.0.0.1", async () => {
          try {
            const client = new CosmosClient({
              endpoint,
              key: masterKey,
              agent
            });
            // create database
            await client.databases.create({
              id: addEntropy("ProxyTest")
            });
            resolve();
          } catch (err) {
            throw err;
          } finally {
            proxy.close();
          }
        });
      }


async () => {
          try {
            const client = new CosmosClient({
              endpoint,
              key: masterKey,
              agent
            });
            // create database
            await client.databases.create({
              id: addEntropy("ProxyTest")
            });
            resolve();
          } catch (err) {
            throw err;
          } finally {
            proxy.close();
          }
        }


(resolve, reject) => {
        proxy.listen(proxyPort + 1, "127.0.0.1", async () => {
          try {
            const client = new CosmosClient({
              endpoint,
              key: masterKey,
              agent
            });
            // create database
            await client.databases.create({
              id: addEntropy("ProxyTest")
            });
            reject(new Error("Should create database in error while the proxy setting is not correct"));
          } catch (err) {
            resolve();
          } finally {
            proxy.close();
          }
        });
      }


async () => {
          try {
            const client = new CosmosClient({
              endpoint,
              key: masterKey,
              agent
            });
            // create database
            await client.databases.create({
              id: addEntropy("ProxyTest")
            });
            reject(new Error("Should create database in error while the proxy setting is not correct"));
          } catch (err) {
            resolve();
          } finally {
            proxy.close();
          }
        }


() => {
  const OLD_ENV = process.env;

  afterAll(() => {
    process.env = OLD_ENV;
  });

  test("footerText prop is set correctly", async () => {
    process.env = {
      ...OLD_ENV,
      FOOTER_TEXT: "Footer Text Test",
    };
    jest.resetModules();

    const { default: config } = await import("../config");
    expect(config.footerText).toEqual("Footer Text Test");
  });

  test("lastCommit and lastTag props are set correctly", async () => {
    process.env = {
      ...OLD_ENV,
      LAST_COMMIT: "LAST COMMIT",
      LAST_TAG: "LAST TAG",
    };

    jest.resetModules();

    const { default: config } = await import("../config");
    expect(config.lastCommit).toEqual("LAST COMMIT");
    expect(config.lastTag).toEqual("LAST TAG");
  });
}


() => {
    process.env = OLD_ENV;
  }


async () => {
    process.env = {
      ...OLD_ENV,
      FOOTER_TEXT: "Footer Text Test",
    };
    jest.resetModules();

    const { default: config } = await import("../config");
    expect(config.footerText).toEqual("Footer Text Test");
  }


async () => {
    process.env = {
      ...OLD_ENV,
      LAST_COMMIT: "LAST COMMIT",
      LAST_TAG: "LAST TAG",
    };

    jest.resetModules();

    const { default: config } = await import("../config");
    expect(config.lastCommit).toEqual("LAST COMMIT");
    expect(config.lastTag).toEqual("LAST TAG");
  }


export function ThemeContextProvider(props: ThemeContextProviderProps) {
  const [currentTheme, setCurrentTheme] = useState<Theme>(() => {
    const storagedTheme = localStorage.getItem('theme') as Theme

    return (storagedTheme ?? 'light') as Theme

  })

  useEffect(() => {
    localStorage.setItem('theme', currentTheme)
  }, [currentTheme])
  function toggleTheme() {
    setCurrentTheme(currentTheme === 'light' ? 'dark' : 'light')
  }

  return(
    <ThemeContext.Provider value={{theme: currentTheme, toggleTheme}}


function toggleTheme() {
    setCurrentTheme(currentTheme === 'light' ? 'dark' : 'light')
  }


() => {
    const storagedTheme = localStorage.getItem('theme') as Theme

    return (storagedTheme ?? 'light') as Theme

  }


() => {
    localStorage.setItem('theme', currentTheme)
  }


type Theme = 'light' | 'dark'


type ThemeContextProviderProps = {
  children: ReactNode
}


type ThemeContextType = {
  theme: Theme,
  toggleTheme: () => void
}


export function compareGlossaryEntries(
  [, [{ dictionaryWord: firstWord }]]: GlossaryEntry,
  [, [{ dictionaryWord: secondWord }]]: GlossaryEntry
): number {
  if (firstWord && secondWord) {
    return compareWord(firstWord, secondWord)
  } else {
    throw new Error(
      'Either of the glossary entries is missing the dictionary word.'
    )
  }
}


export interface GlossaryToken {
  readonly label: Label
  readonly value: string
  readonly word: AnyWord
  readonly uniqueLemma: string
  readonly dictionaryWord: DictionaryWord | null
}


export type GlossaryEntry = readonly [string, readonly GlossaryToken[]]


export type GlossaryData = readonly GlossaryEntry[]


@Component({
  selector: 'nz-demo-slider-vertical',
  template: `

        <nz-slider nzVertical [ngModel]="30"></nz-slider>

        <nz-slider nzVertical nzRange [nzStep]="10" [ngModel]="[20, 50]"></nz-slider>

        <nz-slider nzVertical nzRange [nzMarks]="marks" [ngModel]="[26, 37]"></nz-slider>

  `
})
export class NzDemoSliderVerticalComponent {

  style = {
    float     : 'left',
    height    : '300px',
    marginLeft: '70px'
  };

  marks = {
    0  : '0°C',
    26 : '26°C',
    37 : '37°C',
    100: {
      style: {
        color: '#f50',
      },
      label: '100°C',
    }
  };

}


// Configurer la directive
@Component({
    selector: 'add-task',
    templateUrl: 'app/partials/directives/add.task.dir.html',
})

// Exporter la class de la directive
export class AddTask{

    private newTask: TaskItem;

    constructor(
        private taskService: TaskService
    ){
        this.resetInput(); //on initialise la variable
    };

    addNewTask(event: Event){
        if(this.newTask.content.length>0){
            this.taskService.addNewTask(this.newTask);
            this.resetInput();
        }
    }

    resetInput():void{
        this.newTask = new TaskItem({content:""});
    }
}


addNewTask(event: Event){
        if(this.newTask.content.length>0){
            this.taskService.addNewTask(this.newTask);
            this.resetInput();
        }
    }


resetInput():void{
        this.newTask = new TaskItem({content:""});
    }


() => {
  let filter: filterUtils.FilterWith<'onFetch'>
  let notificationSchemeType: ObjectType
  let instance: InstanceElement

  beforeEach(async () => {
    const { client, paginator } = mockClient()

    filter = notificationSchemeStructureFilter({
      client,
      paginator,
      config: DEFAULT_CONFIG,
      elementsSource: buildElementsSourceFromElements([]),
      fetchQuery: elementUtils.query.createMockQuery(),
    }) as filterUtils.FilterWith<'onFetch'>

    notificationSchemeType = new ObjectType({
      elemID: new ElemID(JIRA, NOTIFICATION_SCHEME_TYPE_NAME),
    })

    instance = new InstanceElement(
      'instance',
      notificationSchemeType,
      {
        notificationSchemeEvents: [
          {
            event: {
              id: '1',
            },
            notifications: [{
              id: '2',
              notificationType: 'type',
              parameter: 'parameter',
              user: 'user',
              additionalProperties: 'additionalProperties',
            }],
          },
        ],
      },
    )
  })

  describe('onFetch', () => {
    it('should add notificationIds field', async () => {
      await filter.onFetch([notificationSchemeType])
      expect(notificationSchemeType.fields.notificationIds.annotations).toEqual({
        [CORE_ANNOTATIONS.HIDDEN_VALUE]: true,
      })
    })

    it('should transform notifications', async () => {
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toEqual([
        {
          eventType: '1',
          notifications: [{
            type: 'type',
            parameter: 'parameter',
          }],
        },
      ])
    })

    it('should do nothing when there are no notifications', async () => {
      delete instance.value.notificationSchemeEvents[0].notifications
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toEqual([
        {
          eventType: '1',
        },
      ])
    })

    it('should do nothing when there are no notificationSchemeEvents', async () => {
      delete instance.value.notificationSchemeEvents
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toBeUndefined()
    })

    it('should add notificationIds', async () => {
      await filter.onFetch([instance])
      expect(instance.value.notificationIds).toEqual({
        '1-type-parameter': '2',
      })
    })
  })
}


async () => {
    const { client, paginator } = mockClient()

    filter = notificationSchemeStructureFilter({
      client,
      paginator,
      config: DEFAULT_CONFIG,
      elementsSource: buildElementsSourceFromElements([]),
      fetchQuery: elementUtils.query.createMockQuery(),
    }) as filterUtils.FilterWith<'onFetch'>

    notificationSchemeType = new ObjectType({
      elemID: new ElemID(JIRA, NOTIFICATION_SCHEME_TYPE_NAME),
    })

    instance = new InstanceElement(
      'instance',
      notificationSchemeType,
      {
        notificationSchemeEvents: [
          {
            event: {
              id: '1',
            },
            notifications: [{
              id: '2',
              notificationType: 'type',
              parameter: 'parameter',
              user: 'user',
              additionalProperties: 'additionalProperties',
            }],
          },
        ],
      },
    )
  }


() => {
    it('should add notificationIds field', async () => {
      await filter.onFetch([notificationSchemeType])
      expect(notificationSchemeType.fields.notificationIds.annotations).toEqual({
        [CORE_ANNOTATIONS.HIDDEN_VALUE]: true,
      })
    })

    it('should transform notifications', async () => {
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toEqual([
        {
          eventType: '1',
          notifications: [{
            type: 'type',
            parameter: 'parameter',
          }],
        },
      ])
    })

    it('should do nothing when there are no notifications', async () => {
      delete instance.value.notificationSchemeEvents[0].notifications
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toEqual([
        {
          eventType: '1',
        },
      ])
    })

    it('should do nothing when there are no notificationSchemeEvents', async () => {
      delete instance.value.notificationSchemeEvents
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toBeUndefined()
    })

    it('should add notificationIds', async () => {
      await filter.onFetch([instance])
      expect(instance.value.notificationIds).toEqual({
        '1-type-parameter': '2',
      })
    })
  }


async () => {
      await filter.onFetch([notificationSchemeType])
      expect(notificationSchemeType.fields.notificationIds.annotations).toEqual({
        [CORE_ANNOTATIONS.HIDDEN_VALUE]: true,
      })
    }


async () => {
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toEqual([
        {
          eventType: '1',
          notifications: [{
            type: 'type',
            parameter: 'parameter',
          }],
        },
      ])
    }


async () => {
      delete instance.value.notificationSchemeEvents[0].notifications
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toEqual([
        {
          eventType: '1',
        },
      ])
    }


async () => {
      delete instance.value.notificationSchemeEvents
      await filter.onFetch([instance])
      expect(instance.value.notificationSchemeEvents).toBeUndefined()
    }


async () => {
      await filter.onFetch([instance])
      expect(instance.value.notificationIds).toEqual({
        '1-type-parameter': '2',
      })
    }


error =>
    console.error(`[VideoPlayer] ${error.type} Error - ${error.message}: ${error.obj}`)


() =>
      // eslint-disable-next-line no-console
      console.log('[VideoPlayer] - missing `enterFullscreen` function in `fullscreen` prop')


() =>
      // eslint-disable-next-line no-console
      console.log('[VideoPlayer] - missing `exitFullscreen` function in `fullscreen` prop')


// https://github.com/typescript-cheatsheets/react/issues/415
export type Props = RequiredProps & DefaultProps


type RequiredProps = {
  videoProps: VideoProps & {
    ref?: MutableRefObject<Video>
  }
}


type DefaultProps = {
  errorCallback: (error: ErrorType) => void
  playbackCallback: (status: AVPlaybackStatus) => void
  defaultControlsVisible: boolean
  timeVisible: boolean
  textStyle: TextStyle
  slider: {
    visible?: boolean
  } & SliderProps
  activityIndicator: ActivityIndicatorProps
  animation: {
    fadeInDuration?: number
    fadeOutDuration?: number
  }
  renderHeaderComponent: React.ReactNode
  style: {
    width?: number
    height?: number
    videoBackgroundColor?: ColorValue
    controlsBackgroundColor?: ColorValue
  }
  icon: {
    size?: number
    color?: ColorValue
    style?: TextStyle
    pause?: JSX.Element
    play?: JSX.Element
    replay?: JSX.Element
    loading?: JSX.Element
    fullscreen?: JSX.Element
    exitFullscreen?: JSX.Element
  }
  fullscreen: {
    enterFullscreen?: () => void
    exitFullscreen?: () => void
    inFullscreen?: boolean
    visible?: boolean
  },
  autoHidePlayer: boolean
}


@Injectable()
export class PusherService {
    public async trigger(
        channelName: string,
        eventName: string,
        jsonData: { [key: string]: any },
    ) {
        pusher.trigger(channelName, eventName, jsonData);
    }
}


public async trigger(
        channelName: string,
        eventName: string,
        jsonData: { [key: string]: any },
    ) {
        pusher.trigger(channelName, eventName, jsonData);
    }


attachment => (
			attachment.type === type
		)


// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default class NewAttachmentsContext<S = Record<string, any>>
	extends Context<INewAttachmentsContextPayload, S> {
	public attachments: Attachment[];

	public constructor(options: NewAttachmentsContextOptions<S>) {
		const [subType, PayloadAttachment] = subTypes[options.updateType];

		super({
			...options,

			type: 'new_attachment',
			subTypes: [
				subType
			]
		});

		this.attachments = [new PayloadAttachment(this.payload, this.vk)];
	}

	/**
	 * Checks is attachment photo
	 */
	public get isPhoto(): boolean {
		return this.subTypes.includes('new_photo_attachment');
	}

	/**
	 * Checks is attachment video
	 */
	public get isVideo(): boolean {
		return this.subTypes.includes('new_video_attachment');
	}

	/**
	 * Checks is attachment audio
	 */
	public get isAudio(): boolean {
		return this.subTypes.includes('new_audio_attachment');
	}

	/**
	 * Checks for the presence of attachments
	 */
	public hasAttachments(type: string = null): boolean {
		if (type === null) {
			return this.attachments.length > 0;
		}

		return this.attachments.some(attachment => (
			attachment.type === type
		));
	}

	/**
	 * Returns the attachments
	 */
	public getAttachments(type: string = null): Attachment[] {
		if (type === null) {
			return this.attachments;
		}

		return this.attachments.filter(attachment => (
			attachment.type === type
		));
	}

	/**
	 * Removes the attachment
	 */
	public deleteAttachment(): Promise<number> {
		if (this.isPhoto) {
			const [photo] = this.getAttachments('photo');

			// @ts-ignore
			return this.vk.api.photos.delete({
				owner_id: photo.ownerId,
				photo_id: photo.id
			});
		}

		if (this.isVideo) {
			const [video] = this.getAttachments('video');

			// @ts-ignore
			return this.vk.api.video.delete({
				owner_id: video.ownerId,
				video_id: video.id
			});
		}

		if (this.isAudio) {
			const [audio] = this.getAttachments('audio');

			// @ts-ignore
			return this.vk.api.audio.delete({
				owner_id: audio.ownerId,
				audio_id: audio.id
			});
		}

		return Promise.reject(new VKError({
			message: 'Unsupported event for deleting attachment',
			code: 'UNSUPPORTED_EVENT'
		}));
	}

	/**
	 * Returns the custom data
	 */
	public [inspectCustomData](): object {
		return copyParams(this, [
			'attachments',
			'isPhoto',
			'isVideo',
			'isAudio'
		]);
	}
}


export interface INewAttachmentsContextPayload {
	id: number;
}


export type NewAttachmentsContextOptions<S> =
	Omit<IContextOptions<INewAttachmentsContextPayload, S>, 'type' | 'subTypes'>;


/**
	 * Checks for the presence of attachments
	 */
public hasAttachments(type: string = null): boolean {
		if (type === null) {
			return this.attachments.length > 0;
		}

		return this.attachments.some(attachment => (
			attachment.type === type
		));
	}


/**
	 * Returns the attachments
	 */
public getAttachments(type: string = null): Attachment[] {
		if (type === null) {
			return this.attachments;
		}

		return this.attachments.filter(attachment => (
			attachment.type === type
		));
	}


/**
	 * Removes the attachment
	 */
public deleteAttachment(): Promise<number> {
		if (this.isPhoto) {
			const [photo] = this.getAttachments('photo');

			// @ts-ignore
			return this.vk.api.photos.delete({
				owner_id: photo.ownerId,
				photo_id: photo.id
			});
		}

		if (this.isVideo) {
			const [video] = this.getAttachments('video');

			// @ts-ignore
			return this.vk.api.video.delete({
				owner_id: video.ownerId,
				video_id: video.id
			});
		}

		if (this.isAudio) {
			const [audio] = this.getAttachments('audio');

			// @ts-ignore
			return this.vk.api.audio.delete({
				owner_id: audio.ownerId,
				audio_id: audio.id
			});
		}

		return Promise.reject(new VKError({
			message: 'Unsupported event for deleting attachment',
			code: 'UNSUPPORTED_EVENT'
		}));
	}


/**
	 * Returns the custom data
	 */
public [inspectCustomData](): object {
		return copyParams(this, [
			'attachments',
			'isPhoto',
			'isVideo',
			'isAudio'
		]);
	}


export default class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link rel="preconnect" href="https://fonts.googleapis.com" />
          <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700;900&display=swap" rel="stylesheet" />

          <link rel="shortcut icon" href="/favicon.png" type="image/png" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}


render() {
    return (
      <Html>
        <Head>
          <link rel="preconnect" href="https://fonts.googleapis.com" />
          <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700;900&display=swap" rel="stylesheet" />

          <link rel="shortcut icon" href="/favicon.png" type="image/png" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }


services  <Link href="#">


link<


(): ThunkResult<Promise<RefreshProjectsAccepted>> => async (dispatch) => {
    dispatch({ type: 'REFRESH_PROJECTS_REQUESTED' });
    try {
      await apiFetch({
        url: window.api_urls.current_user_refresh(),
        dispatch,
        opts: {
          method: 'POST',
        },
      });
      return dispatch({
        type: 'REFRESH_PROJECTS_ACCEPTED' as const,
      });
    } catch (err) {
      dispatch({ type: 'REFRESH_PROJECTS_REJECTED' });
      throw err;
    }
  }


async (dispatch) => {
    dispatch({ type: 'REFRESH_PROJECTS_REQUESTED' });
    try {
      await apiFetch({
        url: window.api_urls.current_user_refresh(),
        dispatch,
        opts: {
          method: 'POST',
        },
      });
      return dispatch({
        type: 'REFRESH_PROJECTS_ACCEPTED' as const,
      });
    } catch (err) {
      dispatch({ type: 'REFRESH_PROJECTS_REJECTED' });
      throw err;
    }
  }


(): ProjectsRefreshing => ({
  type: 'REFRESHING_PROJECTS',
})


(): ThunkResult<Promise<FetchObjectsSucceeded>> => (dispatch) => {
    dispatch({ type: 'PROJECTS_REFRESHED' });
    return dispatch(
      fetchObjects({
        objectType: OBJECT_TYPES.PROJECT,
        reset: true,
      }),
    );
  }


(dispatch) => {
    dispatch({ type: 'PROJECTS_REFRESHED' });
    return dispatch(
      fetchObjects({
        objectType: OBJECT_TYPES.PROJECT,
        reset: true,
      }),
    );
  }


(message?: string): ThunkResult<ProjectsRefreshError> =>
  (dispatch) => {
    dispatch(
      addToast({
        heading: i18n.t('Uh oh. There was an error re-syncing Projects.'),
        details: message,
        variant: 'error',
      }),
    );
    return dispatch({ type: 'REFRESH_PROJECTS_ERROR' });
  }


(dispatch) => {
    dispatch(
      addToast({
        heading: i18n.t('Uh oh. There was an error re-syncing Projects.'),
        details: message,
        variant: 'error',
      }),
    );
    return dispatch({ type: 'REFRESH_PROJECTS_ERROR' });
  }


(projectId: string): ThunkResult<Promise<RefreshGitHubUsersAccepted>> =>
  async (dispatch) => {
    dispatch({ type: 'REFRESH_GH_USERS_REQUESTED', payload: projectId });
    try {
      await apiFetch({
        url: window.api_urls.project_refresh_github_users(projectId),
        dispatch,
        opts: {
          method: 'POST',
        },
      });
      return dispatch({
        type: 'REFRESH_GH_USERS_ACCEPTED' as const,
        payload: projectId,
      });
    } catch (err) {
      dispatch({ type: 'REFRESH_GH_USERS_REJECTED', payload: projectId });
      throw err;
    }
  }


async (dispatch) => {
    dispatch({ type: 'REFRESH_GH_USERS_REQUESTED', payload: projectId });
    try {
      await apiFetch({
        url: window.api_urls.project_refresh_github_users(projectId),
        dispatch,
        opts: {
          method: 'POST',
        },
      });
      return dispatch({
        type: 'REFRESH_GH_USERS_ACCEPTED' as const,
        payload: projectId,
      });
    } catch (err) {
      dispatch({ type: 'REFRESH_GH_USERS_REJECTED', payload: projectId });
      throw err;
    }
  }


(payload: Project): ProjectUpdated => ({
  type: 'PROJECT_UPDATE',
  payload,
})


({
    model,
    message,
    originating_user_id,
  }: {
    model: Project;
    message?: string;
    originating_user_id: string | null;
  }): ThunkResult<ProjectUpdated> =>
  (dispatch, getState) => {
    if (isCurrentUser(originating_user_id, getState())) {
      dispatch(
        addToast({
          heading: i18n.t(
            'Uh oh. There was an error re-syncing GitHub users for this project: “{{project_name}}”.',
            { project_name: model.name },
          ),
          details: message,
          variant: 'error',
        }),
      );
    }

    return dispatch(updateProject(model));
  }


(dispatch, getState) => {
    if (isCurrentUser(originating_user_id, getState())) {
      dispatch(
        addToast({
          heading: i18n.t(
            'Uh oh. There was an error re-syncing GitHub users for this project: “{{project_name}}”.',
            { project_name: model.name },
          ),
          details: message,
          variant: 'error',
        }),
      );
    }

    return dispatch(updateProject(model));
  }


(id: string): ThunkResult<Promise<RefreshOrgConfigsAction>> =>
  async (dispatch) => {
    dispatch({ type: 'REFRESH_ORG_CONFIGS_REQUESTED', payload: id });
    try {
      await apiFetch({
        url: window.api_urls.project_refresh_org_config_names(id),
        dispatch,
        opts: {
          method: 'POST',
        },
      });
      return dispatch({
        type: 'REFRESH_ORG_CONFIGS_ACCEPTED' as const,
        payload: id,
      });
    } catch (err) {
      dispatch({ type: 'REFRESH_ORG_CONFIGS_REJECTED', payload: id });
      throw err;
    }
  }


async (dispatch) => {
    dispatch({ type: 'REFRESH_ORG_CONFIGS_REQUESTED', payload: id });
    try {
      await apiFetch({
        url: window.api_urls.project_refresh_org_config_names(id),
        dispatch,
        opts: {
          method: 'POST',
        },
      });
      return dispatch({
        type: 'REFRESH_ORG_CONFIGS_ACCEPTED' as const,
        payload: id,
      });
    } catch (err) {
      dispatch({ type: 'REFRESH_ORG_CONFIGS_REJECTED', payload: id });
      throw err;
    }
  }


interface ProjectUpdated {
  type: 'PROJECT_UPDATE';
  payload: Project;
}


interface ProjectUpdateError {
  type: 'PROJECT_UPDATE_ERROR';
  payload: Project;
}


interface RefreshProjectsRequested {
  type: 'REFRESH_PROJECTS_REQUESTED';
}


interface RefreshProjectsAccepted {
  type: 'REFRESH_PROJECTS_ACCEPTED';
}


interface RefreshProjectsRejected {
  type: 'REFRESH_PROJECTS_REJECTED';
}


interface ProjectsRefreshing {
  type: 'REFRESHING_PROJECTS';
}


export interface ProjectsRefreshed {
  type: 'PROJECTS_REFRESHED';
}


export interface ProjectsRefreshError {
  type: 'REFRESH_PROJECTS_ERROR';
}


interface RefreshGitHubUsersRequested {
  type: 'REFRESH_GH_USERS_REQUESTED';
  payload: string;
}


interface RefreshGitHubUsersAccepted {
  type: 'REFRESH_GH_USERS_ACCEPTED';
  payload: string;
}


interface RefreshGitHubUsersRejected {
  type: 'REFRESH_GH_USERS_REJECTED';
  payload: string;
}


interface RefreshOrgConfigsAction {
  type:
    | 'REFRESH_ORG_CONFIGS_REQUESTED'
    | 'REFRESH_ORG_CONFIGS_ACCEPTED'
    | 'REFRESH_ORG_CONFIGS_REJECTED';
  payload: string;
}


export type ProjectsAction =
  | ProjectUpdated
  | ProjectUpdateError
  | RefreshProjectsRequested
  | RefreshProjectsAccepted
  | RefreshProjectsRejected
  | ProjectsRefreshing
  | ProjectsRefreshed
  | ProjectsRefreshError
  | RefreshGitHubUsersRequested
  | RefreshGitHubUsersAccepted
  | RefreshGitHubUsersRejected
  | RefreshOrgConfigsAction;


() => {
    const objects = [{a: 1}, {a: 1}, {a: 2}, {a: 2}, {a: 2}, {a: 3}];
    describe('without selector', () => {
        it('must emit unique values', () => {
            const input = [1, 1, 2, 2, 2, 3];
            const output = pipe(input, distinct());
            expect([...output]).to.eql([1, 2, 3]);
        });
        it('must emit repeated objects', () => {
            // this test shows that any new object is treated as unique by default,
            // and therefore default unique selection for objects is not possible;
            const output = pipe(objects, distinct());
            expect([...output]).to.deep.equal(objects);
        });
    });
    describe('with selector', () => {
        it('must emit unique values based on selector', () => {
            const indexes: any[] = [];
            const output = pipe(
                objects,
                distinct((sel, idx) => {
                    indexes.push(idx);
                    return sel.a;
                })
            );
            expect([...output]).to.eql([{a: 1}, {a: 2}, {a: 3}]);
            expect(indexes).to.eql([0, 1, 2, 3, 4, 5]); // index is for each original element
        });
        it('must ignore invalid selectors', () => {
            const output = pipe([1, 1, 2, 2], distinct('bla' as any));
            expect([...output]).to.eql([1, 2]);
        });
    });
}


() => {
        it('must emit unique values', () => {
            const input = [1, 1, 2, 2, 2, 3];
            const output = pipe(input, distinct());
            expect([...output]).to.eql([1, 2, 3]);
        });
        it('must emit repeated objects', () => {
            // this test shows that any new object is treated as unique by default,
            // and therefore default unique selection for objects is not possible;
            const output = pipe(objects, distinct());
            expect([...output]).to.deep.equal(objects);
        });
    }


() => {
            const input = [1, 1, 2, 2, 2, 3];
            const output = pipe(input, distinct());
            expect([...output]).to.eql([1, 2, 3]);
        }


() => {
            // this test shows that any new object is treated as unique by default,
            // and therefore default unique selection for objects is not possible;
            const output = pipe(objects, distinct());
            expect([...output]).to.deep.equal(objects);
        }


() => {
        it('must emit unique values based on selector', () => {
            const indexes: any[] = [];
            const output = pipe(
                objects,
                distinct((sel, idx) => {
                    indexes.push(idx);
                    return sel.a;
                })
            );
            expect([...output]).to.eql([{a: 1}, {a: 2}, {a: 3}]);
            expect(indexes).to.eql([0, 1, 2, 3, 4, 5]); // index is for each original element
        });
        it('must ignore invalid selectors', () => {
            const output = pipe([1, 1, 2, 2], distinct('bla' as any));
            expect([...output]).to.eql([1, 2]);
        });
    }


() => {
            const indexes: any[] = [];
            const output = pipe(
                objects,
                distinct((sel, idx) => {
                    indexes.push(idx);
                    return sel.a;
                })
            );
            expect([...output]).to.eql([{a: 1}, {a: 2}, {a: 3}]);
            expect(indexes).to.eql([0, 1, 2, 3, 4, 5]); // index is for each original element
        }


(sel, idx) => {
                    indexes.push(idx);
                    return sel.a;
                }


() => {
            const output = pipe([1, 1, 2, 2], distinct('bla' as any));
            expect([...output]).to.eql([1, 2]);
        }


() => {
    const objects = [{a: 1}, {a: 1}, {a: 2}, {a: 2}, {a: 2}, {a: 3}];
    describe('without selector', () => {
        it('must emit unique values', async () => {
            const input = _async([1, 1, 2, 2, 2, 3]);
            const output = pipe(input, distinct());
            expect(await _asyncValues(output)).to.eql([1, 2, 3]);
        });
        it('must emit repeated objects', async () => {
            // this test shows that any new object is treated as unique by default,
            // and therefore default unique selection for objects is not possible;
            const output = pipe(_async(objects), distinct());
            expect(await _asyncValues(output)).to.deep.equal(objects);
        });
    });
    describe('with selector', () => {
        it('must emit unique values based on selector', async () => {
            const indexes: any[] = [];
            const output = pipe(
                _async(objects),
                distinct((sel, idx) => {
                    indexes.push(idx);
                    return sel.a;
                })
            );
            expect(await _asyncValues(output)).to.eql([{a: 1}, {a: 2}, {a: 3}]);
            expect(indexes).to.eql([0, 1, 2, 3, 4, 5]); // index is for each original element
        });
        it('must ignore invalid selectors', async () => {
            const output = pipe(_async([1, 1, 2, 2]), distinct('bla' as any));
            expect(await _asyncValues(output)).to.eql([1, 2]);
        });
    });
}


() => {
        it('must emit unique values', async () => {
            const input = _async([1, 1, 2, 2, 2, 3]);
            const output = pipe(input, distinct());
            expect(await _asyncValues(output)).to.eql([1, 2, 3]);
        });
        it('must emit repeated objects', async () => {
            // this test shows that any new object is treated as unique by default,
            // and therefore default unique selection for objects is not possible;
            const output = pipe(_async(objects), distinct());
            expect(await _asyncValues(output)).to.deep.equal(objects);
        });
    }


async () => {
            const input = _async([1, 1, 2, 2, 2, 3]);
            const output = pipe(input, distinct());
            expect(await _asyncValues(output)).to.eql([1, 2, 3]);
        }


async () => {
            // this test shows that any new object is treated as unique by default,
            // and therefore default unique selection for objects is not possible;
            const output = pipe(_async(objects), distinct());
            expect(await _asyncValues(output)).to.deep.equal(objects);
        }


() => {
        it('must emit unique values based on selector', async () => {
            const indexes: any[] = [];
            const output = pipe(
                _async(objects),
                distinct((sel, idx) => {
                    indexes.push(idx);
                    return sel.a;
                })
            );
            expect(await _asyncValues(output)).to.eql([{a: 1}, {a: 2}, {a: 3}]);
            expect(indexes).to.eql([0, 1, 2, 3, 4, 5]); // index is for each original element
        });
        it('must ignore invalid selectors', async () => {
            const output = pipe(_async([1, 1, 2, 2]), distinct('bla' as any));
            expect(await _asyncValues(output)).to.eql([1, 2]);
        });
    }


async () => {
            const indexes: any[] = [];
            const output = pipe(
                _async(objects),
                distinct((sel, idx) => {
                    indexes.push(idx);
                    return sel.a;
                })
            );
            expect(await _asyncValues(output)).to.eql([{a: 1}, {a: 2}, {a: 3}]);
            expect(indexes).to.eql([0, 1, 2, 3, 4, 5]); // index is for each original element
        }


async () => {
            const output = pipe(_async([1, 1, 2, 2]), distinct('bla' as any));
            expect(await _asyncValues(output)).to.eql([1, 2]);
        }


async assert => {
        // A fully loaded dashboard should have no major troubles
        const currentUser = server.create('user', 'loggedIn');
        const nodes = server.createList('node', 10, {}, 'withContributors');
        server.create('node', {
            id: noteworthyNode,
            linkedNodes: nodes.slice(0, 5),
            title: 'NNW',
        });
        server.create('node', {
            id: popularNode,
            linkedNodes: nodes.slice(5, 10),
            title: 'Popular',
        });
        for (const node of nodes.slice(4, 10)) {
            server.create('contributor', { node, users: currentUser, index: 11 });
        }
        server.createList('institution', 20);

        await visit('/dashboard');

        assert.equal(currentURL(), '/dashboard', 'We stayed on the proper page');
        assert.dom('nav.navbar').exists();
        assert.dom('nav.navbar .service-name').hasText('OSF HOME');
        assert.dom('nav.navbar .secondary-nav-dropdown .nav-profile-name')
            .hasText(currentUser.fullName, 'User\'s name is in navbar');

        await percySnapshot(assert);
    }


async assert => {
        server.create('user', 'loggedIn');
        const institutions = server.createList('institution', 20);

        await visit('/dashboard');
        assert.dom(`[data-test-institution-carousel] img[name*="${institutions[0].name}"]`).exists();
        assert.dom('[data-test-institution-carousel-item="1"]').exists();
        assert.dom('[data-test-institution-carousel-item="6"]').isNotVisible();

        // Click next to make item six visible
        await click('.carousel-control.right');

        assert.dom(`[data-test-institution-carousel-item] a[href="/institutions/${institutions[6].id}/"]`)
            .exists('Institutions are linked properly');

        assert.dom('[data-test-institution-carousel-item="6"]').isVisible();
    }


async assert => {
        server.create('user', 'loggedIn');
        const nodes = server.createList('node', 10, {}, 'withContributors');
        server.create('node', {
            id: noteworthyNode,
            linkedNodes: nodes.slice(0, 5),
            title: 'NNW',
        });
        server.create('node', {
            id: popularNode,
            linkedNodes: nodes.slice(5, 10),
            title: 'Popular',
        });
        await visit('/dashboard');
        let i = 0;
        for (const node of nodes) {
            const { id, title, description } = node.attrs;
            let projectType = 'noteworthy';
            if (i > 4) {
                projectType = 'popular';
            }
            i++;
            assert.dom(`[data-test-${projectType}-project="${id}"]`)
                .exists(`The ${projectType} project ${id} exists`);
            assert.dom(`[data-test-${projectType}-project="${id}"] [data-test-nnwp-project-title]`)
                .includesText(title, `The ${projectType} project ${id} has correct title`);
            assert.dom(`[data-test-${projectType}-project="${id}"] [data-test-nnwp-project-description]`)
                .includesText(description, `The ${projectType} project ${id} has correct description`);
        }
    }


async assert => {
        server.create('user', 'loggedIn');
        await visit('/dashboard');
        assert.dom('div[class*="quick-project"]')
            .includesText('You have no projects yet. Create a project with the button on the top right.');
        await percySnapshot(assert);
    }


async assert => {
        const currentUser = server.create('user', 'loggedIn');
        const node = server.create('node', {}, 'withContributors');
        server.create('contributor', { node, users: currentUser, index: 11 });
        await visit('/dashboard');
        assert.dom('div[class*="quick-project"]')
            .doesNotIncludeText('You have no projects yet. Create a project with the button on the top right.');
        assert.dom('div[class*="quick-project"]').includesText(node.attrs.title);
    }


async assert => {
        server.loadFixtures('regions');
        server.create('user', 'loggedIn');
        const title = 'Giraffical Interchange Format';
        await visit('/dashboard');
        assert.dom('div[class*="quick-project"]')
            .includesText('You have no projects yet. Create a project with the button on the top right.');
        assert.dom('div[class*="quick-project"]').doesNotIncludeText(title);
        await percySnapshot(assert);

        await click('[data-test-create-project-modal-button]');
        assert.dom('img[alt*="Missing translation"]').doesNotExist();
        assert.dom('[data-test-create-project-header]').includesText('Create new project');
        await fillIn('[data-test-new-project-title]', title);
        await click('[data-test-create-project-submit]');
        await click('[data-test-stay-here]');

        assert.dom('div[class*="quick-project"]')
            .doesNotIncludeText('You have no projects yet. Create a project with the button on the top right.');
        assert.dom('div[class*="quick-project"]').includesText(title);
        const newNode = server.schema.nodes.findBy({ title });
        assert.equal(newNode.attrs.regionId, 'us');
    }