UNPKG

@qodalis/angular-cli

Version:

A lightweight and flexible command-line interface (CLI) tool designed to streamline workflows and automate tasks for developers.

3,935 lines 166 kB
import * as i0 from '@angular/core';
import { InjectionToken, Injectable, Inject, EventEmitter, Component, Input, Output, ViewChild, ViewEncapsulation, HostListener, Injector, NgModule } from '@angular/core';
import * as i1 from '@angular/common';
import { CommonModule } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import * as i2 from 'primeng/button';
import { ButtonModule } from 'primeng/button';
import { TabMenuModule } from 'primeng/tabmenu';
import { MenuModule } from 'primeng/menu';
import { SplitButtonModule } from 'primeng/splitbutton';
import * as i3 from 'primeng/tooltip';
import { TooltipModule } from 'primeng/tooltip';
import { CliForegroundColor, CliIcon, formatJson, delay, CliLogLevel, initializeBrowserEnvironment, DefaultThemes, getRightOfWord, CancellablePromise, getParameterValue, colorFirstWord, DefaultLibraryAuthor, LIBRARY_VERSION as LIBRARY_VERSION$1 } from '@qodalis/cli-core';
import { BehaviorSubject, Subject, map as map$1, combineLatest, filter, of, Subscription, firstValueFrom } from 'rxjs';
import { map, distinctUntilChanged } from 'rxjs/operators';
import { Terminal } from '@xterm/xterm';
import { FitAddon } from '@xterm/addon-fit';
import { WebLinksAddon } from '@xterm/addon-web-links';
import { Unicode11Addon } from '@xterm/addon-unicode11';

/**
 * Represents a command processor token for dependency injection
 */
const CliCommandProcessor_TOKEN = new InjectionToken('cli-processors');
/**
 * Represents a user session service token for dependency injection
 */
const ICliUserSessionService_TOKEN = new InjectionToken('cli-user-session-service');
/**
 * Represents a user store service token for dependency injection
 */
const ICliUsersStoreService_TOKEN = new InjectionToken('cli-users-store-service');
/**
 * Represents a ping server service token for dependency injection
 */
const ICliPingServerService_TOKEN = new InjectionToken('cli-ping-server-service');
/**
 * Represents a logger token for dependency injection
 */
const CliLogger_TOKEN = new InjectionToken('cli-logger');
/**
 * Represents a command processor registry token for dependency injection
 */
const CliProcessorsRegistry_TOKEN = new InjectionToken('cli-processors-registry');
const CliServiceProvider_TOKEN = new InjectionToken('cli-service-provider');

class CliStateStore {
    constructor(services, name, initialState) {
        this.services = services;
        this.name = name;
        this.initialState = initialState;
        this.state$ = new BehaviorSubject(initialState);
        this.storageKey = `store-state-${name}`;
    }
    getState() {
        return this.state$.getValue();
    }
    updateState(newState) {
        this.state$.next({ ...this.getState(), ...newState });
    }
    select(selector) {
        return this.state$.asObservable().pipe(map(selector), distinctUntilChanged());
    }
    subscribe(callback) {
        return this.state$.asObservable().subscribe(callback);
    }
    reset() {
        this.state$.next(this.initialState);
    }
    async persist() {
        const keyValueStore = this.services.get('cli-key-value-store');
        await keyValueStore.set(this.storageKey, this.getState());
    }
    async initialize() {
        const keyValueStore = this.services.get('cli-key-value-store');
        const state = await keyValueStore.get(this.storageKey);
        if (state) {
            this.state$.next(state);
        }
    }
}

class CliStateStoreManager {
    constructor(services) {
        this.services = services;
        this.stores = new Map();
    }
    getStateStore(name, defaultState) {
        if (!this.stores.has(name)) {
            this.stores.set(name, new CliStateStore(this.services, name, defaultState ?? {}));
        }
        return this.stores.get(name);
    }
    getProcessorStateStore(processor) {
        const registry = this.services.get(CliProcessorsRegistry_TOKEN);
        const rootProcessor = registry.getRootProcessor(processor);
        return this.getStateStore(rootProcessor.stateConfiguration?.storeName ||
            rootProcessor.command, rootProcessor.stateConfiguration?.initialState);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliStateStoreManager, deps: [{ token: CliServiceProvider_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliStateStoreManager }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliStateStoreManager, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [CliServiceProvider_TOKEN]
                }] }]; } });

class CliCommandExecutionContext {
    constructor(context, processor) {
        this.context = context;
        this.userSession = context.userSession;
        this.spinner = context.spinner;
        this.progressBar = context.progressBar;
        this.textAnimator = context.textAnimator;
        this.onAbort = context.onAbort;
        this.terminal = context.terminal;
        this.writer = context.writer;
        this.executor = context.executor;
        this.clipboard = context.clipboard;
        this.options = context.options;
        this.showPrompt = context.showPrompt;
        this.setContextProcessor = context.setContextProcessor;
        this.process = context.process;
        this.logger = context.logger;
        this.services = context.services;
        this.state = context.services
            .get(CliStateStoreManager)
            .getProcessorStateStore(processor);
    }
}

class CliTerminalWriter {
    constructor(terminal) {
        this.terminal = terminal;
    }
    write(text) {
        this.terminal.write(text);
    }
    writeln(text) {
        this.terminal.writeln(text || '');
    }
    writeSuccess(message) {
        this.writeLog(message, CliForegroundColor.Green, CliIcon.CheckIcon);
    }
    writeInfo(message) {
        this.writeLog(message, CliForegroundColor.Cyan, CliIcon.InfoIcon);
    }
    writeWarning(message) {
        this.writeLog(message, CliForegroundColor.Yellow, CliIcon.WarningIcon);
    }
    writeError(message) {
        this.writeLog(message, CliForegroundColor.Red, CliIcon.CrossIcon);
    }
    writeLog(message, color, icon) {
        this.terminal.writeln(this.wrapInColor(icon ? icon + ' ' + message : message, color));
    }
    writeDivider(options) {
        const { color, length: oLength, char: oChar } = options || {};
        let length = oLength ?? 80;
        let char = oChar ?? '-';
        if (this.terminal.cols < length) {
            length = this.terminal.cols;
        }
        let text = char.repeat(length);
        if (color) {
            text = this.wrapInColor(text, color);
        }
        this.writeln(text);
    }
    wrapInColor(text, color) {
        return color + text + CliForegroundColor.Reset;
    }
    wrapInBackgroundColor(text, color) {
        return color + text + CliForegroundColor.Reset;
    }
    writeJson(json) {
        this.terminal.writeln(formatJson(json));
    }
    writeToFile(fileName, content) {
        const blob = new Blob([content], { type: 'text/plain' });
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = fileName;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }
    writeObjectsAsTable(objects) {
        if (objects.length === 0) {
            this.writeInfo('No objects to display');
            return;
        }
        const headers = Object.keys(objects[0]);
        const rows = objects.map((object) => headers.map((header) => object[header]));
        this.writeTable(headers, rows);
    }
    writeTable(headers, rows) {
        // Calculate column widths
        const colWidths = headers.map((header, colIndex) => Math.max(header.length, ...rows.map((row) => row[colIndex]?.length || 0)));
        // Function to pad text to a specific width
        const padText = (text, width) => text?.toString()?.padEnd(width, ' ');
        // Write the header
        this.write(headers
            .map((header, i) => this.wrapInColor(padText(header, colWidths[i]), CliForegroundColor.Yellow))
            .join(' | ') + '\r\n');
        this.write('-'.repeat(colWidths.reduce((sum, w) => sum + w + 3, -3)) + '\r\n');
        // Write the rows
        rows.forEach((row) => {
            this.write(row.map((cell, i) => padText(cell, colWidths[i])).join(' | ') +
                '\r\n');
        });
    }
}

class CliTerminalSpinner {
    constructor(terminal) {
        this.terminal = terminal;
        this.isRunning = false;
        this.text = '';
        this.spinnerFrames = ['|', '/', '-', '\\'];
        this.spinnerIndex = 0;
    }
    show(text) {
        if (text) {
            this.text = text;
        }
        this.isRunning = true;
        this.spinnerInterval = setInterval(() => {
            // Clear the current line
            this.terminal.write('\x1b[2K\r');
            // Write the spinner frame
            this.terminal.write(this.spinnerFrames[this.spinnerIndex] +
                (this.text.length > 0 ? ` ${this.text}` : ''));
            // Update the frame index
            this.spinnerIndex =
                (this.spinnerIndex + 1) % this.spinnerFrames.length;
        }, 100);
    }
    hide() {
        this.isRunning = false;
        if (this.spinnerInterval) {
            clearInterval(this.spinnerInterval);
            this.spinnerInterval = null;
        }
        // Clear the spinner character and reset the line
        this.terminal.write('\x1b[2K\r');
        this.text = '';
    }
    setText(text) {
        this.text = text;
    }
}

class CliTerminalProgressBar {
    constructor(terminal) {
        this.terminal = terminal;
        this.isRunning = false;
        this.text = '';
        this.progress = 0;
        this.total = 100;
        this.progressText = '';
    }
    show(text) {
        this.isRunning = true;
        this.progress = 0;
        this.text = text || '';
        // Update progress bar every 100ms
        this.progressBarInterval = setInterval(() => {
            this.updateProgressBar();
            // Stop when progress reaches 100%
            if (this.progress > this.total) {
                this.progress = this.total;
                this.hide(); // Stop the progress bar
            }
        }, 100);
    }
    hide() {
        this.isRunning = false;
        if (this.progressBarInterval) {
            clearInterval(this.progressBarInterval);
            this.progressBarInterval = null;
        }
        this.clearCurrentLine();
        this.text = '';
        this.progressText = '';
    }
    update(progress, options) {
        if (options?.type === 'increment') {
            this.progress += progress;
        }
        else {
            this.progress = progress;
        }
        if (this.progress > this.total) {
            this.progress = this.total;
        }
        this.updateProgressBar();
    }
    complete() {
        this.progress = 100;
        this.hide();
    }
    setText(text) {
        this.text = text;
        this.updateProgressBar();
    }
    updateProgressBar() {
        const totalBars = 50; // Length of the progress bar
        const filledBars = Math.round((this.progress / this.total) * totalBars);
        const emptyBars = totalBars - filledBars;
        const progressBar = `[${'#'.repeat(filledBars)}${'.'.repeat(emptyBars)}]`;
        const percentage = `${this.progress}%`.padStart(4, ' ');
        const text = this.text.length > 0 ? ` ${this.text}` : '';
        this.clearCurrentLine();
        this.progressText = `${progressBar} ${percentage} ${text}`;
        this.terminal.write(this.progressText); // Write progress bar
    }
    clearCurrentLine() {
        const wrappedLines = Math.ceil(this.progressText.length / this.terminal.cols);
        for (let i = 0; i < wrappedLines; i++) {
            this.terminal.write('\x1b[2K'); // Clear the current line
            this.terminal.write('\r'); // Move the cursor to the start of the line
            if (i < wrappedLines - 1) {
                this.terminal.write('\x1b[F'); // Move the cursor up for all but the last line
            }
            if (i === wrappedLines - 1) {
                this.terminal.write('\r');
            }
        }
    }
}

class CliClipboard {
    constructor(context) {
        this.context = context;
    }
    async write(text) {
        try {
            await navigator.clipboard.writeText(text);
        }
        catch (error) {
            this.context.writer.writeError('Failed to write to clipboard');
        }
    }
    async read() {
        try {
            return await navigator.clipboard.readText();
        }
        catch (error) {
            this.context.writer.writeError('Failed to read from clipboard');
            return '';
        }
    }
}

class ProcessExitedError extends Error {
    constructor(code) {
        super(`Process exited with code ${code}`);
        this.name = 'ProcessExitedError';
        this.code = code;
        Object.setPrototypeOf(this, new.target.prototype);
    }
}

class CliExecutionProcess {
    constructor(context) {
        this.context = context;
        this.running = false;
    }
    exit(code, options) {
        code = code ?? 0;
        this.exited = true;
        this.exitCode = code;
        if (!options?.silent) {
            throw new ProcessExitedError(code);
        }
    }
    output(data) {
        this.data = data;
    }
    start() {
        this.exited = undefined;
        this.exitCode = undefined;
        this.data = undefined;
        this.running = true;
    }
    end() {
        this.running = false;
        this.exitCode = 0;
    }
}

class CliTerminalTextAnimator {
    constructor(terminal) {
        this.terminal = terminal;
        this.isRunning = false;
        this.text = '';
    }
    show(text) {
        this.showText(text || '');
    }
    showText(text, options) {
        const { speed, removeAfterTyping } = options || {};
        if (this.isRunning) {
            return; // Prevent multiple animations
        }
        this.isRunning = true;
        this.text = text || this.text;
        let index = 0;
        let isTyping = true;
        this.animationInterval = setInterval(() => {
            if (isTyping) {
                // Write text character by character
                this.terminal.write(this.text[index]);
                index++;
                // Switch to erasing mode once typing is done
                if (index === this.text.length) {
                    isTyping = false;
                }
            }
            else if (removeAfterTyping) {
                clearInterval(this.animationInterval);
                this.animationInterval = null;
                delay(1000).then(() => {
                    for (let i = 0; i < this.text.length; i++) {
                        this.terminal.write('\b \b');
                    }
                });
                this.isRunning = false;
            }
            else {
                // Erase text character by character
                this.terminal.write('\b \b'); // Backspace, overwrite with space, and backspace again
                index--;
                // Stop animation once all characters are erased
                if (index < 0) {
                    clearInterval(this.animationInterval);
                    this.animationInterval = null;
                    this.isRunning = false;
                }
            }
        }, speed || 100);
    }
    /**
     * Hide the animation and reset the state.
     */
    hide() {
        if (!this.isRunning) {
            return; // Prevent stopping if not running
        }
        this.isRunning = false;
        if (this.animationInterval) {
            clearInterval(this.animationInterval);
            this.animationInterval = null;
        }
        this.text = '';
    }
}

class CliExecutionContext {
    constructor(injector, terminal, executor, showPrompt, cliOptions) {
        this.terminal = terminal;
        this.executor = executor;
        this.showPrompt = showPrompt;
        this.onAbort = new Subject();
        //initialize services
        this.services = injector.get(CliServiceProvider_TOKEN);
        //initialize state store
        const stateStoreManager = injector.get(CliStateStoreManager);
        this.state = stateStoreManager.getStateStore('shared');
        this.options = cliOptions;
        this.writer = new CliTerminalWriter(terminal);
        this.spinner = new CliTerminalSpinner(terminal);
        this.progressBar = new CliTerminalProgressBar(terminal);
        this.textAnimator = new CliTerminalTextAnimator(terminal);
        this.clipboard = new CliClipboard(this);
        this.process = new CliExecutionProcess(this);
        //initialize logger
        this.logger = injector.get(CliLogger_TOKEN);
        this.logger.setCliLogLevel(cliOptions?.logLevel || CliLogLevel.ERROR);
    }
    setContextProcessor(processor, silent) {
        if (!processor) {
            this.contextProcessor = processor;
            return;
        }
        if (!silent) {
            this.writer.writeInfo('Set ' +
                processor?.command +
                ' as context processor, press Ctrl+C to exit');
        }
        this.contextProcessor = processor;
    }
    /**
     * Checks if there is a progress running
     * @returns true if there is a progress running
     * @returns false if there is no progress running
     */
    isProgressRunning() {
        return (this.progressBar.isRunning ||
            this.spinner.isRunning ||
            this.textAnimator.isRunning);
    }
    /**
     * Aborts the current command
     */
    abort() {
        this.onAbort.next();
    }
    setSession(session) {
        this.userSession = session;
    }
}

