import { Constants } from '../constants.js';
import {} from '../enums/index.js';
/** Cancelled Extension that can be only be thrown by the `cancellablePromise()` function */
export class CancelledException extends Error {
    constructor(message) {
        super(message);
        Object.setPrototypeOf(this, CancelledException.prototype);
    }
}
/**
 * From an input Promise, make it cancellable by wrapping it inside an object that holds the promise and a `cancel()` method
 * @param {Promise<any>} - input Promise
 * @returns {Object} - Promise wrapper that holds the promise and a `cancel()` method
 */
export function cancellablePromise(inputPromise) {
    let hasCancelled = false;
    if (inputPromise instanceof Promise) {
        return {
            promise: inputPromise.then((result) => {
                if (hasCancelled) {
                    throw new CancelledException('Cancelled Promise');
                }
                return result;
            }),
            cancel: () => (hasCancelled = true),
        };
    }
    return inputPromise;
}
/**
 * Try casting an input of type Promise | Observable into a Promise type.
 * @param object which could be of type Promise or Observable
 * @param fromServiceName string representing the caller service name and will be used if we throw a casting problem error
 */
export function castObservableToPromise(rxjs, input, fromServiceName = '') {
    let promise = input;
    if (input instanceof Promise) {
        // if it's already a Promise then return it
        return input;
    }
    else if (rxjs.isObservable(input)) {
        promise = rxjs.firstValueFrom(input);
    }
    if (!(promise instanceof Promise)) {
        throw new Error(`Something went wrong, Slickgrid-Universal ${fromServiceName} is not able to convert the Observable into a Promise.`);
    }
    return promise;
}
export function createDocumentFragmentOrElement(gridOptions, defaultElmType = 'span') {
    return gridOptions?.preventDocumentFragmentUsage ? document.createElement(defaultElmType) : new DocumentFragment();
}
/**
 * Mutate the original array and add a treeLevel (defaults to `__treeLevel`) property on each item.
 * @param {Array<Object>} treeArray - hierarchical tree array
 * @param {Object} options - options containing info like children & treeLevel property names
 * @param {Number} [treeLevel] - current tree level
 */
export function addTreeLevelByMutation(treeArray, options, treeLevel = 0) {
    // options is Required<Pick<...>> so childrenPropName is always a defined string — no need for getTreeDataOptionPropName
    const childrenPropName = options.childrenPropName;
    if (Array.isArray(treeArray)) {
        for (let i = 0; i < treeArray.length; i++) {
            const item = treeArray[i];
            if (item) {
                const children = item[childrenPropName];
                if (Array.isArray(children) && children.length > 0) {
                    treeLevel++;
                    addTreeLevelByMutation(children, options, treeLevel);
                    treeLevel--;
                }
                item[options.levelPropName] = treeLevel;
            }
        }
    }
}
export function addTreeLevelAndAggregatorsByMutation(treeArray, options, treeLevel = 0, parent = null) {
    // options is Required<Pick<...>> so childrenPropName is always a defined string — no need for getTreeDataOptionPropName
    const childrenPropName = options.childrenPropName;
    const { aggregator } = options;
    if (Array.isArray(treeArray)) {
        for (let i = 0; i < treeArray.length; i++) {
            const item = treeArray[i];
            if (item) {
                const children = item[childrenPropName];
                const isParent = Array.isArray(children);
                if (isParent && children.length > 0) {
                    aggregator.init(item, true);
                    treeLevel++;
                    addTreeLevelAndAggregatorsByMutation(children, options, treeLevel, item);
                    treeLevel--;
                }
                if (parent && aggregator.isInitialized && typeof aggregator.accumulate === 'function' && !item?.__filteredOut) {
                    aggregator.accumulate(item, isParent);
                    aggregator.storeResult(parent.__treeTotals);
                }
                item[options.levelPropName] = treeLevel;
            }
        }
    }
}
/**
 * Convert a hierarchical (tree) array (with children) into a flat array structure array (where the children are pushed as next indexed item in the array)
 * Uses an O(n) iterative DFS stack. Creates a shallow flat copy per node (excluding childrenPropName)
 * so that the original hierarchical source nodes are never mutated.
 * Note: for perf reasons, it mutates the array by adding extra props like `treeLevel`
 * @param {Array<Object>} treeArray - input hierarchical (tree) array
 * @param {Object} options - you can provide "childrenPropName" and other options (defaults to "children")
 * @return {Array<Object>} output - Parent/Child array
 */
