forked from OctoWoW/OctoLauncher
5dca94a3fc
Manifest-based CDN updater and mod manager for the OctoWoW 1.12.1 client: launcher-owned realmlist, torrent-backed content sync with bundled aria2c, antivirus and Defender exclusion handling, hardware-aware render distance, optional client tweaks and mods, and the in-launcher news feed.
83 lines
1.9 KiB
TypeScript
83 lines
1.9 KiB
TypeScript
type Path = readonly (string | number)[];
|
|
|
|
const isUnsafeKey = (key: string | number) =>
|
|
key === '__proto__' || key === 'constructor' || key === 'prototype';
|
|
|
|
export const nestedGet = <T>(object: unknown, path: Path) =>
|
|
path.reduce(
|
|
(obj, key) => (isUnsafeKey(key) ? undefined : obj?.[key]),
|
|
object
|
|
) as T;
|
|
|
|
export const nestedSet = (obj: any, path: Path, value: unknown) => {
|
|
const [key, ...rest] = path;
|
|
if (isUnsafeKey(key)) return;
|
|
|
|
if (path.length === 1) {
|
|
obj[key] = value;
|
|
return;
|
|
}
|
|
|
|
if (obj[key] === undefined) {
|
|
obj[key] = typeof rest[0] === 'number' ? [] : {};
|
|
}
|
|
|
|
nestedSet(obj[key], rest as never, value);
|
|
};
|
|
|
|
export const asyncReduce = async <T, U>(
|
|
arr: T[],
|
|
reducer: (acc: U, cur: T) => Promise<U>,
|
|
init: U
|
|
): Promise<U> => {
|
|
let acc: U = init;
|
|
for (const i of arr) acc = await reducer(acc, i);
|
|
return acc;
|
|
};
|
|
|
|
export const asyncMap = async <T, U>(
|
|
arr: T[],
|
|
map: (cur: T) => Promise<U>
|
|
): Promise<U[]> => {
|
|
const acc: U[] = [];
|
|
for (const i of arr) acc.push(await map(i));
|
|
return acc;
|
|
};
|
|
|
|
export const isNotUndef = <T>(obj: T): obj is Exclude<T, undefined> =>
|
|
obj !== undefined;
|
|
|
|
export const formatFileSize = (bytes: number, decimals = 2) => {
|
|
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
let size = bytes;
|
|
let unitIndex = 0;
|
|
|
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
size /= 1024;
|
|
unitIndex++;
|
|
}
|
|
|
|
return `${parseFloat(size.toFixed(decimals))} ${units[unitIndex]}`;
|
|
};
|
|
|
|
export const formatDuration = (remaining: number) => {
|
|
const hours = Math.floor(remaining / 3600);
|
|
const minutes = Math.floor((remaining % 3600) / 60);
|
|
const seconds = Math.floor(remaining % 60);
|
|
|
|
return `${hours ? `${hours}h ` : ''}${
|
|
minutes ? `${minutes}m ` : ''
|
|
}${seconds}s`;
|
|
};
|
|
|
|
export const omit = <T extends object, const K extends keyof T>(
|
|
obj: T,
|
|
keys: K[]
|
|
): Omit<T, K> => {
|
|
const result = { ...obj };
|
|
keys.forEach(key => {
|
|
delete result[key];
|
|
});
|
|
return result;
|
|
};
|