class CliKeyValueStore {
    constructor() {
        this.dbName = 'CliKeyValueDB';
        this.storeName = 'KeyValueStore';
    }
    /**
     * Initializes the IndexedDB instance.
     */
    initialize() {
        return new Promise((resolve, reject) => {
            const request = indexedDB.open(this.dbName, 1);
            request.onupgradeneeded = (event) => {
                const db = event.target.result;
                if (!db.objectStoreNames.contains(this.storeName)) {
                    db.createObjectStore(this.storeName);
                }
            };
            request.onsuccess = (event) => {
                this.db = event.target.result;
                resolve();
            };
            request.onerror = (event) => {
                console.error('Error initializing IndexedDB:', event);
                reject();
            };
        });
    }
    /**
     * Retrieves a value by key.
     * @param key - The key to retrieve the value for.
     * @returns A promise resolving to the value or undefined if not found.
     */
    async get(key) {
        return new Promise((resolve, reject) => {
            try {
                const transaction = this.db.transaction(this.storeName, 'readonly');
                const store = transaction.objectStore(this.storeName);
                const request = store.get(key);
                request.onsuccess = () => {
                    resolve(request.result);
                };
                request.onerror = (event) => {
                    console.error('Error getting value:', event);
                    reject(undefined);
                };
            }
            catch (e) {
                console.error('Error getting value:', e);
                reject(undefined);
            }
        });
    }
    /**
     * Sets a key-value pair in the store.
     * @param key - The key to set.
     * @param value - The value to store.
     * @returns A promise resolving when the value is stored.
     */
    async set(key, value) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(this.storeName, 'readwrite');
            const store = transaction.objectStore(this.storeName);
            const request = store.put(value, key);
            request.onsuccess = () => resolve();
            request.onerror = (event) => {
                console.error('Error setting value:', event);
                reject();
            };
        });
    }
    /**
     * Removes a key-value pair by key.
     * @param key - The key to remove.
     * @returns A promise resolving when the key is removed.
     */
    async remove(key) {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(this.storeName, 'readwrite');
            const store = transaction.objectStore(this.storeName);
            const request = store.delete(key);
            request.onsuccess = () => resolve();
            request.onerror = (event) => {
                console.error('Error removing value:', event);
                reject();
            };
        });
    }
    /**
     * Clears all key-value pairs from the store.
     * @returns A promise resolving when the store is cleared.
     */
    async clear() {
        return new Promise((resolve, reject) => {
            const transaction = this.db.transaction(this.storeName, 'readwrite');
            const store = transaction.objectStore(this.storeName);
            const request = store.clear();
            request.onsuccess = () => resolve();
            request.onerror = (event) => {
                console.error('Error clearing store:', event);
                reject();
            };
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliKeyValueStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliKeyValueStore, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliKeyValueStore, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return []; } });

class CliBoot {
    constructor(implementations, registry) {
        this.implementations = implementations;
        this.registry = registry;
        this.initialized = false;
        this.initializing = false;
    }
    async boot(context) {
        context.spinner?.show(CliIcon.Rocket + '  Booting...');
        if (this.initialized || this.initializing) {
            await this.bootShared(context);
            context.spinner?.hide();
            return;
        }
        this.initializing = true;
        await this.registerServices(context);
        initializeBrowserEnvironment({
            context,
            handlers: [
                async (module) => {
                    await this.registerUmdModule(module, context);
                },
            ],
        });
        let processors = this.implementations;
        //TODO: refactor in a better way
        if (!context.options?.usersModule?.enabled) {
            processors = processors.filter((p) => p.metadata?.module !== 'users');
        }
        processors.forEach((impl) => this.registry.registerProcessor(impl));
        await this.bootShared(context);
        context.spinner?.hide();
        this.initialized = true;
    }
    async bootShared(context) {
        await this.initializeProcessorsInternal(context, this.registry.processors);
        await delay(300);
    }
    async initializeProcessorsInternal(context, processors, parent) {
        try {
            for (const p of processors) {
                p.parent = parent;
                if (p.initialize) {
                    const processorContext = new CliCommandExecutionContext(context, p);
                    await processorContext.state.initialize();
                    await p.initialize(processorContext);
                }
                if (p.processors && p.processors.length > 0) {
                    await this.initializeProcessorsInternal(context, p.processors, p);
                }
            }
        }
        catch (e) {
            context.writer.writeError(`Error initializing processors: ${e}`);
        }
    }
    async registerServices(context) {
        context.services.set([
            {
                provide: 'cli-key-value-store',
                useValue: context.services.get(CliKeyValueStore),
            },
        ]);
    }
    async registerUmdModule(module, context) {
        const { logger } = context;
        if (!module) {
            return;
        }
        if (module.processors) {
            logger.info('Registering processors from module ' + module.name);
            for (const processor of module.processors) {
                this.registry.registerProcessor(processor);
            }
            await this.initializeProcessorsInternal(context, module.processors);
        }
        else {
            logger.warn(`Module ${module.name} has no processors`);
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliBoot, deps: [{ token: CliCommandProcessor_TOKEN }, { token: CliProcessorsRegistry_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliBoot, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliBoot, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [CliCommandProcessor_TOKEN]
                }] }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [CliProcessorsRegistry_TOKEN]
                }] }]; } });

// Automatically generated during build
const LIBRARY_VERSION = '1.0.35';

const CLi_Name_Art = `
   ____            _       _ _        _____ _      _____
  / __ \\          | |     | (_)      / ____| |    |_   _|
 | |  | | ___   __| | __ _| |_ ___  | |    | |      | |
 | |  | |/ _ \\ / _\` |/ _\` | | / __| | |    | |      | |
 | |__| | (_) | (_| | (_| | | \\__ \\ | |____| |____ _| |_
  \\___\\_\\\\___/ \\__,_|\\__,_|_|_|___/  \\_____|______|_____|
`;
const hotkeysInfo = [
    {
        key: 'Ctrl + C',
        description: 'Cancel the current command (if possible) or copy the current selection',
    },
    {
        key: 'Ctrl + V',
        description: 'Paste the copied text',
    },
    {
        key: 'Ctrl + L',
        description: 'Clear the screen',
    },
];

/**
 * A utility class for parsing command strings into command names and arguments.
 */
class CommandParser {
    /**
     * Parse a command string into a full command name and arguments.
     * @param command - The full command string.
     * @returns An object containing the full command name and arguments.
     */
    parse(command) {
        try {
            // Match quoted strings, single-quoted strings, or unquoted words
            const regex = /(?:--?([a-zA-Z0-9-_]+)(?:=("[^"]*"|'[^']*'|[^\s]+))?)|(?:[^\s]+)/g;
            const matches = Array.from(command.matchAll(regex));
            if (matches.length === 0) {
                throw new Error('Invalid command string');
            }
            const commandParts = [];
            const args = [];
            // Process matches
            matches.forEach((match) => {
                if (match[1]) {
                    // Handle arguments
                    const key = match[1];
                    let value = match[2];
                    if (value) {
                        // Remove surrounding quotes, if any
                        value = value.replace(/^['"]|['"]$/g, '');
                    }
                    else {
                        // Flag without a value
                        value = true;
                    }
                    args.push({
                        name: key,
                        value: this.parseValue(value),
                    });
                }
                else if (!match[0].startsWith('--')) {
                    // Command name
                    commandParts.push(match[0]);
                }
            });
            const commandName = commandParts.join(' ');
            return {
                commandName,
                args,
            };
        }
        catch (e) {
            console.error('Unable to parse the command:', command, e);
            return {
                commandName: '',
                args: [],
            };
        }
    }
    /**
     * Parse individual values to their appropriate type.
     * @param value - The value to parse.
     * @returns The parsed value.
     */
    parseValue(value) {
        if (!isNaN(Number(value)))
            return Number(value); // Convert numeric strings to numbers
        if (value === 'true' || value === 'false')
            return value === 'true'; // Convert "true"/"false" to boolean
        return value; // Return as string otherwise
    }
}

const resolveCliProvider = (token, provider) => ({
    provide: token,
    useExisting: provider,
    multi: true,
});
const resolveCommandProcessorProvider = (provider) => [
    provider,
    resolveCliProvider(CliCommandProcessor_TOKEN, provider),
];

const openLink = (link) => {
    try {
        window.open(link, '_blank');
    }
    catch (e) {
        console.error(e);
    }
};
const getGreetingBasedOnTime = (date) => {
    const currentHour = (date ?? new Date()).getHours();
    if (currentHour >= 5 && currentHour < 12) {
        return 'Good morning! Wishing you a productive day ahead!';
    }
    else if (currentHour >= 12 && currentHour < 18) {
        return 'Good afternoon! Keep up the great work!';
    }
    else if (currentHour >= 18 && currentHour < 22) {
        return 'Good evening! Hope you had a fantastic day!';
    }
    else {
        return 'Good night! Rest well and see you tomorrow!';
    }
};

/**
 * Service that displays the welcome message to the user.
 */
class CliWelcomeMessageService {
    /**
     * Displays the welcome message to the user.
     * @param context
     * @returns void
     */
    displayWelcomeMessage(context) {
        const welcomeConfig = context.options?.welcomeMessage;
        // Handle the 'show' property
        if (welcomeConfig?.show) {
            const showOption = welcomeConfig.show;
            // Determine if the welcome message should be shown
            if (!this.shouldDisplayWelcomeMessage(showOption)) {
                context.showPrompt();
                return;
            }
        }
        if (welcomeConfig?.message) {
            context.terminal.writeln(welcomeConfig?.message);
        }
        else {
            const welcomeMessage = [
                `Welcome to Web CLI [Version ${context.writer.wrapInColor(LIBRARY_VERSION, CliForegroundColor.Green)}]`,
                '(c) 2024 Qodalis Solutions. All rights reserved.',
                CLi_Name_Art,
                '',
                context.writer.wrapInColor('Documentation: ', CliForegroundColor.Green) + 'https://cli-docs.qodalis.com/',
                '',
                "Type 'help' to get started.",
                '',
            ];
            welcomeMessage.forEach((line, index) => {
                context.terminal.write(line + '\r\n');
            });
        }
        this.recordWelcomeMessageDisplay();
        context.showPrompt();
        context.textAnimator?.showText(getGreetingBasedOnTime(), {
            speed: 60,
            removeAfterTyping: true,
        });
    }
    /**
     * Determines if the welcome message should be displayed based on the show option.
     * @param showOption - The show option from the config.
     * @returns true if the message should be displayed, false otherwise.
     */
    shouldDisplayWelcomeMessage(showOption) {
        const lastDisplayed = this.getLastWelcomeMessageDisplayTime();
        switch (showOption) {
            case 'always':
                return true;
            case 'once':
                return !lastDisplayed; // Show only if it hasn't been shown before
            case 'daily':
                if (!lastDisplayed)
                    return true; // No previous display, show it
                const now = new Date();
                const lastDate = new Date(lastDisplayed);
                return now.toDateString() !== lastDate.toDateString(); // Compare dates
            case 'never':
                return false;
            default:
                return true;
        }
    }
    /**
     * Records the current time as the last display time for the welcome message.
     */
    recordWelcomeMessageDisplay() {
        localStorage.setItem('cliWelcomeMessageLastDisplayed', new Date().toISOString());
    }
    /**
     * Retrieves the last display time of the welcome message.
     * @returns The ISO string of the last display time or null if not recorded.
     */
    getLastWelcomeMessageDisplayTime() {
        return localStorage.getItem('cliWelcomeMessageLastDisplayed');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliWelcomeMessageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliWelcomeMessageService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliWelcomeMessageService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

const themes = {
    ...DefaultThemes,
};

class CliCommandHistoryService {
    constructor(store) {
        this.store = store;
        this.storageKey = 'cli-command-history';
        this.commandHistory = [];
    }
    async addCommand(command) {
        const normalizedCommand = command.trim();
        if (normalizedCommand) {
            if (this.commandHistory.length > 0) {
                const lastCommand = this.commandHistory[this.commandHistory.length - 1];
                if (lastCommand === normalizedCommand) {
                    return;
                }
            }
            this.commandHistory.push(command);
            await this.saveHistory();
        }
    }
    getLastIndex() {
        return this.commandHistory.length;
    }
    getHistory() {
        return [...this.commandHistory];
    }
    async clearHistory() {
        this.commandHistory = [];
        await this.saveHistory();
    }
    getCommand(index) {
        return this.commandHistory[index];
    }
    async saveHistory() {
        //save only last 500 commands
        const trimmedHistory = this.commandHistory.slice(-500);
        return await this.store.set(this.storageKey, trimmedHistory);
    }
    async loadHistory() {
        const savedOldHistory = localStorage.getItem('cliCommandHistory');
        if (savedOldHistory) {
            localStorage.removeItem('cliCommandHistory');
            await this.store.set(this.storageKey, JSON.parse(savedOldHistory));
        }
        const savedHistory = await this.store.get(this.storageKey);
        if (savedHistory) {
            this.commandHistory = savedHistory;
        }
    }
    async initialize() {
        await this.loadHistory();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandHistoryService, deps: [{ token: CliKeyValueStore }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandHistoryService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandHistoryService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: CliKeyValueStore }]; } });

class CliUserSessionService {
    constructor(usersService) {
        this.usersService = usersService;
        this.userSessionSubject = new BehaviorSubject({
            user: {
                id: 'anonymous',
                name: 'Anonymous',
                email: 'anonymous',
            },
        });
        this.usersService.getUsers().subscribe((users) => {
            const user = users.find((u) => u.id === 'root');
            if (user) {
                this.setUserSession({
                    user,
                });
            }
        });
    }
    async setUserSession(session) {
        this.userSessionSubject.next(session);
    }
    getUserSession() {
        return this.userSessionSubject.asObservable();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliUserSessionService, deps: [{ token: ICliUsersStoreService_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliUserSessionService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliUserSessionService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliUsersStoreService_TOKEN]
                }] }]; } });

class CliUsersStoreService {
    constructor() {
        this.localStorageKey = 'users';
        this.defaultUsers = [
            {
                id: 'root',
                name: 'root',
                email: 'root@domain.com',
                groups: ['admin'],
            },
        ];
        this.usersSubject = new BehaviorSubject([]);
        this.initialize(this.defaultUsers);
    }
    initialize(defaultUsers) {
        const storedUsers = this.loadUsers(defaultUsers);
        this.usersSubject.next(storedUsers);
    }
    async createUser(user) {
        const users = this.usersSubject.getValue();
        if (users.some((u) => u.email === user.email || u.name === user.name)) {
            return Promise.reject('User already exists');
        }
        const newUser = {
            ...user,
            id: user.email,
        };
        users.push(newUser);
        this.saveUsers(users);
        this.usersSubject.next(users);
        return newUser;
    }
    getUsers(options) {
        const { query, skip, take } = options || {};
        return this.usersSubject.asObservable().pipe(map$1((users) => {
            if (query) {
                const queryLower = query.toLowerCase();
                users = users.filter((u) => u.name.toLowerCase().includes(queryLower) ||
                    u.email.toLowerCase().includes(queryLower));
            }
            if (skip) {
                users = users.slice(skip);
            }
            if (take) {
                users = users.slice(0, take);
            }
            return users;
        }));
    }
    getUser(id) {
        return this.usersSubject
            .asObservable()
            .pipe(map$1((users) => users.find((u) => u.id === id || u.name === id || u.email === id)));
    }
    saveUsers(users) {
        localStorage.setItem(this.localStorageKey, JSON.stringify(users));
    }
    loadUsers(fallbackUsers) {
        const data = localStorage.getItem(this.localStorageKey);
        if (!data) {
            return fallbackUsers;
        }
        try {
            return JSON.parse(data);
        }
        catch (error) {
            console.error('Failed to parse user data from localStorage', error);
            return [];
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliUsersStoreService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliUsersStoreService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliUsersStoreService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return []; } });

