import type { BinaryFileType } from './Interfaces'; export type Primitives = string | number | boolean | bigint | symbol | null | undefined; /* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */ export const deepCopy = string }) | Primitives>( source: T, hash = new WeakMap(), path = '', ): T => { const hasOwnProp = Object.prototype.hasOwnProperty.bind(source); // Primitives & Null & Function if (typeof source !== 'object' || source === null || typeof source === 'function') { return source; } // Date and other objects with toJSON method // TODO: remove this when other code parts not expecting objects with `.toJSON` method called and add back checking for Date and cloning it properly if (typeof source.toJSON === 'function') { return source.toJSON() as T; } if (hash.has(source)) { return hash.get(source); } // Array if (Array.isArray(source)) { const clone = []; const len = source.length; for (let i = 0; i < len; i++) { clone[i] = deepCopy(source[i], hash, path + `[${i}]`); } return clone as T; } // Object const clone = Object.create(Object.getPrototypeOf({})); hash.set(source, clone); for (const i in source) { if (hasOwnProp(i)) { clone[i] = deepCopy((source as any)[i], hash, path + `.${i}`); } } return clone; }; // eslint-enable type MutuallyExclusive = | (T & { [k in Exclude]?: never }) | (U & { [k in Exclude]?: never }); type JSONParseOptions = MutuallyExclusive<{ errorMessage: string }, { fallbackValue: T }>; export const jsonParse = (jsonString: string, options?: JSONParseOptions): T => { try { return JSON.parse(jsonString) as T; } catch (error) { if (options?.fallbackValue !== undefined) { return options.fallbackValue; } else if (options?.errorMessage) { throw new Error(options.errorMessage); } throw error; } }; type JSONStringifyOptions = { replaceCircularRefs?: boolean; }; const replaceCircularReferences = (value: T, knownObjects = new WeakSet()): T => { if (typeof value !== 'object' || value === null || value instanceof RegExp) return value; if ('toJSON' in value && typeof value.toJSON === 'function') return value.toJSON() as T; if (knownObjects.has(value)) return '[Circular Reference]' as T; knownObjects.add(value); const copy = (Array.isArray(value) ? [] : {}) as T; for (const key in value) { copy[key] = replaceCircularReferences(value[key], knownObjects); } knownObjects.delete(value); return copy; }; export const jsonStringify = (obj: unknown, options: JSONStringifyOptions = {}): string => { return JSON.stringify(options?.replaceCircularRefs ? replaceCircularReferences(obj) : obj); }; export const sleep = async (ms: number): Promise => new Promise((resolve) => { setTimeout(resolve, ms); }); export function fileTypeFromMimeType(mimeType: string): BinaryFileType | undefined { if (mimeType.startsWith('application/json')) return 'json'; if (mimeType.startsWith('image/')) return 'image'; if (mimeType.startsWith('video/')) return 'video'; if (mimeType.startsWith('text/')) return 'text'; return; } export function assert(condition: T, msg?: string): asserts condition { if (!condition) { const error = new Error(msg ?? 'Invalid assertion'); // hide assert stack frame if supported if (Error.hasOwnProperty('captureStackTrace')) { // V8 only - https://nodejs.org/api/errors.html#errors_error_capturestacktrace_targetobject_constructoropt Error.captureStackTrace(error, assert); } else if (error.stack) { // fallback for IE and Firefox error.stack = error.stack .split('\n') .slice(1) // skip assert function from stack frames .join('\n'); } throw error; } }