export function flattenToParentChildArray(treeArray, options) {
    const identifierPropName = getTreeDataOptionPropName(options, 'identifierPropName');
    const childrenPropName = getTreeDataOptionPropName(options, 'childrenPropName');
    const hasChildrenPropName = getTreeDataOptionPropName(options, 'hasChildrenPropName');
    const parentPropName = getTreeDataOptionPropName(options, 'parentPropName');
    const levelPropName = getTreeDataOptionPropName(options, 'levelPropName');
    if (options?.shouldAddTreeLevelNumber) {
        if (Array.isArray(options?.aggregators)) {
            options.aggregators.forEach((aggregator) => {
                addTreeLevelAndAggregatorsByMutation(treeArray, { childrenPropName, levelPropName, aggregator });
            });
        }
        else {
            addTreeLevelByMutation(treeArray, { childrenPropName, levelPropName });
        }
    }
    // O(n) iterative DFS - two parallel stacks avoid tuple array allocations per node
    const flat = [];
    const nodeStack = [];
    const parentStack = [];
    for (let i = treeArray.length - 1; i >= 0; i--) {
        nodeStack.push(treeArray[i]);
        parentStack.push(undefined);
    }
    while (nodeStack.length > 0) {
        const node = nodeStack.pop();
        const parentNode = parentStack.pop();
        const children = node[childrenPropName];
        // Create a shallow flat copy without childrenPropName. Flat and hierarchical items share the same
        // object references, so in-place deletion on the original would corrupt the hierarchical dataset
        // (breaking findItemInTreeStructure and direct tree mutations). Spread + delete on the copy keeps
        // the hierarchical source intact and produces a clean flat item.
        const flatNode = { ...node };
        delete flatNode[childrenPropName];
        flatNode[parentPropName] = parentNode != null ? parentNode[identifierPropName] : null;
        flatNode[hasChildrenPropName] = Array.isArray(children);
        flat.push(flatNode);
        if (Array.isArray(children)) {
            for (let i = children.length - 1; i >= 0; i--) {
                nodeStack.push(children[i]);
                parentStack.push(node);
            }
        }
    }
    return flat;
}
/** Find the associated property name from the Tree Data option when found or return a default property name that we defined internally */
export function getTreeDataOptionPropName(treeDataOptions, optionName, defaultDataIdPropName = 'id') {
    let propName = '';
    switch (optionName) {
        case 'childrenPropName':
            propName = treeDataOptions?.[optionName] ?? Constants.treeDataProperties.CHILDREN_PROP;
            break;
        case 'collapsedPropName':
            propName = treeDataOptions?.[optionName] ?? Constants.treeDataProperties.COLLAPSED_PROP;
            break;
        case 'hasChildrenPropName':
            propName = treeDataOptions?.[optionName] ?? Constants.treeDataProperties.HAS_CHILDREN_PROP;
            break;
        case 'identifierPropName':
            propName = treeDataOptions?.[optionName] ?? defaultDataIdPropName;
            break;
        case 'lazyLoadingPropName':
            propName = treeDataOptions?.[optionName] ?? Constants.treeDataProperties.LAZY_LOADING_PROP;
            break;
        case 'levelPropName':
            propName = treeDataOptions?.[optionName] ?? Constants.treeDataProperties.TREE_LEVEL_PROP;
            break;
        case 'parentPropName':
            propName = treeDataOptions?.[optionName] ?? Constants.treeDataProperties.PARENT_PROP;
            break;
    }
    return propName;
}
/**
 * Convert a flat array (with "parentId" references) into a hierarchical (tree) dataset structure (where children are array(s) inside their parent objects)
 * Note: for perf reasons, it mutates the array by adding extra props like `treeLevel`
 * @param flatArray input array (flat dataset)
 * @param options you can provide the following tree data options (which are all prop names, except 1 boolean flag, to use or else use their defaults):: collapsedPropName, childrenPropName, parentPropName, identifierPropName and levelPropName and initiallyCollapsed (boolean)
 * @return roots - hierarchical (tree) data view array
 */