class CliArgsParser {
    static convertToRecord(args, processor) {
        const result = {};
        const updateParamValue = (parameter, value) => {
            result[parameter.name] = value;
            parameter.aliases?.forEach((alias) => {
                result[alias] = value;
            });
        };
        args.forEach((arg) => {
            const parameter = processor.parameters?.find((p) => p.name === arg.name ||
                p.aliases?.some((a) => a === arg.name));
            if (parameter) {
                let value = arg.value;
                switch (parameter.type) {
                    case 'array':
                        const previousValue = Array.isArray(result[arg.name])
                            ? result[arg.name]
                            : [];
                        value = [...previousValue, arg.value];
                        break;
                    case 'boolean':
                        value =
                            value === 'true' ||
                                value === '1' ||
                                value === 'yes' ||
                                value === 'y' ||
                                value === 1;
                        break;
                    default:
                        value = arg.value;
                        break;
                }
                updateParamValue(parameter, value);
            }
            else {
                result[arg.name] = arg.value;
            }
        });
        return result;
    }
}

class CliCommandExecutorService {
    constructor(registry) {
        this.registry = registry;
        this.commandParser = new CommandParser();
    }
    async executeCommand(command, context) {
        // Split commands by logical operators
        const parts = command.split(/(&&|\|\|)/).map((part) => part.trim());
        let shouldRunNextCommand = true; // Tracks whether to execute the next command
        let rootContext;
        if (context instanceof CliCommandExecutionContext) {
            rootContext = context.context;
        }
        else {
            rootContext = context;
        }
        for (let i = 0; i < parts.length; i++) {
            const current = parts[i];
            // If the current part is a logical operator, adjust the shouldRunNextCommand flag
            if (current === '&&') {
                shouldRunNextCommand = shouldRunNextCommand && true;
                continue;
            }
            else if (current === '||') {
                shouldRunNextCommand = !shouldRunNextCommand;
                continue;
            }
            // Skip execution based on previous command's result and operator
            if (!shouldRunNextCommand) {
                shouldRunNextCommand = true; // Reset for next iteration
                continue;
            }
            // Execute the command
            let commandSuccess = true;
            try {
                const data = context.process.data;
                const command = current;
                await this.executeSingleCommand(command, data, rootContext);
                commandSuccess = context.process.exitCode === 0;
            }
            catch (e) {
                commandSuccess = false;
                context.writer.writeError(`Command ${current} failed: ${e}`);
            }
            shouldRunNextCommand = commandSuccess;
        }
    }
    async executeSingleCommand(command, data, context) {
        const process = context.process;
        process.start();
        const { commandName, args: parsedArgs } = this.commandParser.parse(command);
        const [mainCommand, ...other] = commandName.split(' ');
        const chainCommands = other.map((c) => c.toLowerCase());
        const searchableProcessors = context.contextProcessor
            ? (context.contextProcessor.processors ?? [])
            : this.registry.processors;
        const processor = this.registry.findProcessorInCollection(mainCommand, chainCommands, searchableProcessors);
        if (!processor) {
            const aliases = this.registry.findProcessor('alias', []).aliases ?? {};
            if (aliases[mainCommand]) {
                const alias = aliases[mainCommand];
                return await this.executeSingleCommand(alias, data, context);
            }
            context.writer.writeError(`Command: ${commandName} not found or not installed`);
            context.writer.writeln();
            context.writer.writeInfo('Type "help" for a list of available commands.');
            context.writer.writeInfo('Use packages to install additional commands.');
            context.process.exit(-1, {
                silent: true,
            });
            return;
        }
        const args = CliArgsParser.convertToRecord(parsedArgs, processor);
        const commandToProcess = {
            command: commandName,
            chainCommands: chainCommands,
            rawCommand: command,
            args: args,
            data: data,
        };
        if (this.versionRequested(context, processor, args)) {
            process.end();
            return;
        }
        if (await this.helpRequested(commandToProcess, context)) {
            process.end();
            return;
        }
        if (this.setContextProcessorRequested(context, processor, args)) {
            process.end();
            return;
        }
        if (!this.validateBeforeExecution(context, processor, args)) {
            process.end();
            return;
        }
        const value = processor.allowUnlistedCommands || processor.valueRequired
            ? getRightOfWord(commandName, processor.command)
            : undefined;
        commandToProcess.value = value;
        const missingValue = processor.valueRequired && !value;
        if (missingValue) {
            context.writer.writeError(`Value required for command: ${commandName} <value>`);
            context.process.exit(-1);
            return;
        }
        if (processor.validateBeforeExecution) {
            const validationResult = processor.validateBeforeExecution(commandToProcess, context);
            if (validationResult.valid === false) {
                context.writer.writeError(validationResult?.message ||
                    'An error occurred while validating the command.');
                context.process.exit(-1);
                return;
            }
        }
        let cancellable = null;
        const commandContext = new CliCommandExecutionContext(context, processor);
        try {
            const hooks = processor.hooks ?? [];
            for (const hook of hooks.filter((h) => h.when === 'before')) {
                await hook.execute(commandContext);
            }
            cancellable = new CancellablePromise(async (resolve, reject) => {
                processor
                    .processCommand(commandToProcess, commandContext)
                    .then(() => {
                    resolve();
                })
                    .catch((e) => {
                    reject(e);
                });
            });
            await cancellable.execute();
            for (const hook of hooks.filter((h) => h.when === 'after')) {
                await hook.execute(commandContext);
            }
            process.end();
        }
        catch (e) {
            context.spinner?.hide();
            if (e instanceof ProcessExitedError) {
                cancellable?.cancel();
                context?.abort();
                if (e.code !== 0) {
                    context.writer.writeError(`Process exited with code ${e.code}`);
                }
                else {
                    context.writer.writeInfo('Process exited successfully with code 0');
                }
            }
            else {
                context.writer.writeError(`Error executing command: ${e}`);
                context.process.exit(-1);
            }
        }
    }
    async showHelp(command, context) {
        try {
            await this.executeCommand('help ' + command.rawCommand, context);
        }
        catch (e) {
            context.writer.writeError(`Error executing command: ${e}`);
        }
    }
    versionRequested(context, processor, args) {
        if (args['v'] || args['version']) {
            context.writer.writeln(`${context.writer.wrapInColor(processor.version || '1.0.0', CliForegroundColor.Cyan)}`);
            return true;
        }
        return false;
    }
    setContextProcessorRequested(context, processor, args) {
        if (args['context']) {
            context.setContextProcessor(processor);
            return true;
        }
        return false;
    }
    async helpRequested(commandToProcess, context) {
        if (commandToProcess.command?.startsWith('help')) {
            return false;
        }
        if (commandToProcess.args['h'] || commandToProcess.args['help']) {
            await this.showHelp(commandToProcess, context);
            return true;
        }
        return false;
    }
    /**
     * Validates the command arguments before execution.
     * @param context The current CLI execution context.
     * @param processor The processor to validate.
     * @param args The command arguments.
     * @returns True if the arguments are valid, false otherwise.
     */
    validateBeforeExecution(context, processor, args) {
        // Check for required parameters
        if (processor.parameters?.some((p) => p.required)) {
            const missingParams = processor.parameters?.filter((p) => p.required &&
                !args[p.name] &&
                !p.aliases?.some((a) => args[a]));
            if (missingParams?.length) {
                context.writer.writeError(`Missing required parameters: ${missingParams
                    .map((p) => p.name)
                    .join(', ')}`);
                return false;
            }
        }
        // Check for parameter validation
        const parametersToValidate = processor.parameters
            ?.filter((x) => x.validator)
            ?.map((p) => ({
            parameter: p,
            value: getParameterValue(p, args),
        }))
            ?.filter((p) => p.value) ?? [];
        if (parametersToValidate.length > 0) {
            const invalidParams = parametersToValidate
                .filter((x) => !x.parameter.validator(x.value).valid)
                .map((p) => ({
                name: p.parameter.name,
                message: p.parameter.validator(args[p.parameter.name])
                    .message,
            }));
            if (invalidParams?.length) {
                context.writer.writeError('Invalid parameters:');
                invalidParams.forEach((p, index) => {
                    context.writer.writeln(`${index + 1}. Invalid value for ${p.name}: ${args[p.name]} -> ${p.message}`);
                });
                return false;
            }
        }
        return true;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandExecutorService, deps: [{ token: CliProcessorsRegistry_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandExecutorService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandExecutorService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [CliProcessorsRegistry_TOKEN]
                }] }]; } });

class CliDefaultPingServerService {
    ping() {
        // Simulate a server ping
        return new Promise((resolve) => setTimeout(resolve, 2000));
    }
}

class ScriptLoaderService {
    constructor() { }
    injectScript(src) {
        return new Promise((resolve, reject) => {
            const script = document.createElement('script');
            script.src = src;
            script.async = true;
            script.onload = () => resolve();
            script.onerror = () => reject(new Error(`Failed to load script: ${src}`));
            document.head.appendChild(script);
        });
    }
    getScript(src, options) {
        const { onProgress } = options || {};
        let fetchProgress = 0;
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open('GET', src, true);
            xhr.onprogress = (event) => {
                if (event.lengthComputable) {
                    const progress = Math.round((event.loaded / event.total) * 100);
                    fetchProgress = progress;
                    onProgress?.(progress);
                }
                else {
                    fetchProgress += 20;
                    onProgress?.(fetchProgress);
                }
            };
            xhr.onload = () => {
                if (xhr.status === 200) {
                    onProgress?.(100);
                    fetchProgress = 100;
                    resolve({
                        xhr,
                        content: xhr.responseText,
                    });
                }
                else {
                    reject(new Error(`Failed to load package: ${src}, Status: ${xhr.status}, Message: ${xhr.responseText}`));
                }
            };
            xhr.onerror = () => {
                reject(new Error(`Failed to load package: ${src}`));
            };
            xhr.send();
        });
    }
    injectBodyScript(code) {
        const script = document.createElement('script');
        script.text = code;
        document.head.appendChild(script);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ScriptLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ScriptLoaderService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: ScriptLoaderService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return []; } });

class OverlayAddon {
    constructor() {
        this.overlayNode = document.createElement('div');
        this.overlayNode.style.cssText = `border-radius: 15px;
font-size: xx-large;
opacity: 0.75;
padding: 0.2em 0.5em 0.2em 0.5em;
position: absolute;
-webkit-user-select: none;
-webkit-transition: opacity 180ms ease-in;
-moz-user-select: none;
-moz-transition: opacity 180ms ease-in;`;
        this.overlayNode.addEventListener('mousedown', (e) => {
            e.preventDefault();
            e.stopPropagation();
        }, true);
    }
    activate(terminal) {
        this.terminal = terminal;
    }
    dispose() { }
    showOverlay(msg, timeout) {
        const { terminal, overlayNode } = this;
        if (!terminal.element)
            return;
        overlayNode.style.color = '#101010';
        overlayNode.style.backgroundColor = '#f0f0f0';
        overlayNode.textContent = msg;
        overlayNode.style.opacity = '0.75';
        if (!overlayNode.parentNode) {
            terminal.element.appendChild(overlayNode);
        }
        const divSize = terminal.element.getBoundingClientRect();
        const overlaySize = overlayNode.getBoundingClientRect();
        overlayNode.style.top =
            (divSize.height - overlaySize.height) / 2 + 'px';
        overlayNode.style.left = (divSize.width - overlaySize.width) / 2 + 'px';
        if (this.overlayTimeout)
            clearTimeout(this.overlayTimeout);
        if (!timeout)
            return;
        this.overlayTimeout = window.setTimeout(() => {
            overlayNode.style.opacity = '0';
            this.overlayTimeout = window.setTimeout(() => {
                if (overlayNode.parentNode) {
                    overlayNode.parentNode.removeChild(overlayNode);
                }
                this.overlayTimeout = undefined;
                overlayNode.style.opacity = '0.75';
            }, 200);
        }, timeout || 1500);
    }
}

class CliTerminalComponent {
    constructor() {
        this.onTerminalReady = new EventEmitter();
    }
    ngOnInit() {
        this.initializeTerminal();
    }
    ngAfterViewInit() {
        this.handleResize();
    }
    initializeTerminal() {
        if (!this.options) {
            this.options = {
                allowProposedApi: true,
            };
        }
        this.terminal = new Terminal(this.options);
        this.fitAddon = new FitAddon();
        this.terminal.loadAddon(this.fitAddon);
        const webLinksAddon = new WebLinksAddon();
        this.terminal.loadAddon(webLinksAddon);
        const overlayAddon = new OverlayAddon();
        this.terminal.loadAddon(overlayAddon);
        // const webGlAddon = new WebglAddon();
        // webGlAddon.onContextLoss((e) => {
        //     webGlAddon.dispose();
        // });
        // this.terminal.loadAddon(webGlAddon);
        const unicode11Addon = new Unicode11Addon();
        this.terminal.loadAddon(unicode11Addon);
        this.terminal.open(this.terminalDiv.nativeElement);
        this.fitAddon.fit();
        this.terminal.focus();
        this.onTerminalReady.emit(this.terminal);
    }
    handleResize() {
        window.addEventListener('resize', () => {
            this.fitAddon.fit();
        });
        this.observeContainerSize();
    }
    observeContainerSize() {
        this.resizeObserver = new ResizeObserver(() => {
            this.fitAddon.fit();
        });
        this.resizeObserver.observe(this.terminalDiv.nativeElement);
    }
    ngOnDestroy() {
        if (this.resizeObserver) {
            this.resizeObserver.disconnect();
        }
        this.terminal?.dispose();
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliTerminalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: CliTerminalComponent, selector: "cli-terminal", inputs: { options: "options", height: "height" }, outputs: { onTerminalReady: "onTerminalReady" }, viewQueries: [{ propertyName: "terminalDiv", first: true, predicate: ["terminal"], descendants: true, static: true }], ngImport: i0, template: "<div #terminal class=\"terminal-container\" [style.height]=\"height\"></div>\n", styles: [".terminal-container{background-color:#0c0c0c;padding:4px}\n"] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliTerminalComponent, decorators: [{
            type: Component,
            args: [{ selector: 'cli-terminal', template: "<div #terminal class=\"terminal-container\" [style.height]=\"height\"></div>\n", styles: [".terminal-container{background-color:#0c0c0c;padding:4px}\n"] }]
        }], ctorParameters: function () { return []; }, propDecorators: { options: [{
                type: Input
            }], height: [{
                type: Input
            }], onTerminalReady: [{
                type: Output
            }], terminalDiv: [{
                type: ViewChild,
                args: ['terminal', { static: true }]
            }] } });

