/* IMPORT */
import { debounce } from 'dettle';
import fs from 'node:fs';
import path from 'node:path';
import sfs from 'stubborn-fs';
import readdir from 'tiny-readdir';
import { POLLING_TIMEOUT } from './constants.js';
/* MAIN */
const Utils = {
    /* LANG API */
    lang: {
        debounce,
        attempt: (fn) => {
            try {
                return fn();
            }
            catch (error) {
                return Utils.lang.castError(error);
            }
        },
        castArray: (x) => {
            return Utils.lang.isArray(x) ? x : [x];
        },
        castError: (exception) => {
            if (Utils.lang.isError(exception))
                return exception;
            if (Utils.lang.isString(exception))
                return new Error(exception);
            return new Error('Unknown error');
        },
        defer: (callback) => {
            return setTimeout(callback, 0);
        },
        isArray: (value) => {
            return Array.isArray(value);
        },
        isError: (value) => {
            return value instanceof Error;
        },
        isFunction: (value) => {
            return typeof value === 'function';
        },
        isNaN: (value) => {
            return Number.isNaN(value);
        },
        isNumber: (value) => {
            return typeof value === 'number';
        },
        isPrimitive: (value) => {
            if (value === null)
                return true;
            const type = typeof value;
            return type !== 'object' && type !== 'function';
        },
        isShallowEqual: (x, y) => {
            if (x === y)
                return true;
            if (Utils.lang.isNaN(x))
                return Utils.lang.isNaN(y);
            if (Utils.lang.isPrimitive(x) || Utils.lang.isPrimitive(y))
                return x === y;
            for (const i in x)
                if (!(i in y))
                    return false;
            for (const i in y)
                if (x[i] !== y[i])
                    return false;
            return true;
        },
        isSet: (value) => {
            return value instanceof Set;
        },
        isString: (value) => {
            return typeof value === 'string';
        },
        isUndefined: (value) => {
            return value === undefined;
        },
        noop: () => {
            return;
        },
        uniq: (arr) => {
            if (arr.length < 2)
                return arr;
            return Array.from(new Set(arr));
        }
    },
    /* FS API */
    fs: {
        getDepth: (targetPath) => {
            return Math.max(0, targetPath.split(path.sep).length - 1);
        },
        getRealPath: (targetPath, native) => {
            try {
                return native ? fs.realpathSync.native(targetPath) : fs.realpathSync(targetPath);
            }
            catch {
                return;
            }
        },
        isSubPath: (targetPath, subPath) => {
            return (subPath.startsWith(targetPath) && subPath[targetPath.length] === path.sep && (subPath.length - targetPath.length) > path.sep.length);
        },
        poll: (targetPath, timeout = POLLING_TIMEOUT) => {
            return sfs.retry.stat(timeout)(targetPath, { bigint: true }).catch(Utils.lang.noop);
        },
        readdir: async (rootPath, ignore, depth = Infinity, limit = Infinity, signal, readdirMap) => {
            if (readdirMap && depth === 1 && rootPath in readdirMap) { // Reusing cached data
                const result = readdirMap[rootPath];
                return [result.directories, result.files];
            }
            else { // Retrieving fresh data
                const result = await readdir(rootPath, { depth, limit, ignore, signal });
                return [result.directories, result.files];
            }
        }
    }
};
/* EXPORT */
export default Utils;