export function unflattenParentChildArrayToTree(flatArray, options) {
    const identifierPropName = getTreeDataOptionPropName(options, 'identifierPropName');
    const childrenPropName = getTreeDataOptionPropName(options, 'childrenPropName');
    const parentPropName = getTreeDataOptionPropName(options, 'parentPropName');
    const levelPropName = getTreeDataOptionPropName(options, 'levelPropName');
    const collapsedPropName = getTreeDataOptionPropName(options, 'collapsedPropName');
    const initiallyCollapsed = options?.initiallyCollapsed ?? false;
    const inputArray = flatArray || [];
    const roots = []; // items without parent which are at the root
    // build id→item index in a single pass, clearing any stale children
    // Map is used instead of a plain object to avoid prototype chain overhead and Object.keys() allocation
    const all = new Map();
    for (let i = 0; i < inputArray.length; i++) {
        const item = inputArray[i];
        all.set(item[identifierPropName], item);
        delete item[childrenPropName];
    }
    // connect children to their parent in a second pass over the original array
    // (avoids Object.keys() which would allocate a separate string[] of all ids)
    for (let i = 0; i < inputArray.length; i++) {
        const item = inputArray[i];
        const parentId = item[parentPropName];
        if (!(parentPropName in item) || parentId === null || parentId === undefined || parentId === '') {
            roots.push(item);
        }
        else {
            const p = all.get(parentId);
            if (p !== undefined) {
                if (!(childrenPropName in p)) {
                    p[childrenPropName] = [];
                }
                p[childrenPropName].push(item);
                if (p[collapsedPropName] === undefined) {
                    p[collapsedPropName] = initiallyCollapsed;
                }
            }
        }
    }
    // we need and want the Tree Level,
    // we can do that after the tree is created and mutate the array by adding a __treeLevel property on each item
    // perhaps there might be a way to add this while creating the tree for now that is the easiest way I found
    if (Array.isArray(options?.aggregators)) {
        options.aggregators.forEach((aggregator) => {
            addTreeLevelAndAggregatorsByMutation(roots, { childrenPropName, levelPropName, aggregator }, 0);
        });
    }
    else {
        addTreeLevelByMutation(roots, { childrenPropName, levelPropName }, 0);
    }
    return roots;
}
/**
 * Find an item from a tree (hierarchical) view structure (a parent that can have children array which themseleves can have children and so on)
 * @param {Array<Object>} treeArray - hierarchical tree dataset
 * @param {Function} predicate - search predicate to find the item in the hierarchical tree structure
 * @param {String} childrenPropertyName - children property name to use in the tree (defaults to "children")
 */
export function findItemInTreeStructure(treeArray, predicate, childrenPropertyName) {
    if (!childrenPropertyName) {
        throw new Error('findItemInTreeStructure requires parameter "childrenPropertyName"');
    }
    // O(n) iterative DFS — avoids recursion (no stack overflow risk) and eliminates
    // the two-pass per level (find + filter) and intermediate childElements array of the recursive approach
    const stack = treeArray.slice();
    while (stack.length > 0) {
        const item = stack.pop();
        if (predicate(item)) {
            return item;
        }
        const children = item[childrenPropertyName];
        if (Array.isArray(children)) {
            for (let i = children.length - 1; i >= 0; i--) {
                stack.push(children[i]);
            }
        }
    }
    return undefined;
}
/**
 * Take a number (or a string) and display it as a formatted decimal string with defined minimum and maximum decimals
 * @param input
 * @param minDecimal
 * @param maxDecimal
 * @param decimalSeparator
 * @param thousandSeparator
 */
export function decimalFormatted(input, minDecimal, maxDecimal, decimalSeparator = '.', thousandSeparator = '') {
    if (isNaN(+input)) {
        return input;
    }
    const minDec = minDecimal === undefined ? 2 : minDecimal;
    const maxDec = maxDecimal === undefined ? 2 : maxDecimal;
    let amount = String(Math.round(+input * Math.pow(10, maxDec)) / Math.pow(10, maxDec));
    if (amount.indexOf('.') < 0 && minDec > 0) {
        amount += '.';
    }
    while (amount.length - amount.indexOf('.') <= minDec) {
        amount += '0';
    }
    const decimalSplit = amount.split('.');
    let integerNumber;
    let decimalNumber;
    // do we want to display our number with a custom separator in each thousand position
    if (thousandSeparator) {
        integerNumber = decimalSplit.length >= 1 ? thousandSeparatorFormatted(decimalSplit[0], thousandSeparator) : undefined;
    }
    else {
        integerNumber = decimalSplit.length >= 1 ? decimalSplit[0] : amount;
    }
    // when using a separator that is not a dot, replace it with the new separator
    if (decimalSplit.length > 1) {
        decimalNumber = decimalSplit[1];
    }
    let output = '';
    if (integerNumber !== undefined && decimalNumber !== undefined) {
        output = `${integerNumber}${decimalSeparator}${decimalNumber}`;
    }
    else if (integerNumber !== undefined && integerNumber !== null) {
        output = integerNumber;
    }
    return output;
}
/**
 * Format a number following options passed as arguments (decimals, separator, ...)
 * @param input
 * @param minDecimal
 * @param maxDecimal
 * @param wrapNegativeNumberInBraquets
 * @param symbolPrefix
 * @param symbolSuffix
 * @param decimalSeparator
 * @param thousandSeparator
 */