class CliComponent {
    constructor(injector, userManagementService, commandExecutor, commandHistoryService, store) {
        this.injector = injector;
        this.userManagementService = userManagementService;
        this.commandExecutor = commandExecutor;
        this.commandHistoryService = commandHistoryService;
        this.store = store;
        this.currentLine = '';
        this.minDepsInitialized = new BehaviorSubject(false);
        this.terminalInitialized = new BehaviorSubject(false);
        this.selectionStart = null;
        this.selectionEnd = null;
        this.historyIndex = 0;
        this.cursorPosition = 0;
        this.userManagementService.getUserSession().subscribe((session) => {
            this.currentUserSession = session;
            this.executionContext?.setSession(session);
            if (this.terminal) {
                this.printPrompt({
                    reset: true,
                });
            }
        });
    }
    ngOnInit() {
        combineLatest([this.minDepsInitialized, this.terminalInitialized])
            .pipe(filter(([x, y]) => x && y))
            .subscribe(() => {
            this.initialize();
        });
        this.terminalOptions = {
            cursorBlink: true,
            allowProposedApi: true,
            fontSize: 20,
            theme: themes.default,
            convertEol: true,
            ...(this.options?.terminalOptions ?? {}),
        };
        this.store.initialize().then(() => {
            this.minDepsInitialized.next(true);
        });
    }
    onTerminalReady(terminal) {
        this.terminal = terminal;
        this.terminalInitialized.next(true);
    }
    initialize() {
        this.commandHistoryService.initialize().then(() => {
            this.historyIndex = this.commandHistoryService.getLastIndex();
        });
        this.addTerminalEventListeners();
        this.executionContext = new CliExecutionContext(this.injector, this.terminal, this.commandExecutor, (o) => this.printPrompt(o), {
            ...(this.options ?? {}),
            terminalOptions: this.terminalOptions,
        });
        this.executionContext.setSession(this.currentUserSession);
        this.injector
            .get(CliBoot)
            .boot(this.executionContext)
            .then(() => {
            this.injector
                .get(CliWelcomeMessageService)
                .displayWelcomeMessage(this.executionContext);
        });
    }
    getTerminalCursorPosition() {
        const x = this.terminal._core.buffer.x;
        const y = this.terminal._core.buffer.y;
        return {
            x,
            y,
        };
    }
    addTerminalEventListeners() {
        // Handle user input
        this.terminal.onData(async (data) => await this.handleInput(data));
        this.terminal.onKey(async (event) => { });
        this.terminal.attachCustomKeyEventHandler((event) => {
            if (event.type === 'keydown') {
                if (event.code === 'KeyC' && event.ctrlKey) {
                    // Handle Ctrl+C
                    this.executionContext?.abort();
                    this.executionContext?.setContextProcessor(undefined);
                    this.terminal.writeln('Ctrl+C');
                    this.printPrompt();
                    return false;
                }
                if (event.code === 'Escape') {
                    // Handle Escape
                    this.executionContext?.abort();
                    this.printPrompt({
                        newLine: true,
                    });
                    return false;
                }
                if (event.code === 'KeyV' && event.ctrlKey) {
                    //Handle Ctrl+V
                    return false;
                }
                if (event.code === 'KeyL' && event.ctrlKey) {
                    // Prevent the browser's default action (e.g., focusing the address bar)
                    event.preventDefault();
                    this.clearCurrentLine();
                    // Clear the terminal screen
                    this.terminal.clear();
                    return false;
                }
                if (event.shiftKey &&
                    (event.code === 'ArrowLeft' || event.code === 'ArrowRight')) {
                    if (!this.selectionStart) {
                        this.selectionStart = this.getTerminalCursorPosition();
                    }
                    switch (event.code) {
                        case 'ArrowLeft':
                            this.moveCursorLeft();
                            break;
                        case 'ArrowRight':
                            this.moveCursorRight();
                            break;
                    }
                    this.selectionEnd = this.getTerminalCursorPosition();
                    this.updateSelection();
                    return false;
                }
                else {
                    this.selectionStart = null;
                }
            }
            return true;
        });
    }
    updateSelection() {
        if (this.selectionStart && this.selectionEnd) {
            const startRow = Math.min(this.selectionStart.y, this.selectionEnd.y);
            const endRow = Math.max(this.selectionStart.y, this.selectionEnd.y);
            if (startRow === endRow) {
                const startCol = Math.min(this.selectionStart.x, this.selectionEnd.x);
                const endCol = Math.max(this.selectionStart.x, this.selectionEnd.x);
                // Select text on the same line
                this.terminal.select(startCol, startRow, Math.abs(endCol - startCol));
            }
            else {
                // Select multiple lines
                this.terminal.selectLines(startRow, endRow);
            }
        }
    }
    printPrompt(options) {
        const { reset, newLine, keepCurrentLine } = options || {};
        if (reset) {
            this.terminal.write('\x1b[2K\r');
        }
        if (newLine) {
            this.terminal.write('\r\n');
        }
        if (!keepCurrentLine) {
            this.currentLine = '';
            this.cursorPosition = 0;
        }
        let promtStartMessage = this.options?.usersModule?.hideUserName ||
            !this.options?.usersModule?.enabled
            ? ''
            : `\x1b[32m${this.currentUserSession?.user.email}\x1b[0m:`;
        if (this.executionContext?.contextProcessor) {
            promtStartMessage = `${this.executionContext.contextProcessor.command}`;
        }
        const promtEndMessage = '\x1b[34m~\x1b[0m$ ';
        const prompt = `${promtStartMessage}${promtEndMessage}`;
        this.terminal.write(prompt);
    }
    async handleInput(data) {
        if (this.executionContext?.isProgressRunning()) {
            return;
        }
        if (data === '\r') {
            // Enter key: Process the current command
            this.terminal.write('\r\n'); // Move to the next line
            if (this.currentLine) {
                await this.commandHistoryService.addCommand(this.currentLine);
                this.historyIndex = this.commandHistoryService.getLastIndex();
                //reset cursor position
                this.cursorPosition = 0;
                await this.commandExecutor.executeCommand(this.currentLine, this.executionContext);
                //check if the command has subscribed to the onAbort event
                if (this.executionContext?.onAbort.observed) {
                    this.terminal.writeln('\x1b[33m' + 'Press Ctrl+C to cancel' + '\x1b[0m');
                }
            }
            this.printPrompt();
        }
        else if (data === '\u001B[A') {
            // Arrow Up
            this.showPreviousCommand();
        }
        else if (data === '\u001B[B') {
            // Arrow Down
            this.showNextCommand();
        }
        else if (data === '\u001B[D') {
            // Left Arrow
            this.moveCursorLeft(data);
        }
        else if (data === '\u001B[C') {
            // Right Arrow
            this.moveCursorRight(data);
        }
        else if (data === '\u007F') {
            // Backspace key
            this.handleBackspace();
        }
        else {
            // Append character at cursor position
            this.handleInputText(data);
        }
    }
    normalizeText(text) {
        //handle tab
        if (text === '\u0009') {
            return '    ';
        }
        return text.replace(/[\r\n]+/g, '');
    }
    handleInputText(text) {
        text = this.normalizeText(text);
        this.currentLine =
            this.currentLine.slice(0, this.cursorPosition) +
                text +
                this.currentLine.slice(this.cursorPosition);
        this.cursorPosition += text.length;
        this.refreshCurrentLine();
    }
    refreshCurrentLine() {
        this.terminal.write('\x1b[2K'); // Clear the current line
        this.terminal.write('\r'); // Move the cursor to the start
        this.printPrompt({
            keepCurrentLine: true,
        });
        // Redraw the prompt
        this.writeCurrentLine();
        // Move cursor to the correct position
        const cursorOffset = this.currentLine.length - this.cursorPosition;
        if (cursorOffset > 0) {
            this.terminal.write(`\x1b[${cursorOffset}D`);
        }
    }
    writeCurrentLine() {
        this.terminal.write(colorFirstWord(this.currentLine, (word) => this.executionContext?.writer.wrapInColor(word, CliForegroundColor.Yellow) ?? this.currentLine));
    }
    showPreviousCommand() {
        if (this.historyIndex > 0) {
            this.historyIndex--;
            this.displayCommandFromHistory();
        }
    }
    showNextCommand() {
        if (this.historyIndex < this.commandHistoryService.getLastIndex() - 1) {
            this.historyIndex++;
            this.displayCommandFromHistory();
        }
        else {
            this.historyIndex = this.commandHistoryService.getLastIndex();
            this.clearCurrentLine();
        }
    }
    displayCommandFromHistory() {
        this.clearCurrentLine();
        this.currentLine =
            this.commandHistoryService.getCommand(this.historyIndex) || '';
        this.writeCurrentLine();
        this.cursorPosition = this.currentLine.length;
    }
    clearCurrentLine() {
        const wrappedLines = Math.ceil(this.currentLine.length / this.terminal.cols);
        for (let i = 0; i < wrappedLines; i++) {
            this.terminal.write('\x1b[2K'); // Clear the current line
            this.terminal.write('\r'); // Move the cursor to the start of the line
            if (i < wrappedLines - 1) {
                this.terminal.write('\x1b[F'); // Move the cursor up for all but the last line
            }
            if (i === wrappedLines - 1) {
                this.terminal.write('\r');
                this.printPrompt();
            }
        }
        this.currentLine = '';
        this.cursorPosition = 0;
    }
    moveCursorLeft(key = '\x1b[D') {
        if (this.cursorPosition > 0) {
            this.cursorPosition--;
            this.terminal.write(key);
        }
    }
    moveCursorRight(key = '\x1b[C') {
        if (this.cursorPosition < this.currentLine.length) {
            this.cursorPosition++;
            this.terminal.write(key);
        }
    }
    handleBackspace() {
        if (this.cursorPosition > 0) {
            this.currentLine =
                this.currentLine.slice(0, this.cursorPosition - 1) +
                    this.currentLine.slice(this.cursorPosition);
            this.cursorPosition--;
            this.refreshCurrentLine();
        }
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliComponent, deps: [{ token: i0.Injector }, { token: ICliUserSessionService_TOKEN }, { token: CliCommandExecutorService }, { token: CliCommandHistoryService }, { token: CliKeyValueStore }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: CliComponent, selector: "cli", inputs: { options: "options", height: "height" }, ngImport: i0, template: "<cli-terminal\n  [options]=\"terminalOptions\"\n  [height]=\"height\"\n  (onTerminalReady)=\"onTerminalReady($event)\"\n/>\n", styles: [""], dependencies: [{ kind: "component", type: CliTerminalComponent, selector: "cli-terminal", inputs: ["options", "height"], outputs: ["onTerminalReady"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliComponent, decorators: [{
            type: Component,
            args: [{ selector: 'cli', encapsulation: ViewEncapsulation.None, template: "<cli-terminal\n  [options]=\"terminalOptions\"\n  [height]=\"height\"\n  (onTerminalReady)=\"onTerminalReady($event)\"\n/>\n" }]
        }], ctorParameters: function () { return [{ type: i0.Injector }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliUserSessionService_TOKEN]
                }] }, { type: CliCommandExecutorService }, { type: CliCommandHistoryService }, { type: CliKeyValueStore }]; }, propDecorators: { options: [{
                type: Input
            }], height: [{
                type: Input
            }] } });

class CollapsableContentComponent {
    constructor(el) {
        this.el = el;
        this.previousPanelHeight = 500;
        this.panelHeight = 500;
        this.isResizing = false;
        this.startY = 0;
        this.startHeight = 0;
        this.visible = true;
        this.isCollapsed = true;
        this.isMaximized = false;
        this.onToggle = new EventEmitter();
        this.onContentSizeChange = new EventEmitter();
    }
    ngOnInit() {
        this.items = [
            {
                label: 'Tab 1',
                icon: 'pi pi-plus',
            },
        ];
    }
    onActiveItemChange(event) {
        this.activeItem = event;
    }
    toggleTerminal() {
        this.isCollapsed = !this.isCollapsed;
        this.onToggle.emit(this.isCollapsed);
    }
    closeTerminal() {
        this.visible = false;
    }
    toggleMaximizationTerminal() {
        //check next
        if (!this.isMaximized) {
            this.previousPanelHeight = this.panelHeight;
            const windowHeight = window.innerHeight;
            this.panelHeight = windowHeight;
        }
        else {
            this.panelHeight = this.previousPanelHeight;
        }
        this.isMaximized = !this.isMaximized;
        this.updateTerminalSize();
    }
    onResizeStart(event) {
        this.isResizing = true;
        if (this.isCollapsed) {
            this.toggleTerminal();
        }
        this.startY = event.clientY;
        this.startHeight = this.panelHeight;
        event.preventDefault();
    }
    onMouseMove(event) {
        if (this.isResizing) {
            const deltaY = this.startY - event.clientY;
            let nextHeight = Math.max(100, this.startHeight + deltaY);
            if (nextHeight > window.innerHeight) {
                nextHeight = window.innerHeight;
            }
            this.panelHeight = nextHeight;
            this.updateTerminalSize();
        }
    }
    onMouseUp() {
        this.isResizing = false;
    }
    updateTerminalSize() {
        this.onContentSizeChange.emit(this.panelHeight - 60 - 8);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CollapsableContentComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: CollapsableContentComponent, selector: "collapsable-content", inputs: { visible: "visible", isCollapsed: "isCollapsed", isMaximized: "isMaximized" }, outputs: { onToggle: "onToggle", onContentSizeChange: "onContentSizeChange" }, host: { listeners: { "document:mousemove": "onMouseMove($event)", "document:mouseup": "onMouseUp()" } }, ngImport: i0, template: "<div\n  *ngIf=\"visible\"\n  class=\"terminal-wrapper\"\n  [class.collapsed]=\"isCollapsed\"\n  [style]=\"{\n    height: panelHeight + 'px',\n  }\"\n>\n  <div class=\"card terminal-header\">\n    <div class=\"divider\">\n      <i class=\"pi pi-minus\" (mousedown)=\"onResizeStart($event)\"></i>\n    </div>\n    <div class=\"header-content\">\n      <p class=\"terminal-title\">\n        <i class=\"pi pi-desktop\" style=\"font-size: 2rem\"></i>\n        CLI\n      </p>\n      <div class=\"action-buttons\">\n        <p-button\n          [icon]=\"\n            isMaximized ? 'pi pi-window-minimize' : 'pi pi-window-maximize'\n          \"\n          class=\"p-button-rounded p-button-text p-button-secondary\"\n          [pTooltip]=\"!isMaximized ? 'Maximize' : 'Minimize'\"\n          tooltipPosition=\"top\"\n          [disabled]=\"isCollapsed\"\n          (onClick)=\"toggleMaximizationTerminal()\"\n        />\n\n        <p-button\n          [icon]=\"isCollapsed ? 'pi pi-chevron-up' : 'pi pi-chevron-down'\"\n          class=\"p-button-rounded p-button-text p-button-secondary\"\n          [pTooltip]=\"isCollapsed ? 'Expand' : 'Collapse'\"\n          tooltipPosition=\"top\"\n          (onClick)=\"toggleTerminal()\"\n        />\n\n        <p-button\n          [icon]=\"'pi pi-eye-slash'\"\n          class=\"p-button-rounded p-button-text p-button-secondary\"\n          [pTooltip]=\"'Close'\"\n          tooltipPosition=\"top\"\n          (onClick)=\"closeTerminal()\"\n        />\n      </div>\n    </div>\n  </div>\n  <div class=\"terminal-content\" *ngIf=\"!isCollapsed\">\n    <ng-content></ng-content>\n  </div>\n</div>\n", styles: [".terminal-wrapper{position:fixed;bottom:0;left:0;right:0;width:100vw;transition:transform .3s ease-in-out;transform:translateY(0);background-color:#0c0c0c;color:#fff;border-top:1px solid #444;display:flex;flex-direction:column;z-index:1000}.terminal-wrapper.collapsed{transform:translateY(calc(100% - 60px))}.terminal-header{background-color:var(--surface-a);display:flex;flex-direction:column}.terminal-header .divider{display:flex;justify-content:center;position:absolute;width:100%;top:-10px}.terminal-header .divider i{font-size:2rem;cursor:ns-resize}.terminal-header .header-content{height:60px;display:flex;flex-direction:row;gap:1rem;align-items:center;border-bottom:1px solid #444;padding:.5rem 1rem}.terminal-header .header-content .terminal-title{font-size:1rem;display:flex;gap:.5rem;justify-content:center;align-items:center}.terminal-header .header-content .action-buttons{display:flex;flex-direction:row;gap:1rem;margin-left:auto}.terminal-content{flex:1;width:calc(100% - 8px)}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "size", "style", "styleClass", "badgeClass", "ariaLabel"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i3.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CollapsableContentComponent, decorators: [{
            type: Component,
            args: [{ selector: 'collapsable-content', template: "<div\n  *ngIf=\"visible\"\n  class=\"terminal-wrapper\"\n  [class.collapsed]=\"isCollapsed\"\n  [style]=\"{\n    height: panelHeight + 'px',\n  }\"\n>\n  <div class=\"card terminal-header\">\n    <div class=\"divider\">\n      <i class=\"pi pi-minus\" (mousedown)=\"onResizeStart($event)\"></i>\n    </div>\n    <div class=\"header-content\">\n      <p class=\"terminal-title\">\n        <i class=\"pi pi-desktop\" style=\"font-size: 2rem\"></i>\n        CLI\n      </p>\n      <div class=\"action-buttons\">\n        <p-button\n          [icon]=\"\n            isMaximized ? 'pi pi-window-minimize' : 'pi pi-window-maximize'\n          \"\n          class=\"p-button-rounded p-button-text p-button-secondary\"\n          [pTooltip]=\"!isMaximized ? 'Maximize' : 'Minimize'\"\n          tooltipPosition=\"top\"\n          [disabled]=\"isCollapsed\"\n          (onClick)=\"toggleMaximizationTerminal()\"\n        />\n\n        <p-button\n          [icon]=\"isCollapsed ? 'pi pi-chevron-up' : 'pi pi-chevron-down'\"\n          class=\"p-button-rounded p-button-text p-button-secondary\"\n          [pTooltip]=\"isCollapsed ? 'Expand' : 'Collapse'\"\n          tooltipPosition=\"top\"\n          (onClick)=\"toggleTerminal()\"\n        />\n\n        <p-button\n          [icon]=\"'pi pi-eye-slash'\"\n          class=\"p-button-rounded p-button-text p-button-secondary\"\n          [pTooltip]=\"'Close'\"\n          tooltipPosition=\"top\"\n          (onClick)=\"closeTerminal()\"\n        />\n      </div>\n    </div>\n  </div>\n  <div class=\"terminal-content\" *ngIf=\"!isCollapsed\">\n    <ng-content></ng-content>\n  </div>\n</div>\n", styles: [".terminal-wrapper{position:fixed;bottom:0;left:0;right:0;width:100vw;transition:transform .3s ease-in-out;transform:translateY(0);background-color:#0c0c0c;color:#fff;border-top:1px solid #444;display:flex;flex-direction:column;z-index:1000}.terminal-wrapper.collapsed{transform:translateY(calc(100% - 60px))}.terminal-header{background-color:var(--surface-a);display:flex;flex-direction:column}.terminal-header .divider{display:flex;justify-content:center;position:absolute;width:100%;top:-10px}.terminal-header .divider i{font-size:2rem;cursor:ns-resize}.terminal-header .header-content{height:60px;display:flex;flex-direction:row;gap:1rem;align-items:center;border-bottom:1px solid #444;padding:.5rem 1rem}.terminal-header .header-content .terminal-title{font-size:1rem;display:flex;gap:.5rem;justify-content:center;align-items:center}.terminal-header .header-content .action-buttons{display:flex;flex-direction:row;gap:1rem;margin-left:auto}.terminal-content{flex:1;width:calc(100% - 8px)}\n"] }]
        }], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { visible: [{
                type: Input
            }], isCollapsed: [{
                type: Input
            }], isMaximized: [{
                type: Input
            }], onToggle: [{
                type: Output
            }], onContentSizeChange: [{
                type: Output
            }], onMouseMove: [{
                type: HostListener,
                args: ['document:mousemove', ['$event']]
            }], onMouseUp: [{
                type: HostListener,
                args: ['document:mouseup']
            }] } });

