UNPKG

@elsikora/cladi

Version:

Zero-dependency TypeScript DI toolkit with typed tokens and scoped lifecycles.

130 lines (127 loc) 4.75 kB
'use strict'; /** * Utility method to safely deep clone objects that may contain functions. * Unlike structuredClone, this can handle functions by preserving their reference * but cloning everything else. * @param {T} source Object to clone * @returns {T} A deep clone of the object * @template T Type of object to clone */ function safeDeepClone(source) { return cloneRecursive(source, new WeakMap()); } /** * Clones DataView and typed array instances. * @param {ArrayBufferView} source Typed array or DataView to clone. * @returns {ArrayBufferView} Cloned buffer view. */ function cloneArrayBufferView(source) { if (source instanceof DataView) { const clonedDataViewBuffer = source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength); return new DataView(clonedDataViewBuffer); } const typedArrayConstructor = source.constructor; return new typedArrayConstructor(source); } /** * Recursively clones values while preserving circular references. * @param {T} source Source value to clone. * @param {WeakMap<object, unknown>} references Previously cloned references. * @returns {T} Cloned value. */ function cloneRecursive(source, references) { if (source == null || typeof source !== "object") { return source; } const sourceObject = source; const cachedClone = references.get(sourceObject); if (cachedClone !== undefined) { return cachedClone; } if (source instanceof Date) { return new Date(source); } if (source instanceof RegExp) { return new RegExp(source.source, source.flags); } if (source instanceof ArrayBuffer) { const clonedArrayBuffer = Uint8Array.from(new Uint8Array(source)).buffer; references.set(sourceObject, clonedArrayBuffer); return clonedArrayBuffer; } if (isSharedArrayBuffer(source)) { const clonedSharedArrayBuffer = new SharedArrayBuffer(source.byteLength); new Uint8Array(clonedSharedArrayBuffer).set(new Uint8Array(source)); references.set(sourceObject, clonedSharedArrayBuffer); return clonedSharedArrayBuffer; } const bufferApi = getBufferApi(); if (bufferApi?.isBuffer(source)) { const clonedBuffer = bufferApi.from(source); references.set(sourceObject, clonedBuffer); return clonedBuffer; } if (ArrayBuffer.isView(source)) { const clonedArrayBufferView = cloneArrayBufferView(source); references.set(sourceObject, clonedArrayBufferView); return clonedArrayBufferView; } if (Array.isArray(source)) { const clonedArray = []; references.set(sourceObject, clonedArray); for (const item of source) { clonedArray.push(cloneRecursive(item, references)); } return clonedArray; } if (source instanceof Map) { const clonedMap = new Map(); references.set(sourceObject, clonedMap); for (const [key, value] of source.entries()) { clonedMap.set(cloneRecursive(key, references), cloneRecursive(value, references)); } return clonedMap; } if (source instanceof Set) { const clonedSet = new Set(); references.set(sourceObject, clonedSet); for (const value of source.values()) { clonedSet.add(cloneRecursive(value, references)); } return clonedSet; } const prototype = Object.getPrototypeOf(sourceObject); const clonedObject = Object.create(prototype); references.set(sourceObject, clonedObject); for (const key of Reflect.ownKeys(sourceObject)) { const descriptor = Object.getOwnPropertyDescriptor(sourceObject, key); if (!descriptor) { continue; } if ("value" in descriptor) { const clonedValue = cloneRecursive(descriptor.value, references); Object.defineProperty(clonedObject, key, { ...descriptor, value: clonedValue }); continue; } Object.defineProperty(clonedObject, key, descriptor); } return clonedObject; } /** * Returns Buffer API when running in Node.js. * @returns {TBufferApi | undefined} Node Buffer helpers or undefined. */ function getBufferApi() { const globalWithBuffer = globalThis; return globalWithBuffer.Buffer; } /** * Checks whether value is a SharedArrayBuffer instance. * @param {unknown} value Value to inspect. * @returns {value is SharedArrayBuffer} True when value is SharedArrayBuffer. */ function isSharedArrayBuffer(value) { return typeof SharedArrayBuffer !== "undefined" && value instanceof SharedArrayBuffer; } exports.safeDeepClone = safeDeepClone; //# sourceMappingURL=safe-deep-clone.utility.js.map