export function formatNumber(input, minDecimal, maxDecimal, wrapNegativeNumberInBraquets, symbolPrefix = '', symbolSuffix = '', decimalSeparator = '.', thousandSeparator = '') {
    if (isNaN(+input)) {
        return input;
    }
    const calculatedValue = Math.round(parseFloat(input) * 1000000) / 1000000;
    if (calculatedValue < 0) {
        const absValue = Math.abs(calculatedValue);
        if (wrapNegativeNumberInBraquets) {
            if (!isNaN(minDecimal) || !isNaN(maxDecimal)) {
                return `(${symbolPrefix}${decimalFormatted(absValue, minDecimal, maxDecimal, decimalSeparator, thousandSeparator)}${symbolSuffix})`;
            }
            const formattedValue = thousandSeparatorFormatted(`${absValue}`, thousandSeparator);
            return `(${symbolPrefix}${formattedValue}${symbolSuffix})`;
        }
        else {
            if (!isNaN(minDecimal) || !isNaN(maxDecimal)) {
                return `-${symbolPrefix}${decimalFormatted(absValue, minDecimal, maxDecimal, decimalSeparator, thousandSeparator)}${symbolSuffix}`;
            }
            const formattedValue = thousandSeparatorFormatted(`${absValue}`, thousandSeparator);
            return `-${symbolPrefix}${formattedValue}${symbolSuffix}`;
        }
    }
    else {
        if (!isNaN(minDecimal) || !isNaN(maxDecimal)) {
            return `${symbolPrefix}${decimalFormatted(input, minDecimal, maxDecimal, decimalSeparator, thousandSeparator)}${symbolSuffix}`;
        }
        const formattedValue = thousandSeparatorFormatted(`${input}`, thousandSeparator);
        return `${symbolPrefix}${formattedValue}${symbolSuffix}`;
    }
}
/**
 * When a queryFieldNameGetterFn is defined, then get the value from that getter callback function
 * @param {Column} columnDef
 * @param {Object} dataContext
 * @param {String} defaultValue - optional value to use if value isn't found in data context
 * @return outputValue
 */
export function getCellValueFromQueryFieldGetter(columnDef, dataContext, defaultValue) {
    if (typeof columnDef.queryFieldNameGetterFn === 'function') {
        const queryFieldName = columnDef.queryFieldNameGetterFn(dataContext);
        // get the cell value from the item or when it's a dot notation then exploded the item and get the final value
        if (queryFieldName?.indexOf('.') >= 0) {
            defaultValue = getDescendantProperty(dataContext, queryFieldName);
        }
        else {
            defaultValue = dataContext.hasOwnProperty(queryFieldName) ? dataContext[queryFieldName] : defaultValue;
        }
    }
    return defaultValue;
}
/**
 * From a dot (.) notation path, find and return a property within an object given a path
 * @param object - object input
 * @param path - path of the complex object, string with dot (.) notation
 * @returns outputValue - the object property value found if any
 */