class CliCanViewService {
    constructor() { }
    canView() {
        return of(true);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCanViewService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCanViewService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCanViewService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return []; } });

/**
 * A component that displays the CLI on the bottom of page.
 */
class CliPanelComponent {
    constructor(canView) {
        this.canView = canView;
        this.visible = false;
        this.terminalHeight = `${450 - 8}px`;
        this.initialized = false;
        this.subscriptions = new Subscription();
        this.subscriptions.add(this.canView.canView().subscribe((canView) => {
            this.visible = canView;
        }));
    }
    onToggle($event) {
        if (!$event && !this.initialized) {
            this.initialized = true;
        }
    }
    onContentSizeChange($event) {
        this.terminalHeight = `${$event}px`;
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPanelComponent, deps: [{ token: CliCanViewService }], target: i0.ɵɵFactoryTarget.Component }); }
    static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: CliPanelComponent, selector: "cli-panel", inputs: { options: "options" }, ngImport: i0, template: "<collapsable-content\n  [isCollapsed]=\"options?.isCollapsed ?? true\"\n  (onToggle)=\"onToggle($event)\"\n  (onContentSizeChange)=\"onContentSizeChange($event)\"\n  *ngIf=\"visible\"\n>\n  <ng-container *ngIf=\"initialized\">\n    <cli [options]=\"options\" [height]=\"terminalHeight\" />\n  </ng-container>\n</collapsable-content>\n", styles: [".terminal-tabs{background-color:#1e1e1e;color:#fff;display:flex;align-items:center;padding:10px}.terminal-tabs .tab-list{list-style:none;margin:0;padding:0;display:flex}.terminal-tabs .tab-list li{padding:8px 16px;margin-right:5px;cursor:pointer;border:1px solid #444;border-radius:4px;background-color:#333;display:flex;align-items:center}.terminal-tabs .tab-list li.active{background-color:#007ad9;border-color:#005bb5}.terminal-tabs .tab-list li .close-btn{margin-left:10px;background:none;border:none;color:#fff;cursor:pointer;font-size:.8rem}.terminal-tabs .tab-list .add-tab{padding:8px 16px;margin-left:5px;cursor:pointer;background-color:#444;border:1px dashed #666;border-radius:4px;color:#aaa}.terminal-tabs .tab-list .add-tab:hover{background-color:#555}.terminal-content{background-color:#1e1e1e;color:#fff;padding:15px;border-top:1px solid #444}.terminal-instance{display:flex;flex-direction:column;height:400px}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: CliComponent, selector: "cli", inputs: ["options", "height"] }, { kind: "component", type: CollapsableContentComponent, selector: "collapsable-content", inputs: ["visible", "isCollapsed", "isMaximized"], outputs: ["onToggle", "onContentSizeChange"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPanelComponent, decorators: [{
            type: Component,
            args: [{ selector: 'cli-panel', template: "<collapsable-content\n  [isCollapsed]=\"options?.isCollapsed ?? true\"\n  (onToggle)=\"onToggle($event)\"\n  (onContentSizeChange)=\"onContentSizeChange($event)\"\n  *ngIf=\"visible\"\n>\n  <ng-container *ngIf=\"initialized\">\n    <cli [options]=\"options\" [height]=\"terminalHeight\" />\n  </ng-container>\n</collapsable-content>\n", styles: [".terminal-tabs{background-color:#1e1e1e;color:#fff;display:flex;align-items:center;padding:10px}.terminal-tabs .tab-list{list-style:none;margin:0;padding:0;display:flex}.terminal-tabs .tab-list li{padding:8px 16px;margin-right:5px;cursor:pointer;border:1px solid #444;border-radius:4px;background-color:#333;display:flex;align-items:center}.terminal-tabs .tab-list li.active{background-color:#007ad9;border-color:#005bb5}.terminal-tabs .tab-list li .close-btn{margin-left:10px;background:none;border:none;color:#fff;cursor:pointer;font-size:.8rem}.terminal-tabs .tab-list .add-tab{padding:8px 16px;margin-left:5px;cursor:pointer;background-color:#444;border:1px dashed #666;border-radius:4px;color:#aaa}.terminal-tabs .tab-list .add-tab:hover{background-color:#555}.terminal-content{background-color:#1e1e1e;color:#fff;padding:15px;border-top:1px solid #444}.terminal-instance{display:flex;flex-direction:column;height:400px}\n"] }]
        }], ctorParameters: function () { return [{ type: CliCanViewService }]; }, propDecorators: { options: [{
                type: Input
            }] } });

class CliPingCommandProcessor {
    constructor(pingServerService) {
        this.pingServerService = pingServerService;
        this.command = 'ping';
        this.description = 'Pings the server';
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            icon: '🏓',
        };
    }
    async processCommand(_, context) {
        context.spinner?.show();
        context?.spinner?.setText('Pinging server...');
        this.pingServerService
            .ping()
            .then(() => {
            context.spinner?.hide();
            context.writer.writeln('pong');
            context.showPrompt();
        })
            .catch(() => {
            context.spinner?.hide();
            context.writer.writeError('Failed to ping the server');
            context.showPrompt();
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPingCommandProcessor, deps: [{ token: ICliPingServerService_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPingCommandProcessor }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPingCommandProcessor, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliPingServerService_TOKEN]
                }] }]; } });

class CliEvalCommandProcessor {
    constructor() {
        this.command = 'eval';
        this.description = 'Evaluate a JavaScript expression';
        this.author = DefaultLibraryAuthor;
        this.allowUnlistedCommands = true;
        this.metadata = {
            icon: '🧮',
            module: 'misc',
        };
    }
    async processCommand(command, context) {
        try {
            const output = eval(command.value ?? '');
            if (Array.isArray(output)) {
                context.writer.writeln('Output:');
                context.writer.writeJson(output);
                return;
            }
            if (typeof output === 'object') {
                context.writer.writeln('Output:');
                context.writer.writeJson(output);
                return;
            }
            context.writer.writeln('Output: ' + output?.toString());
        }
        catch (e) {
            context.writer.writeError(e.toString());
        }
    }
    writeDescription(context) {
        context.writer.writeln(this.description);
        context.writer.writeln('Examples:');
        context.writer.writeln('  eval 1 + 1');
        context.writer.writeln('  eval "Hello, " + "World!"');
    }
}

const githubUrl = 'https://github.com/qodalis-solutions/angular-web-cli';
class CliFeedbackCommandProcessor {
    constructor() {
        this.command = 'feedback';
        this.description = 'Allows users to report bugs or request features';
        this.processors = [];
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            icon: CliIcon.Bug,
            module: 'system',
        };
        this.processors?.push({
            command: 'report-bug',
            description: 'Reports a bug on GitHub',
            allowUnlistedCommands: true,
            async processCommand({ value }, context) {
                openLink(`${githubUrl}/issues/new?assignees=&labels=bug&projects=&template=bug_report.md&title=${value}`);
            },
            writeDescription({ writer }) {
                writer.writeln('Reports a bug on GitHub. Usage: feedback report-bug <description>');
            },
        }, {
            command: 'request-feature',
            description: 'Requests a new feature on GitHub',
            allowUnlistedCommands: true,
            async processCommand({ value }, context) {
                openLink(`${githubUrl}/issues/new?assignees=&labels=feature-request&projects=&template=feature_request.md&title=${value}`);
            },
            writeDescription({ writer }) {
                writer.writeln('Requests a new feature on GitHub. Usage: feedback request-feature <description>');
            },
        }, {
            command: 'request-command',
            description: 'Requests a new command on GitHub',
            allowUnlistedCommands: true,
            async processCommand({ value }, context) {
                openLink(`${githubUrl}/issues/new?assignees=&labels=command-request&projects=&template=command-request.md&title=${value}`);
            },
            writeDescription({ writer }) {
                writer.writeln('Requests a new command on GitHub. Usage: feedback request-command <description>');
            },
        });
    }
    async processCommand(_, { writer }) {
        writer.writeln('Use one of the following subcommands:');
        this.processors?.forEach((processor) => {
            writer.writeln(`- ${writer.wrapInColor(`feedback ${processor.command}`, CliForegroundColor.Cyan)}: ${processor.description}`);
        });
    }
    writeDescription({ writer }) {
        writer.writeln('Allows users to report bugs or request features on GitHub.');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliFeedbackCommandProcessor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliFeedbackCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliFeedbackCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return []; } });

const groupBy = (list, keySelector) => {
    return list.reduce((map, item) => {
        const key = keySelector(item);
        if (!map.has(key)) {
            map.set(key, []);
        }
        map.get(key).push(item);
        return map;
    }, new Map());
};

class CliHelpCommandProcessor {
    constructor(injector) {
        this.injector = injector;
        this.command = 'help';
        this.description = 'Displays help for a command';
        this.allowUnlistedCommands = true;
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            icon: CliIcon.Help,
            module: 'system',
        };
        this.registry = this.injector.get(CliProcessorsRegistry_TOKEN);
    }
    async processCommand(command, context) {
        const { writer } = context;
        const [_, ...commandsToHelp] = command.command.split(' ');
        if (commandsToHelp.length === 0) {
            await context.executor.executeCommand('version', context);
            this.writeSeparator(context);
            writer.writeln(writer.wrapInColor('Available commands:', CliForegroundColor.Yellow));
            const groupedCommands = groupBy(this.registry.processors, (x) => x.metadata?.module || 'uncategorized');
            groupedCommands.forEach((processors, module) => {
                writer.writeln(writer.wrapInColor(module, CliForegroundColor.Yellow));
                processors.forEach((processor) => {
                    writer.writeln(`- ${processor?.metadata?.icon ? processor.metadata.icon : CliIcon.Extension}  ${writer.wrapInColor(processor.command, CliForegroundColor.Cyan)} - ${processor?.description || 'Missing description'}`);
                });
                this.writeSeparator(context);
            });
            context.writer.writeln();
            await context.executor.executeCommand('hotkeys', context);
            this.writeSeparator(context);
            writer.writeln('\nType `help <command>` to get more information about a specific command');
        }
        else {
            const processor = this.registry.findProcessor(commandsToHelp[0], commandsToHelp.slice(1));
            if (processor) {
                this.writeProcessorDescription(processor, context);
            }
            else {
                writer.writeln(`\x1b[33mUnknown command: ${commandsToHelp[0]}`);
            }
        }
    }
    writeDescription({ writer }) {
        writer.writeln('Displays help for a command');
        writer.writeln('If no command is specified, it will display a list of available commands');
        writer.writeln('If a command is specified, it will display information about that command');
        writer.writeln('If a command is specified with a subcommand, it will display information about that subcommand');
    }
    writeProcessorDescription(processor, context) {
        const { writer } = context;
        writer.write('\x1b[33mCommand: \x1b[0m');
        if (processor.metadata?.icon) {
            writer.write(`${processor.metadata.icon}  `);
        }
        writer.writeln(`${writer.wrapInColor(processor.command, CliForegroundColor.Cyan)} @${processor.version || '1.0.0'} - ${processor.description}`);
        this.writeSeparator(context);
        if (processor.author) {
            writer.writeln(`\x1b[33mAuthor:\x1b[0m ${processor.author.name}<${processor.author.email}>`);
            this.writeSeparator(context);
        }
        writer.write(writer.wrapInColor('Description: ', CliForegroundColor.Yellow));
        if (processor.writeDescription) {
            processor.writeDescription(context);
        }
        else if (processor.description) {
            writer.writeln(`${writer.wrapInColor('Description:', CliForegroundColor.Yellow)} ${processor.description}`);
        }
        else {
            writer.writeln(writer.wrapInColor('No description available', CliForegroundColor.Yellow));
        }
        this.writeSeparator(context);
        if (processor.processors?.length) {
            writer.writeln(writer.wrapInColor('Subcommands:', CliForegroundColor.Yellow));
            processor.processors.forEach((subprocessor) => {
                writer.writeln(`- ${writer.wrapInColor(subprocessor.command, CliForegroundColor.Cyan)} - ${subprocessor.description}`);
            });
            this.writeSeparator(context);
        }
        const parameters = [
            ...(processor.parameters || []),
            ...defaultParameters,
        ];
        writer.writeln(writer.wrapInColor('Parameters:', CliForegroundColor.Yellow));
        parameters.forEach((parameter) => {
            writer.writeln(`--${writer.wrapInColor(parameter.name, CliForegroundColor.Cyan)} (${parameter.type}) ${parameter.aliases ? `(${parameter.aliases.join(', ')})` : ''} - ${parameter.description}${parameter.required ? ' (required)' : ''}`);
        });
        this.writeSeparator(context);
        if (processor.metadata?.requireServer) {
            writer.writeln(writer.wrapInColor('Requires server to be running', CliForegroundColor.Red));
        }
    }
    writeSeparator({ writer }) {
        writer.writeDivider({
            char: '=',
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHelpCommandProcessor, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHelpCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHelpCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: i0.Injector }]; } });
const defaultParameters = [
    {
        name: 'version',
        aliases: ['v'],
        type: 'boolean',
        description: 'Displays the version of the command',
        required: false,
    },
    {
        name: 'main',
        type: 'boolean',
        description: 'Set the command as the main command',
        required: false,
    },
];