export function getDescendantProperty(object, path) {
    if (!object || !path) {
        return object;
    }
    return path.split('.').reduce((obj, prop) => obj && obj[prop], object);
}
/** Get I18N Translation Prefix, defaults to an empty string */
export function getTranslationPrefix(gridOptions) {
    if (gridOptions?.translationNamespace) {
        return gridOptions.translationNamespace + (gridOptions.translationNamespaceSeparator || '');
    }
    return '';
}
/** From a column definition, find column type */
export function getColumnFieldType(columnDef) {
    return columnDef.outputType || columnDef.type || 'string';
}
/** Return all Date field types that exists in the library */
export function getAllDateFieldTypes() {
    return [
        'date',
        'dateTime',
        'dateIso',
        'dateTimeIso',
        'dateTimeShortIso',
        'dateTimeIsoAmPm',
        'dateTimeIsoAM_PM',
        'dateEuro',
        'dateEuroShort',
        'dateTimeEuro',
        'dateTimeShortEuro',
        'dateTimeEuroAmPm',
        'dateTimeEuroAM_PM',
        'dateTimeEuroShort',
        'dateTimeEuroShortAmPm',
        'dateTimeEuroShortAM_PM',
        'dateUs',
        'dateUsShort',
        'dateTimeUs',
        'dateTimeShortUs',
        'dateTimeUsAmPm',
        'dateTimeUsAM_PM',
        'dateTimeUsShort',
        'dateTimeUsShortAmPm',
        'dateTimeUsShortAM_PM',
        'dateUtc',
    ];
}
/** Verify if the identified column is of type Date */
export function isColumnDateType(fieldType) {
    if (getAllDateFieldTypes().includes(fieldType)) {
        return true;
    }
    return false;
}
/**
 * Mapper for query operators (ex.: `<=` → `LE`, `>` → `GT`, ...)
 * @param string operator
 * @returns string map
 */
export function mapOperatorType(operator) {
    let map;
    switch (operator) {
        case '<':
        case 'LT':
            map = 'LT';
            break;
        case '<=':
        case 'LE':
            map = 'LE';
            break;
        case '>':
        case 'GT':
            map = 'GT';
            break;
        case '>=':
        case 'GE':
            map = 'GE';
            break;
        case '!=':
        case 'NE':
            map = 'NE';
            break;
        case '*':
        case 'a*':
        case 'StartsWith':
            map = 'StartsWith';
            break;
        case '*z':
        case 'EndsWith':
            map = 'EndsWith';
            break;
        case '=':
        case '==':
        case 'EQ':
            map = 'EQ';
            break;
        case 'IN':
            map = 'IN';
            break;
        case 'NIN':
        case 'NOT_IN':
            map = 'NOT_IN';
            break;
        case '<>':
        case 'Not_Contains':
        case 'NOT_CONTAINS':
            map = 'Not_Contains';
            break;
        case 'Contains':
        case 'CONTAINS':
        default:
            map = 'Contains';
            break;
    }
    return map;
}
/**
 * Find equivalent short designation of an Operator Type or Operator String.
 * When using a Compound Filter, we use the short designation and so we need the mapped value.
 * For example `StartsWith` short designation is `a*`, then `GE` → `>=`, ...
 */
export function mapOperatorToShorthandDesignation(operator) {
    let shortOperator = '';
    switch (operator) {
        case 'GT':
        case '>':
            shortOperator = '>';
            break;
        case 'GE':
        case '>=':
            shortOperator = '>=';
            break;
        case 'LT':
        case '<':
            shortOperator = '<';
            break;
        case 'LE':
        case '<=':
            shortOperator = '<=';
            break;
        case 'NE':
        case '<>':
            shortOperator = '<>';
            break;
        case '=':
        case '==':
        case 'EQ':
            shortOperator = '=';
            break;
        case 'StartsWith':
        case 'a*':
        case '*':
            shortOperator = 'a*';
            break;
        case 'EndsWith':
        case '*z':
            shortOperator = '*z';
            break;
        default:
            // any other operator will be considered as already a short expression, so we can return same input operator
            shortOperator = operator;
            break;
    }
    return shortOperator;
}
/**
 * Mapper for query operator by a Filter Type
 * For example a multiple-select typically uses 'IN' operator
 * @param operator
 * @returns string map
 */
export function mapOperatorByFieldType(fieldType) {
    let map;
    if (isColumnDateType(fieldType)) {
        map = 'EQ';
    }
    else {
        switch (fieldType) {
            case 'unknown':
            case 'string':
            case 'text':
            case 'password':
            case 'readonly':
                map = 'Contains';
                break;
            case 'float':
            case 'number':
            default:
                map = 'EQ';
                break;
        }
    }
    return map;
}
/**
 * Format a number or a string into a string that is separated every thousand,
 * the default separator is a comma but user can optionally pass a different one
 * @param inputValue
 * @param separator default to comma ","
 * @returns string
 */
export function thousandSeparatorFormatted(inputValue, separator = ',') {
    if (inputValue !== null && inputValue !== undefined) {
        const stringValue = `${inputValue}`;
        const decimalSplit = stringValue.split('.');
        if (decimalSplit.length === 2) {
            return `${decimalSplit[0].replace(/\B(?=(\d{3})+(?!\d))/g, separator)}.${decimalSplit[1]}`;
        }
        return stringValue.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
    }
    return inputValue;
}
/**
 * Uses the logic function to find an item in an array or returns the default
 * value provided (empty object by default)
 * @param any[] array the array to filter
 * @param function logic the logic to find the item
 * @param any [defaultVal={}] the default value to return
 * @return object the found object or default value
 */
export function findOrDefault(array, logic, defaultVal = {}) {
    if (Array.isArray(array)) {
        return array.find(logic) || defaultVal;
    }
    return array;
}
/**
 * From an input that could be a Promise, an Observable, a Subject or a Fetch
 * @param {Promise<T> | Observable<T> | Subject<T>} input
 * @param {RxJsFacade} rxjs
 * @returns {Promise}
 */
export function fetchAsPromise(input, rxjs) {
    return new Promise((resolve, reject) => {
        if (Array.isArray(input)) {
            resolve(input);
        }
        else if (input instanceof Promise) {
            input
                .then((response) => {
                if (Array.isArray(response)) {
                    resolve(response); // from Promise
                }
                else if (response?.status >= 200 && response.status < 300 && typeof response.json === 'function') {
                    if (response.bodyUsed) {
                        const errorMsg = '[SlickGrid-Universal] The response body passed to Fetch was already read. ' +
                            'Either pass the dataset from the Response or clone the response first using response.clone()';
                        console.warn(errorMsg);
                        resolve(null);
                    }
                    else {
                        resolve(response.json()); // from Fetch
                    }
                }
                else if (response?.content) {
                    resolve(response['content']); // from http-client
                }
                else {
                    resolve(response); // anything else we'll just return "as-is"
                }
            })
                .catch((error) => reject(error));
        }
        else if (input && rxjs?.isObservable(input)) {
            resolve(castObservableToPromise(rxjs, input)); // Observable
        }
        else {
            resolve(null);
        }
    });
}
/**
 * Take all the columns defined in the datagrid and also the preset columns,
 * then sort by the original column definitions unless a different order is specified in the presets which is given precedence
 * @param allColumns
 * @param presetColumns
 * @returns
 */
export function sortPresetColumns(allColumns, presetColumns) {
    // Create a map of preset column IDs with their order and properties
    const presetColumnMap = new Map(presetColumns.map((col, index) => [col.id, { index, column: col }]));
    // Create a copy of allColumns with sorting metadata
    return allColumns
        .map((column, originalIndex) => {
        const presetColumnData = presetColumnMap.get(column.id);
        const presetColumn = presetColumnData?.column;
        // Merge properties from preset columns with original columns
        // Preset properties override original properties where they exist
        return {
            ...column,
            ...(presetColumn && {
                cssClass: presetColumn.cssClass ?? column.cssClass,
                headerCssClass: presetColumn.headerCssClass ?? column.headerCssClass,
                width: presetColumn.width ?? column.width,
                originalWidth: presetColumn.width,
            }),
            hidden: !presetColumnMap.has(column.id) || (presetColumns.find((c) => c.id === column.id)?.hidden ?? false),
            _originalIndex: originalIndex,
            _presetIndex: presetColumnData?.index,
        };
    })
        .sort((a, b) => {
        // If both columns have a preset index, sort by preset order
        if (a._presetIndex !== undefined && b._presetIndex !== undefined) {
            return a._presetIndex - b._presetIndex;
        }
        // If one column is in presets and the other is not, preserve original order
        return a._originalIndex - b._originalIndex;
    })
        .map(({ _originalIndex, _presetIndex, ...column }) => column);
}
/**
 * Unsubscribe all Subscriptions
 * It will return an empty array if it all went well
 * @param subscriptions
 */
export function unsubscribeAll(subscriptions) {
    if (Array.isArray(subscriptions)) {
        while (subscriptions.length > 0) {
            const subscription = subscriptions.pop();
            if (subscription?.unsubscribe) {
                subscription.unsubscribe();
            }
        }
    }
    return subscriptions;
}
//# sourceMappingURL=utilities.js.map