class CliHistoryCommandProcessor {
    constructor(commandHistoryService) {
        this.commandHistoryService = commandHistoryService;
        this.command = 'history';
        this.description = 'Prints the command history of the current session';
        this.processors = [];
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            icon: CliIcon.Code,
            module: 'system',
        };
        this.processors?.push({
            command: 'list',
            description: this.description,
            processCommand: this.processCommand.bind(this),
            writeDescription: this.writeDescription.bind(this),
        });
        this.processors?.push({
            command: 'clear',
            description: 'Clears the command history',
            processCommand: async (_, context) => {
                await this.commandHistoryService.clearHistory();
                context.writer.writeInfo('Command history cleared');
            },
            writeDescription: (context) => {
                context.writer.writeln('Clears the command history');
            },
        });
    }
    async processCommand(_, { writer }) {
        const history = this.commandHistoryService.getHistory();
        if (history.length === 0) {
            writer.writeln('No history available');
            return;
        }
        else {
            writer.writeln('Command history:');
            history.forEach((command, index) => {
                writer.writeln(`${index + 1}. ${command}`);
            });
        }
    }
    writeDescription({ writer }) {
        writer.writeln('Prints the command history of the current session');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHistoryCommandProcessor, deps: [{ token: CliCommandHistoryService }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHistoryCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHistoryCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: CliCommandHistoryService }]; } });

class CliHotKeysCommandProcessor {
    constructor() {
        this.command = 'hotkeys';
        this.description = 'Displays the hotkeys information';
        this.processors = [];
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            icon: '🔥',
            module: 'system',
        };
    }
    async processCommand(_, context) {
        context.writer.writeln(context.writer.wrapInColor('Avalaible hotkeys:', CliForegroundColor.Yellow));
        hotkeysInfo.forEach((hotkey) => {
            context.writer.writeln(`- ${context.writer.wrapInColor(hotkey.key, CliForegroundColor.Yellow)} - ${hotkey.description}`);
        });
    }
    writeDescription(context) {
        context.writer.writeln(this.description || 'Displays the hotkeys information');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHotKeysCommandProcessor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHotKeysCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliHotKeysCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

class CliPackageManagerService {
    constructor(store) {
        this.store = store;
        this.storageKey = 'cli-packages';
        this.QODALIS_COMMAND_PREFIX = '@qodalis/cli-';
    }
    /**
     * Retrieves the list of packages
     * @returns {Package[]} Array of packages
     */
    async getPackages() {
        const oldPackages = localStorage.getItem('cliPackages');
        if (oldPackages) {
            localStorage.removeItem('cliPackages');
            await this.store.set(this.storageKey, JSON.parse(oldPackages));
        }
        const packages = await this.store.get(this.storageKey);
        return packages ? packages : [];
    }
    /**
     * Retrieves a package by name from the list.
     * @param packageName {string} The name of the package to retrieve
     * @returns {Package | undefined} The package if found, undefined otherwise
     */
    async getPackage(packageName) {
        return (await this.getPackages()).find((p) => p.name === packageName ||
            p.name === this.QODALIS_COMMAND_PREFIX + packageName);
    }
    /**
     * Checks if a package with the given name exists in the list.
     * @param packageName {string} The name of the package to check
     * @returns {boolean} True if the package exists, false otherwise
     */
    async hasPackage(packageName) {
        return (await this.getPackage(packageName)) !== undefined;
    }
    /**
     * Adds a new package to the list and saves it in storage.
     * @param pkg {Package} The package to add
     */
    async addPackage(pkg) {
        const packages = await this.getPackages();
        if (packages.find((p) => p.name === pkg.name)) {
            throw new Error(`Package with name "${pkg.name}" already exists.`);
        }
        packages.push(pkg);
        await this.savePackages(packages);
    }
    /**
     * Removes a package by name and saves the updated list in storage
     * @param packageName {string} The name of the package to remove
     */
    async removePackage(packageName) {
        const packages = await this.getPackages();
        const packageToRemove = packages.find((p) => p.name === packageName ||
            p.name === this.QODALIS_COMMAND_PREFIX + packageName);
        if (!packageToRemove) {
            throw new Error(`Package with name "${packageName}" not found.`);
        }
        const updatedPackages = packages.filter((p) => p.name !== packageToRemove.name);
        if (packages.length === updatedPackages.length) {
            throw new Error(`Package with name "${packageName}" not found.`);
        }
        this.savePackages(updatedPackages);
        return packageToRemove;
    }
    /**
     * Updates an existing package in the list and saves the updated list in storage.
     * @param pkg {Package} The updated package
     * @throws {Error} If the package to update is not found
     * @returns {void}
     * @throws {Error} If the package to update is not found
     * @returns {void}
     */
    async updatePackage(pkg) {
        const packages = await this.getPackages();
        const index = packages.findIndex((p) => p.name === pkg.name);
        if (index === -1) {
            throw new Error(`Package with name "${pkg.name}" not found.`);
        }
        packages[index] = pkg;
        await this.savePackages(packages);
    }
    /**
     * Saves the list of packages to storage.
     * @param packages {Package[]} The list of packages to save
     */
    async savePackages(packages) {
        await this.store.set(this.storageKey, packages);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPackageManagerService, deps: [{ token: CliKeyValueStore }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPackageManagerService, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPackageManagerService, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: CliKeyValueStore }]; } });

class CliPackagesCommandProcessor {
    constructor(scriptsLoader, packagesManager, registry) {
        this.scriptsLoader = scriptsLoader;
        this.packagesManager = packagesManager;
        this.registry = registry;
        this.command = 'packages';
        this.description = 'Manage packages in the cli';
        this.author = DefaultLibraryAuthor;
        this.version = '1.0.2';
        this.processors = [];
        this.metadata = {
            sealed: true,
            icon: '📦',
            module: 'system',
        };
        this.registeredDependencies = [];
        const scope = this;
        this.processors = [
            {
                command: 'ls',
                description: 'List all packages in the cli',
                author: DefaultLibraryAuthor,
                async processCommand(_, context) {
                    const packages = await packagesManager.getPackages();
                    context.writer.writeln('Packages:');
                    if (packages.length === 0) {
                        context.writer.writeInfo('No packages installed');
                        context.writer.writeInfo("Use 'packages add <package>' to add a package");
                        return;
                    }
                    packages.forEach((pkg) => {
                        context.writer.writeInfo(`- ${pkg.name}@${pkg.version}`);
                    });
                },
                writeDescription(context) {
                    context.writer.writeln('  packages ls');
                },
            },
            {
                command: 'add',
                description: 'Add a package to the cli, e.g. packages add guid.',
                valueRequired: true,
                async processCommand(command, context) {
                    const packages = command.value.split(' ');
                    for (const pkg of packages) {
                        await scope.addPackage(pkg, context);
                    }
                },
                writeDescription({ writer }) {
                    writer.writeInfo("Supoorts multiple packages, e.g. 'packages add guid server-logs'");
                    writer.writeln('  packages add <package> ' +
                        writer.wrapInColor('# Add a package', CliForegroundColor.Green));
                    writer.writeln('  packages add guid');
                    writer.writeln('  packages add @qodalis/cli-guid');
                    writer.writeln('  packages add @qodalis/cli-server-logs');
                },
            },
            {
                command: 'remove',
                description: 'Remove a package from the cli',
                valueRequired: true,
                async processCommand(command, context) {
                    const packages = command.value.split(' ');
                    for (const pkg of packages) {
                        await scope.removePackage(pkg, context);
                    }
                },
            },
            {
                command: 'update',
                description: 'Update a package in the cli to the latest version or update all packages',
                allowUnlistedCommands: true,
                valueRequired: false,
                async processCommand(command, context) {
                    if (command.value) {
                        await scope.updatePackage(command.value, context);
                    }
                    else {
                        const packages = await packagesManager.getPackages();
                        for (const pkg of packages) {
                            await scope.updatePackage(pkg.name, context);
                        }
                        context.writer.writeSuccess('All packages updated');
                    }
                },
                writeDescription(context) {
                    context.writer.writeln('  packages update <package> ' +
                        context.writer.wrapInColor('# Update a specific package', CliForegroundColor.Green));
                    context.writer.writeln('  packages update ' +
                        context.writer.wrapInColor('# Update all packages', CliForegroundColor.Green));
                },
            },
        ];
    }
    async processCommand(command, context) {
        await context.executor.showHelp(command, context);
    }
    writeDescription({ writer }) {
        writer.writeln(this.description);
        writer.writeInfo('Available packages can be found at https://www.npmjs.com/org/qodalis');
        writer.writeln('  packages ls');
        writer.writeln('  packages add <package>');
    }
    async initialize(context) {
        const packages = await this.packagesManager.getPackages();
        for (const pkg of packages) {
            await this.registerPackageDependencies(pkg, context);
            await this.scriptsLoader.injectScript(pkg.url);
        }
    }
    async removePackage(name, context) {
        context.progressBar.show();
        try {
            const pkg = await this.packagesManager.removePackage(name);
            if (pkg) {
                const module = window[pkg.name];
                if (module) {
                    module.processors?.forEach((processor) => {
                        this.registry.unregisterProcessor(processor);
                    });
                }
            }
            context.progressBar.complete();
            context.writer.writeSuccess(`Package ${pkg.name}@${pkg.version} removed successfully`);
        }
        catch (e) {
            context.progressBar.complete();
            context.writer.writeError(e?.toString() || 'Unknown error');
        }
    }
    async addPackage(name, context) {
        const { progressBar, writer } = context;
        const hasPackage = await this.packagesManager.hasPackage(name);
        if (hasPackage) {
            writer.writeInfo(`Package ${name} already installed`);
            return;
        }
        progressBar.show();
        progressBar.setText(`Fetching package ${name}`);
        try {
            const promises = [
                this.scriptsLoader
                    .getScript(`https://unpkg.com/${name}` + '/package.json')
                    .catch((error) => ({
                    error,
                    content: null,
                    xhr: null,
                }))
                    .finally(() => {
                    progressBar.update(10, {
                        type: 'increment',
                    });
                }),
            ];
            if (!name.startsWith(this.packagesManager.QODALIS_COMMAND_PREFIX)) {
                promises.push(this.scriptsLoader
                    .getScript(`https://unpkg.com/${this.packagesManager.QODALIS_COMMAND_PREFIX}${name}` +
                    '/package.json')
                    .catch((error) => ({
                    error,
                    content: null,
                    xhr: null,
                }))
                    .finally(() => {
                    progressBar.update(10, {
                        type: 'increment',
                    });
                }));
            }
            progressBar.update(10);
            const packages = await Promise.all(promises);
            if (packages.every((p) => p.error)) {
                progressBar.setText('Package not found');
                throw packages[0].error;
            }
            const validResponses = packages
                .filter((p) => p.content)
                .map((p) => JSON.parse(p.content || '{}'));
            progressBar.update(20, {
                type: 'increment',
            });
            progressBar.setText("Checking package's dependencies");
            const packgeInfo = validResponses.some((x) => x.name.startsWith(this.packagesManager.QODALIS_COMMAND_PREFIX))
                ? validResponses.find((x) => x.name.startsWith(this.packagesManager.QODALIS_COMMAND_PREFIX))
                : validResponses[0];
            const response = await this.scriptsLoader.getScript(`https://unpkg.com/${packgeInfo.name}`, {
                onProgress: (progress) => {
                    context.progressBar.update(progress);
                },
            });
            progressBar.update(20, {
                type: 'increment',
            });
            progressBar.setText('Injecting package');
            const pkg = {
                name: packgeInfo.name,
                version: packgeInfo.version || 'latest',
                url: response.xhr.responseURL,
                dependencies: packgeInfo.cliDependencies || [],
            };
            progressBar.update(80);
            progressBar.setText('Registering dependencies');
            await this.registerPackageDependencies(pkg, context);
            progressBar.update(90);
            progressBar.setText('Dependencies registered');
            if (response.content) {
                await this.scriptsLoader.injectBodyScript(response.content);
            }
            progressBar.update(95);
            progressBar.setText('Saving package');
            await this.packagesManager.addPackage(pkg);
            context.progressBar.complete();
            context.writer.writeSuccess(`Package ${packgeInfo.name}@${packgeInfo.version} added successfully`);
        }
        catch (e) {
            context.progressBar.complete();
            context.writer.writeError(e?.toString() || 'Unknown error');
        }
    }
    async updatePackage(name, context) {
        const { progressBar, writer } = context;
        progressBar.show();
        try {
            progressBar.update(10, {
                type: 'increment',
            });
            progressBar.setText(`Updating package ${name}`);
            const currentPackage = await this.packagesManager.getPackage(name);
            if (!currentPackage) {
                progressBar.complete();
                writer.writeError(`Package ${name} not found`);
                return;
            }
            const packageUrl = `https://unpkg.com/${currentPackage.name}`;
            const newPackage = await this.scriptsLoader
                .getScript(packageUrl + '/package.json')
                .then((response) => {
                return JSON.parse(response.content || '{}');
            });
            progressBar.update(20, {
                type: 'increment',
            });
            progressBar.setText(`Checking for updates for package ${currentPackage.name}`);
            if (newPackage.version === currentPackage.version) {
                context.progressBar.complete();
                context.writer.writeInfo(`Package ${currentPackage.name} is already up to date with version ${currentPackage.version}`);
                return;
            }
            const response = await this.scriptsLoader.getScript(packageUrl, {
                onProgress: (progress) => {
                    context.progressBar.update(progress);
                },
            });
            progressBar.update(20, {
                type: 'increment',
            });
            progressBar.setText(`Updating package ${currentPackage.name}`);
            const updatedPkg = {
                name: newPackage.name,
                version: newPackage.version || 'latest',
                url: response.xhr.responseURL,
                dependencies: newPackage.cliDependencies || [],
            };
            await this.registerPackageDependencies(updatedPkg, context);
            progressBar.update(90);
            progressBar.setText(`Injecting package ${currentPackage.name}`);
            if (response.content) {
                await this.scriptsLoader.injectBodyScript(response.content);
            }
            await this.packagesManager.updatePackage(updatedPkg);
            progressBar.setText(`Package ${currentPackage.name} updated successfully`);
            progressBar.complete();
            writer.writeSuccess(`Package ${currentPackage.name} updated successfully to version ${newPackage.version} from ${currentPackage.version}`);
        }
        catch (e) {
            progressBar.complete();
            writer.writeError(e?.toString() || 'Unknown error');
        }
    }
    async registerPackageDependencies(pkg, { logger }) {
        if (pkg.dependencies && pkg.dependencies.length > 0) {
            logger.info(`Injecting package ${pkg.name} dependencies`);
            for (const dependency of pkg.dependencies) {
                if (this.registeredDependencies.includes(dependency.name)) {
                    logger.info(`Dependency ${dependency.globalName} already registered`);
                    continue;
                }
                await this.scriptsLoader.injectScript(dependency.url);
                if (dependency.globalName) {
                    const isAvailable = await this.waitForGlobal(dependency.globalName, 3000);
                    if (!isAvailable) {
                        logger.error(`Dependency ${dependency.globalName} not found in global scope within timeout`);
                        return;
                    }
                    else {
                        logger.info(`Dependency ${dependency.globalName} is available in global scope`);
                    }
                }
                this.registeredDependencies.push(dependency.name);
            }
        }
        else {
            logger.info(`Package ${pkg.name} has no dependencies`);
        }
    }
    /**
     * Waits until a global variable is available on the `window` object.
     * @param globalName {string} The name of the global variable to check.
     * @param timeout {number} The maximum time to wait in milliseconds.
     * @returns {Promise<boolean>} Resolves to `true` if the variable is found, `false` otherwise.
     */
    waitForGlobal(globalName, timeout) {
        return new Promise((resolve) => {
            const interval = 50; // Check every 50ms
            let elapsed = 0;
            const check = () => {
                if (window[globalName]) {
                    resolve(true);
                }
                else if (elapsed >= timeout) {
                    resolve(false);
                }
                else {
                    elapsed += interval;
                    setTimeout(check, interval);
                }
            };
            check();
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPackagesCommandProcessor, deps: [{ token: ScriptLoaderService }, { token: CliPackageManagerService }, { token: CliProcessorsRegistry_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPackagesCommandProcessor }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliPackagesCommandProcessor, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return [{ type: ScriptLoaderService }, { type: CliPackageManagerService }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [CliProcessorsRegistry_TOKEN]
                }] }]; } });

class CliVersionCommandProcessor {
    constructor() {
        this.command = 'version';
        this.description = 'Prints the version information';
        this.processors = [];
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            icon: CliIcon.Settings,
            module: 'system',
        };
    }
    async processCommand(_, { writer }) {
        writer.writeln(`CLI Version@${writer.wrapInColor(LIBRARY_VERSION, CliForegroundColor.Green)}`);
        writer.writeln(CLi_Name_Art);
        writer.writeln(writer.wrapInColor('Documentation: ', CliForegroundColor.Green) +
            'https://cli-docs.qodalis.com/');
        writer.writeln();
    }
    writeDescription(context) {
        context.writer.writeln('Prints the current version of the CLI');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliVersionCommandProcessor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliVersionCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliVersionCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }] });

const systemProviders = [
    resolveCommandProcessorProvider(CliHelpCommandProcessor),
    resolveCommandProcessorProvider(CliVersionCommandProcessor),
    resolveCommandProcessorProvider(CliFeedbackCommandProcessor),
    resolveCommandProcessorProvider(CliHistoryCommandProcessor),
    resolveCommandProcessorProvider(CliPackagesCommandProcessor),
    resolveCommandProcessorProvider(CliHotKeysCommandProcessor),
];

class CliClearCommandProcessor {
    constructor() {
        this.command = 'clear';
        this.description = 'Clears the terminal';
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            icon: '🧹',
            module: 'misc',
        };
    }
    async processCommand(_, context) {
        context.terminal.clear();
    }
}

class CliUnameCommandProcessor {
    constructor() {
        this.command = 'uname';
        this.description = 'Prints detailed system and browser information';
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            icon: CliIcon.Flame,
            module: 'misc',
        };
    }
    async processCommand(_, context) {
        const { writer } = context;
        function writeItem(key, value) {
            writer.writeln(`- ${writer.wrapInColor(key, CliForegroundColor.Cyan)}: ${value}`);
        }
        writer.writeInfo('CLI:');
        writeItem('Core version', LIBRARY_VERSION$1);
        writeItem('Cli version', LIBRARY_VERSION);
        writer.writeln();
        if (typeof navigator !== 'undefined') {
            writer.writeInfo('Browser Information:');
            writeItem('User Agent', navigator.userAgent);
            writeItem('Language', navigator.language);
            writeItem('Platform', navigator.platform);
            // Extracting more detailed information from userAgent
            const browserDetails = this.parseUserAgent(navigator.userAgent);
            writeItem('Browser', browserDetails.browserName);
            writeItem('Version', browserDetails.version);
            writeItem('OS', browserDetails.os);
        }
        else {
            writer.writeError('Browser information is not available in this environment.');
        }
    }
    parseUserAgent(userAgent) {
        let browserName = 'Unknown';
        let version = 'Unknown';
        let os = 'Unknown';
        // Simple parsing logic for demonstration purposes
        if (userAgent.includes('Chrome')) {
            browserName = 'Chrome';
            version = userAgent.match(/Chrome\/([\d.]+)/)?.[1] || 'Unknown';
        }
        else if (userAgent.includes('Firefox')) {
            browserName = 'Firefox';
            version = userAgent.match(/Firefox\/([\d.]+)/)?.[1] || 'Unknown';
        }
        else if (userAgent.includes('Safari') &&
            !userAgent.includes('Chrome')) {
            browserName = 'Safari';
            version = userAgent.match(/Version\/([\d.]+)/)?.[1] || 'Unknown';
        }
        else if (userAgent.includes('MSIE') ||
            userAgent.includes('Trident')) {
            browserName = 'Internet Explorer';
            version =
                userAgent.match(/(?:MSIE |rv:)([\d.]+)/)?.[1] || 'Unknown';
        }
        if (userAgent.includes('Windows')) {
            os = 'Windows';
        }
        else if (userAgent.includes('Mac OS')) {
            os = 'Mac OS';
        }
        else if (userAgent.includes('Linux')) {
            os = 'Linux';
        }
        else if (userAgent.includes('Android')) {
            os = 'Android';
        }
        else if (userAgent.includes('iPhone') || userAgent.includes('iPad')) {
            os = 'iOS';
        }
        return { browserName, version, os };
    }
}

class CliEchoCommandProcessor {
    constructor() {
        this.command = 'echo';
        this.description = 'Prints the specified text';
        this.allowUnlistedCommands = true;
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            icon: '📢',
            module: 'misc',
        };
    }
    async processCommand(command, context) {
        const text = command.value || command.data || '';
        if (typeof text === 'object') {
            context.writer.writeJson(text);
        }
        else {
            context.writer.writeln(text);
        }
        context.process.output(text);
    }
    writeDescription(context) {
        context.writer.writeln('echo <text>');
        context.writer.writeln('Prints the specified text');
    }
}

class CliAliasCommandProcessor {
    constructor() {
        this.command = 'alias';
        this.author = DefaultLibraryAuthor;
        this.description = 'Manage aliases for commands';
        this.metadata = {
            icon: '🔥',
            module: 'misc',
            sealed: true,
        };
        this.stateConfiguration = {
            initialState: {
                aliases: {},
            },
            storeName: 'aliases',
        };
        this.aliases = {};
        this.processors = [
            {
                command: 'ls',
                description: 'List all aliases',
                processCommand: async (_, context) => {
                    const { writer } = context;
                    writer.writeln('Aliases:');
                    Object.entries(this.aliases).forEach(([alias, command]) => {
                        writer.writeInfo(`  ${alias} -> ${command}`);
                    });
                    if (Object.keys(this.aliases).length === 0) {
                        writer.writeInfo('  No aliases defined');
                    }
                },
            },
        ];
    }
    async processCommand(command, context) {
        const registry = context.services.get(CliProcessorsRegistry_TOKEN);
        const { writer } = context;
        const aliases = Object.keys(command.args);
        if (aliases.length === 0) {
            writer.writeError('No aliases provided');
            context.process.exit(-1);
            return;
        }
        for (const alias of aliases) {
            if (registry.processors.some((p) => p.command === alias)) {
                writer.writeError(`${alias} cannot be aliased to ${command.args[alias]}`);
                context.process.exit(-1);
                return;
            }
            context.writer.writeInfo(`${alias} -> ${command.args[alias]}`);
        }
        context.state.updateState({
            aliases: {
                ...this.aliases,
                ...command.args,
            },
        });
        await context.state.persist();
    }
    async initialize(context) {
        context.state
            .select((x) => x['aliases'])
            .subscribe((aliases) => {
            this.aliases = aliases ?? {};
        });
    }
}

class CliUnAliasCommandProcessor {
    constructor() {
        this.command = 'unalias';
        this.author = DefaultLibraryAuthor;
        this.description = 'Remove aliases for commands';
        this.valueRequired = true;
        this.stateConfiguration = {
            initialState: {},
            storeName: 'aliases',
        };
        this.metadata = {
            icon: '🔥',
            module: 'misc',
            sealed: true,
        };
    }
    async processCommand(command, context) {
        const { writer } = context;
        const aliasToRemove = command.value;
        const { aliases } = context.state.getState();
        if (!aliases[aliasToRemove]) {
            writer.writeError(`Alias ${aliasToRemove} not found`);
            context.process.exit(-1);
            return;
        }
        const updated = {};
        Object.keys(aliases)
            .filter((alias) => alias !== aliasToRemove)
            .forEach((alias) => {
            updated[alias] = aliases[alias];
        });
        context.state.updateState({
            aliases: updated,
        });
        await context.state.persist();
    }
}

class CliSleepCommandProcessor {
    constructor() {
        this.command = 'sleep';
        this.description = 'Sleep for a specified amount of time';
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            module: 'misc',
            icon: CliIcon.Timer,
        };
        this.valueRequired = true;
    }
    async processCommand(command, { writer }) {
        const time = parseInt(command.value);
        await delay(time);
        writer.writeInfo(`Slept for ${time}ms`);
    }
    writeDescription(context) {
        context.writer.writeln('Sleep for a specified amount of time');
        context.writer.writeln('Usage: sleep <time>');
        context.writer.writeln('Usage: sleep 5000 # Sleep for 5 seconds');
    }
}

const miscProcessors = [
    new CliClearCommandProcessor(),
    new CliEchoCommandProcessor(),
    new CliEvalCommandProcessor(),
    new CliAliasCommandProcessor(),
    new CliUnAliasCommandProcessor(),
    new CliSleepCommandProcessor(),
    new CliUnameCommandProcessor(),
];

class CliAddUserCommandProcessor {
    constructor(usersStore) {
        this.usersStore = usersStore;
        this.command = 'adduser';
        this.description = 'Add a new user';
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            module: 'users',
            icon: CliIcon.User,
        };
        this.stateConfiguration = {
            initialState: {},
            storeName: 'users',
        };
        this.allowUnlistedCommands = true;
        this.valueRequired = true;
        this.parameters = [
            {
                name: 'email',
                description: 'The email of the user',
                required: true,
                type: 'email',
                validator: (value) => {
                    //validate email format
                    const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
                    if (!emailRegex.test(value)) {
                        return {
                            valid: false,
                            message: 'Invalid email format',
                        };
                    }
                    return {
                        valid: true,
                    };
                },
            },
            {
                name: 'groups',
                description: 'The groups the user belongs to, separated by commas',
                type: 'string',
                required: false,
            },
        ];
    }
    async processCommand(command, context) {
        const name = command.value;
        const email = command.args['email'];
        const groups = command.args['groups']?.split(',') || [];
        try {
            await this.usersStore.createUser({
                name,
                email,
                groups,
            });
            context.writer.writeInfo('User created successfully');
        }
        catch (e) {
            console.error(e);
            context.writer.writeError(e?.toString() || 'An error occurred while creating the user');
        }
    }
    writeDescription(context) {
        context.writer.writeln('Add a new user');
        context.writer.writeln('Usage: adduser <name> --email=<email> --groups=<groups>');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliAddUserCommandProcessor, deps: [{ token: ICliUsersStoreService_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliAddUserCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliAddUserCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliUsersStoreService_TOKEN]
                }] }]; } });

class CliListUsersCommandProcessor {
    constructor(usersStore) {
        this.usersStore = usersStore;
        this.command = 'listusers';
        this.description = 'List all users';
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            module: 'users',
            icon: CliIcon.User,
        };
        this.stateConfiguration = {
            initialState: {},
            storeName: 'users',
        };
        this.parameters = [
            {
                name: 'query',
                description: 'The query to filter the users',
                type: 'string',
                required: false,
            },
            {
                name: 'skip',
                description: 'The number of users to skip',
                type: 'number',
                required: false,
            },
            {
                name: 'take',
                description: 'The maximum number of users to return',
                type: 'number',
                required: false,
            },
        ];
    }
    async processCommand(command, { writer }) {
        const users = await firstValueFrom(this.usersStore.getUsers({
            query: command.args['query'],
            skip: command.args['skip'],
            take: command.args['take'],
        }));
        writer.writeln('Users:');
        writer.writeObjectsAsTable(users);
    }
    writeDescription({ writer }) {
        writer.writeln(this.description);
        writer.writeln('Usage: listusers');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliListUsersCommandProcessor, deps: [{ token: ICliUsersStoreService_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliListUsersCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliListUsersCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliUsersStoreService_TOKEN]
                }] }]; } });

class CliSwitchUserCommandProcessor {
    constructor(userSessionService, usersStore) {
        this.userSessionService = userSessionService;
        this.usersStore = usersStore;
        this.command = 'su';
        this.description = 'Switch user';
        this.allowUnlistedCommands = true;
        this.parameters = [
            {
                name: 'reload',
                description: 'Reload the page after switching user',
                type: 'boolean',
                required: false,
                aliases: ['r'],
            },
        ];
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            module: 'users',
            icon: CliIcon.User,
        };
        this.stateConfiguration = {
            initialState: {},
            storeName: 'users',
        };
        this.valueRequired = true;
    }
    async processCommand(command, context) {
        try {
            const fromUser = context.userSession?.user;
            const toUser = command.value;
            if (!fromUser) {
                context.writer.writeError('Missing user to switch from');
                context.process.exit(-1);
                return;
            }
            if (!toUser) {
                context.writer.writeError('Missing user to switch to');
                context.process.exit(-1);
                return;
            }
            context.spinner?.show(CliIcon.User + '  Switching...');
            const user = await firstValueFrom(this.usersStore.getUser(toUser));
            if (!user) {
                context.writer.writeError(`User ${toUser} not found`);
                context.spinner?.hide();
                context.process.exit(-1);
                return;
            }
            if (user.id === fromUser.id) {
                context.writer.writeError('Already on the user');
                context.spinner?.hide();
                context.process.exit(-1);
                return;
            }
            await this.userSessionService.setUserSession({
                user,
            });
            context.spinner?.hide();
            context.writer.writeSuccess(`Switch to ${context.writer.wrapInColor(toUser, CliForegroundColor.Cyan)} was successfully`);
            const reload = command.args['reload'] ||
                command.args['r'] ||
                context.options?.usersModule?.reloadPageOnUserChange === true;
            if (reload) {
                context.writer.writeln('Reloading the page in 3 seconds...');
                setTimeout(() => {
                    window.location.reload();
                }, 3000);
            }
        }
        catch (e) {
            console.error(e);
            context.spinner?.hide();
            context.writer.writeError('Failed to switch user');
            context.process.exit(-1);
            return;
        }
    }
    writeDescription(context) {
        context.writer.writeln('Switch user command');
        context.writer.writeln('Usage: su <user email>');
        context.writer.writeln('Example: su user@domain.com');
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliSwitchUserCommandProcessor, deps: [{ token: ICliUserSessionService_TOKEN }, { token: ICliUsersStoreService_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliSwitchUserCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliSwitchUserCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliUserSessionService_TOKEN]
                }] }, { type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliUsersStoreService_TOKEN]
                }] }]; } });

class CliWhoamiCommandProcessor {
    constructor(userSessionService) {
        this.userSessionService = userSessionService;
        this.command = 'whoami';
        this.description = 'Display current user information';
        this.author = DefaultLibraryAuthor;
        this.metadata = {
            sealed: true,
            module: 'users',
            icon: CliIcon.User,
        };
        this.stateConfiguration = {
            initialState: {},
            storeName: 'users',
        };
        this.parameters = [
            {
                name: 'info',
                description: 'Display user information',
                type: 'boolean',
                required: false,
                aliases: ['i'],
            },
        ];
    }
    async processCommand(command, context) {
        const user = await firstValueFrom(this.userSessionService.getUserSession());
        if (!user) {
            context.writer.writeln('No user session found');
            return;
        }
        if (command.args['info'] || command.args['i']) {
            context.writer.writeln('User information:');
            context.writer.writeObjectsAsTable([user.user]);
        }
        else {
            context.writer.writeln(user?.user.email);
        }
    }
    writeDescription(context) {
        context.writer.writeln(this.description);
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliWhoamiCommandProcessor, deps: [{ token: ICliUserSessionService_TOKEN }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliWhoamiCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliWhoamiCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return [{ type: undefined, decorators: [{
                    type: Inject,
                    args: [ICliUserSessionService_TOKEN]
                }] }]; } });

const usersProviders = [
    resolveCommandProcessorProvider(CliSwitchUserCommandProcessor),
    resolveCommandProcessorProvider(CliWhoamiCommandProcessor),
    resolveCommandProcessorProvider(CliAddUserCommandProcessor),
    resolveCommandProcessorProvider(CliListUsersCommandProcessor),
];

class CliLogger {
    constructor() {
        this.CliLogLevel = CliLogLevel.ERROR;
    }
    setCliLogLevel(level) {
        this.CliLogLevel = level;
    }
    log(...args) {
        if (this.CliLogLevel <= CliLogLevel.LOG) {
            console.log(...args);
        }
    }
    info(...args) {
        if (this.CliLogLevel <= CliLogLevel.INFO) {
            console.info(...args);
        }
    }
    warn(...args) {
        if (this.CliLogLevel <= CliLogLevel.WARN) {
            console.warn(...args);
        }
    }
    error(...args) {
        if (this.CliLogLevel <= CliLogLevel.ERROR) {
            console.error(...args);
        }
    }
    debug(...args) {
        if (this.CliLogLevel <= CliLogLevel.DEBUG) {
            console.debug(...args);
        }
    }
}

class CliCommandProcessorRegistry {
    constructor() {
        this.processors = [...miscProcessors];
    }
    registerProcessor(processor) {
        const existingProcessor = this.getProcessorByName(processor.command);
        if (existingProcessor) {
            if (existingProcessor.metadata?.sealed) {
                console.warn(`Processor with command: ${processor.command} is sealed and cannot be replaced.`);
                return;
            }
            const existingIndex = this.processors.findIndex((p) => p.command === processor.command);
            // Replace the existing processor
            this.processors[existingIndex] = processor;
        }
        else {
            this.processors.push(processor);
        }
    }
    unregisterProcessor(processor) {
        const existingProcessor = this.getProcessorByName(processor.command);
        if (existingProcessor) {
            if (existingProcessor.metadata?.sealed) {
                console.warn(`Processor with command: ${processor.command} is sealed and cannot be removed.`);
                return;
            }
        }
        const index = this.processors.findIndex((p) => p.command === processor.command);
        if (index !== -1) {
            this.processors.splice(index, 1);
        }
    }
    findProcessor(mainCommand, chainCommands) {
        return this.findProcessorInCollection(mainCommand, chainCommands, this.processors);
    }
    /**
     * Recursively searches for a processor matching the given command.
     * @param mainCommand The main command name.
     * @param chainCommands The remaining chain commands (if any).
     * @param processors The list of available processors.
     * @returns The matching processor or undefined if not found.
     */
    findProcessorInCollection(mainCommand, chainCommands, processors) {
        const processor = processors.find((p) => p.command.toLowerCase() === mainCommand.toLowerCase());
        if (!processor) {
            return undefined;
        }
        if (chainCommands.length === 0) {
            return processor;
        }
        if (processor.processors && processor.processors.length > 0) {
            return this.findProcessorInCollection(chainCommands[0], chainCommands.slice(1), processor.processors);
        }
        else if (processor.allowUnlistedCommands || processor.valueRequired) {
            return processor;
        }
        return undefined;
    }
    getRootProcessor(child) {
        return child.parent ? this.getRootProcessor(child.parent) : child;
    }
    getProcessorByName(name) {
        return this.processors.find((p) => p.command.toLowerCase() === name.toLowerCase());
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandProcessorRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandProcessorRegistry }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliCommandProcessorRegistry, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return []; } });

class CliServiceProvider {
    constructor(injector) {
        this.providers = [];
        this.currentInjector = injector;
    }
    get(service) {
        try {
            return this.currentInjector.get(service);
        }
        catch (e) {
            throw new Error(`Service ${service} not found`);
        }
    }
    set(definition) {
        const definitions = Array.isArray(definition)
            ? definition
            : [definition];
        const newProviders = [];
        for (const def of definitions) {
            let newProvider = {
                provide: def.provide,
            };
            if (def.hasOwnProperty('useClass')) {
                const classProvider = {
                    provide: def.provide,
                    useClass: def.useClass,
                    multi: def.multi,
                };
                newProvider = classProvider;
            }
            else if (def.hasOwnProperty('useValue')) {
                const valueProvider = {
                    provide: def.provide,
                    useValue: def.useValue,
                    multi: def.multi,
                };
                newProvider = valueProvider;
            }
            else if (def.hasOwnProperty('useFactory')) {
                const valueProvider = {
                    provide: def.provide,
                    useFactory: def.useFactory,
                    multi: def.multi,
                };
                newProvider = valueProvider;
            }
            newProviders.push(newProvider);
        }
        this.providers.push(...newProviders);
        this.currentInjector = Injector.create({
            providers: newProviders,
            parent: this.currentInjector,
        });
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliServiceProvider, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliServiceProvider }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliServiceProvider, decorators: [{
            type: Injectable
        }], ctorParameters: function () { return [{ type: i0.Injector }]; } });

class CliThemeCommandProcessor {
    constructor() {
        this.command = 'theme';
        this.description = 'Interact with the theme';
        this.author = DefaultLibraryAuthor;
        this.version = '1.0.1';
        this.processors = [];
        this.metadata = {
            sealed: true,
            icon: '🎨',
            module: 'system',
        };
        this.stateConfiguration = {
            initialState: {
                selectedTheme: 'default',
                customOptions: null,
            },
        };
        this.themeOptions = Object.keys(themeOptions);
        this.processors = [
            {
                command: 'list',
                description: 'List available themes',
                processCommand: async (command, context) => {
                    context.writer.writeln('Available themes:');
                    Object.keys(themes).forEach((theme) => {
                        context.writer.writeln(context.writer.wrapInColor(theme, CliForegroundColor.Cyan));
                    });
                },
            },
            {
                command: 'current',
                description: 'Show the current theme',
                processCommand: async (_, context) => {
                    const state = context.state.getState();
                    const theme = state.selectedTheme || 'custom';
                    context.writer.writeln(`Current theme: ${theme}`);
                    context.writer.writeln('Theme options:');
                    Object.keys(context.terminal.options.theme).forEach((key) => {
                        context.writer.writeln(`${key}: ${context.terminal.options.theme[key]}`);
                    });
                },
            },
            {
                command: 'apply',
                description: 'Apply a theme',
                valueRequired: true,
                processCommand: async (command, context) => {
                    const theme = command.value;
                    if (!themes[theme]) {
                        context.writer.writeError(`Theme ${theme} not found`);
                        return;
                    }
                    context.terminal.options.theme = themes[theme];
                    context.state.updateState({
                        selectedTheme: theme,
                        customOptions: null,
                    });
                    await context.state.persist();
                    this.applyStyles(context);
                    context.writer.writeSuccess(`Theme ${theme} applied`);
                },
            },
            {
                command: 'set',
                allowUnlistedCommands: true,
                description: 'Set a theme variable',
                parameters: [
                    {
                        name: 'save',
                        description: 'Save the theme settings',
                        type: 'boolean',
                        required: false,
                    },
                ],
                processCommand: async (command, context) => {
                    const [key, value] = command.command.split(' ').slice(2);
                    if (!this.themeOptions.includes(key)) {
                        context.writer.writeError(`Unsupported key: ${key}, supported keys: ${this.themeOptions.join(', ')}`);
                        return;
                    }
                    context.terminal.options.theme = {
                        ...context.terminal.options.theme,
                        [key]: value,
                    };
                    context.state.updateState({
                        selectedTheme: null,
                        customOptions: context.terminal.options.theme,
                    });
                    context.writer.writeSuccess(`Set ${key} to ${value}`);
                    this.applyStyles(context);
                    if (command.args['save']) {
                        await this.saveTheme(context);
                    }
                },
            },
            {
                command: 'save',
                description: 'Save the current theme settings',
                processCommand: async (command, context) => {
                    context.terminal.options.theme = {};
                    context.state.updateState({
                        selectedTheme: null,
                        customOptions: context.terminal.options.theme,
                    });
                    await this.saveTheme(context);
                },
            },
            {
                command: 'reset',
                description: 'Reset the theme to the default',
                processCommand: async (command, context) => {
                    context.terminal.options.theme = { ...this.defaultTheme };
                    context.state.reset();
                    await context.state.persist();
                    context.writer.writeSuccess('Theme reset to default');
                },
            },
        ];
    }
    async initialize(context) {
        this.defaultTheme = context.terminal.options.theme;
        const state = context.state.getState();
        if (state.selectedTheme && state.selectedTheme !== 'default') {
            context.terminal.options.theme = themes[state.selectedTheme];
        }
        else if (state.customOptions) {
            context.terminal.options.theme = state.customOptions;
        }
        this.applyStyles(context);
    }
    async processCommand(command, context) {
        context.executor.showHelp(command, context);
    }
    applyStyles(context) {
        const parents = document.getElementsByClassName('terminal-container');
        for (const parent of Array.from(parents)) {
            parent.style.background =
                context.terminal.options.theme?.background ||
                    this.defaultTheme.background;
        }
    }
    async saveTheme(context) {
        await context.state.persist();
        context.writer.writeSuccess('Theme saved');
    }
    writeDescription(context) {
        context.writer.writeln('Interact with the theme');
        context.writer.writeln('Examples:');
        context.writer.writeln(context.writer.wrapInColor('theme apply dracula', CliForegroundColor.Magenta));
        context.writer.writeln(context.writer.wrapInColor('theme reset', CliForegroundColor.Magenta));
        context.writer.writeln(context.writer.wrapInColor('theme set background red', CliForegroundColor.Magenta));
        context.writer.writeln(context.writer.wrapInColor('theme set foreground red', CliForegroundColor.Magenta));
        context.writer.write('Available theme options: ');
        context.writer.writeln(context.writer.wrapInColor(this.themeOptions.join(', ') ?? '', CliForegroundColor.Blue));
    }
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliThemeCommandProcessor, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
    static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliThemeCommandProcessor, providedIn: 'root' }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliThemeCommandProcessor, decorators: [{
            type: Injectable,
            args: [{
                    providedIn: 'root',
                }]
        }], ctorParameters: function () { return []; } });
const themeOptions = {
    background: '',
    foreground: '',
    black: '',
    blue: '',
    brightBlack: '',
    brightBlue: '',
    brightCyan: '',
    brightGreen: '',
    brightMagenta: '',
    brightRed: '',
    brightWhite: '',
    brightYellow: '',
    cyan: '',
    green: '',
    magenta: '',
    red: '',
    white: '',
    yellow: '',
    cursor: '',
    cursorAccent: '',
    selectionBackground: '',
    selectionForeground: '',
    selectionInactiveBackground: '',
};

const resolveCliProviders = () => {
    return [
        ScriptLoaderService,
        CliCanViewService,
        CliStateStoreManager,
        {
            useClass: CliUserSessionService,
            provide: ICliUserSessionService_TOKEN,
        },
        {
            useClass: CliUsersStoreService,
            provide: ICliUsersStoreService_TOKEN,
        },
        {
            useClass: CliDefaultPingServerService,
            provide: ICliPingServerService_TOKEN,
        },
        {
            useClass: CliLogger,
            provide: CliLogger_TOKEN,
        },
        {
            useClass: CliCommandProcessorRegistry,
            provide: CliProcessorsRegistry_TOKEN,
        },
        {
            useClass: CliServiceProvider,
            provide: CliServiceProvider_TOKEN,
        },
        ...systemProviders,
        ...usersProviders,
        resolveCommandProcessorProvider(CliThemeCommandProcessor),
        resolveCommandProcessorProvider(CliPingCommandProcessor),
    ];
};

class CliModule {
    static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
    static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "16.2.12", ngImport: i0, type: CliModule, declarations: [CliComponent,
            CollapsableContentComponent,
            CliPanelComponent,
            CliTerminalComponent], imports: [CommonModule,
            //PrimeNG
            ButtonModule,
            TabMenuModule,
            MenuModule,
            SplitButtonModule,
            TooltipModule], exports: [CliPanelComponent, CliComponent] }); }
    static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliModule, providers: [
            resolveCliProviders(),
            { provide: ActivatedRoute, useValue: {} },
        ], imports: [CommonModule,
            //PrimeNG
            ButtonModule,
            TabMenuModule,
            MenuModule,
            SplitButtonModule,
            TooltipModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CliModule, decorators: [{
            type: NgModule,
            args: [{
                    declarations: [
                        CliComponent,
                        CollapsableContentComponent,
                        CliPanelComponent,
                        CliTerminalComponent,
                    ],
                    imports: [
                        CommonModule,
                        //PrimeNG
                        ButtonModule,
                        TabMenuModule,
                        MenuModule,
                        SplitButtonModule,
                        TooltipModule,
                    ],
                    providers: [
                        resolveCliProviders(),
                        { provide: ActivatedRoute, useValue: {} },
                    ],
                    exports: [CliPanelComponent, CliComponent],
                }]
        }] });

/*
 * Public API Surface of cli
 */

/**
 * Generated bundle index. Do not edit.
 */

export { CliCanViewService, CliCommandExecutionContext, CliCommandExecutorService, CliCommandHistoryService, CliCommandProcessor_TOKEN, CliComponent, CliDefaultPingServerService, CliExecutionContext, CliExecutionProcess, CliLogger_TOKEN, CliModule, CliPanelComponent, CliProcessorsRegistry_TOKEN, CliServiceProvider_TOKEN, CliUserSessionService, CliUsersStoreService, CommandParser, ICliPingServerService_TOKEN, ICliUserSessionService_TOKEN, ICliUsersStoreService_TOKEN, ScriptLoaderService, getGreetingBasedOnTime, openLink, resolveCliProvider, resolveCommandProcessorProvider };
//# sourceMappingURL=qodalis-angular-cli.mjs.map