<!-- START_PACKAGE_OG_IMAGE_PLACEHOLDER -->

<a href="https://www.anolilab.com/open-source" align="center">

  <img src="__assets__/package-og.svg" alt="deep-clone" />

</a>

<h3 align="center">Fastest deep clone implementation.</h3>

<!-- END_PACKAGE_OG_IMAGE_PLACEHOLDER -->

<br />

<div align="center">

[![typescript-image][typescript-badge]][typescript-url]
[![mit licence][license-badge]][license]
[![npm downloads][npm-downloads-badge]][npm-downloads]
[![Chat][chat-badge]][chat]
[![PRs Welcome][prs-welcome-badge]][prs-welcome]

</div>

---

<div align="center">
    <p>
        <sup>
            Daniel Bannert's open source work is supported by the community on <a href="https://github.com/sponsors/prisis">GitHub Sponsors</a>
        </sup>
    </p>
</div>

---

## Install

```sh
npm install @visulima/deep-clone
```

```sh
yarn add @visulima/deep-clone
```

```sh
pnpm add @visulima/deep-clone
```

## Usage

Copy or deep clone an input value to an arbitrary depth. The function accepts both objects and primitives.

```typescript
import { deepClone } from "@visulima/deep-clone";

const cloned = deepClone({ a: 1, b: { c: 2 } });

console.log(cloned); // => {a: 1, b: {c: 2}}
```

### createDeepClone(options?)

For hot loops that clone many values with the same configuration, create a pre-configured cloner once and reuse it. This resolves the handler table a single time instead of on every call.

```typescript
import { createDeepClone } from "@visulima/deep-clone";

const cloneStrict = createDeepClone({ strict: true });

const a = cloneStrict({ a: 1 });
const b = cloneStrict({ b: 2 });
```

## API

### deepClone(input, options?)

#### input

Type: `any`

The input value to copy.

#### options

Type: `object`

##### strict

Type: `boolean`<br>

Default: `false`

If `true`, it will copy all properties, including non-enumerable ones and symbols.

##### handler

Type: `object` (all keys optional)

A set of custom handlers for specific types of value. Each handler is a function `(value, state) => value` that takes the original value and returns a new value (or throws if the value is not supported). You only need to provide the keys you want to override — the rest fall back to the built-in defaults:

```typescript
import { deepClone } from "@visulima/deep-clone";

// Override just the Date handler; everything else uses the defaults.
const cloned = deepClone(
    { when: new Date() },
    {
        handler: {
            Date: (date) => new Date(date.getTime() + 1000),
        },
    },
);
```

Supported handler keys (each is an `InternalHandler<T>`):

- `Array`
- `ArrayBuffer`
- `Blob`
- `DataView`
- `Date`
- `Error`
- `File`
- `Function`
- `Map`
- `Object`
- `Promise`
- `RegExp`
- `Set`
- `SharedArrayBuffer`
- `WeakMap`
- `WeakSet`

> Note: typed arrays (`Uint8Array`, `Float64Array`, …) are always cloned through the `ArrayBuffer` handler, so there are no dedicated typed-array keys.

## Utils

### copyOwnProperties(input)

Copy all properties contained on the object.

```typescript
import { copyOwnProperties } from "@visulima/deep-clone/utils";

const obj = { a: 1, b: 2 };

const copy = copyOwnProperties(obj);

console.log(copy); // => {a: 1, b: 2}
```

### getCleanClone(input)

Get an empty version of the object with the same prototype it has.

```typescript
import { getCleanClone } from "@visulima/deep-clone/utils";

const obj = { a: 1, b: 2 };

const clean = getCleanClone(obj);

console.log(clean); // => {}
```

## Notes

- List of **supported** values/types:
    - `undefined` (original value is returned)
    - `null` (original value is returned)
    - `boolean`/`Boolean` (original value is returned)
    - `string`/`String` (original value is returned)
    - `number`/`Number` (original value is returned)
    - `function`
    - `Object`
    - `Date`
    - `RegExp`
    - `Set`
    - `Map`
    - [`Error`][mdn-error]
    - [`URIError`][mdn-uri-error]
    - [`ReferenceError`][mdn-reference-error]
    - [`SyntaxError`][mdn-syntax-error]
    - [`RangeError`][mdn-range-error]
    - [`EvalError`][mdn-eval-error]
    - [`TypeError`][mdn-type-error]
    - [`System Error`][node-system-error] (Node.js)
    - `Array`
    - `Int8Array`
    - `Uint8Array`
    - `Uint8ClampedArray`
    - `Init16Array`
    - `Uint16Array`
    - `Int32Array`
    - `Uint32Array`
    - `Float32Array`
    - `Float64Array`
    - `Buffer` ([Node.js][node-buffer])
    - `DataView`
    - `Blob`
    - `File` (preserves `name` and `lastModified`)

- List of **unsupported** values/types:
    - `DOMElement`: to copy DOM elements, use `element.cloneNode()`.
    - `Symbol`
    - `WeakMap`
    - `WeakSet`
    - `FileList`
    - `ImageData`
    - `ImageBitmap`
    - `Promise`
    - `SharedArrayBuffer`

- The implementation **can** handle circular references.
- If a `Number`, `String`, or `Boolean` object is encountered, the value is cloned as a **primitive**. This behavior is intentional. The implementation is opinionated in wanting to **avoid** creating `numbers`, `strings`, and `booleans` via the `new` operator and a constructor.
- The implementation **only** checks whether basic `Objects`, `Arrays`, and class instances are `extensible`, `sealed`, and/or `frozen`.
- `functions` are **not** cloned; their reference is copied.
- The implementation supports custom [`error`][mdn-error] types which are [`Error`][mdn-error] instances (e.g., ES2015 subclasses).

## Benchmarks

> **Note:** Run `pnpm --filter "deep-clone-bench" run test:bench` to run the benchmarks yourself.

### Shallow Clone Performance

Performance comparison for shallow object cloning `{ a: "a", b: "b", c: "c" }`:

| Library                          | Operations/sec | Relative Performance |
| -------------------------------- | -------------- | -------------------- |
| **plain-object-clone**           | 5,650,888      | **1.00x** (fastest)  |
| fast-copy                        | 5,056,607      | 0.89x                |
| rfdc - default                   | 4,674,249      | 0.83x                |
| deep-copy                        | 4,522,595      | 0.80x                |
| rfdc - proto                     | 4,371,388      | 0.77x                |
| nanoclone                        | 4,282,256      | 0.76x                |
| **@visulima/deep-clone - loose** | 3,551,940      | 0.63x                |
| clone-deep                       | 3,740,025      | 0.66x                |
| nano-copy                        | 3,673,002      | 0.65x                |
| rfdc - circles and proto         | 3,423,507      | 0.61x                |
| rfdc - circles                   | 3,235,513      | 0.57x                |
| ramda.clone                      | 2,514,785      | 0.44x                |
| lodash.clonedeep                 | 2,236,361      | 0.40x                |
| structured-clone                 | 1,503,190      | 0.27x                |
| @mfederczuk/deeptools            | 1,305,034      | 0.23x                |
| @ungap/structured-clone          | 1,231,364      | 0.22x                |
| fast-copy strict                 | 1,196,932      | 0.21x                |
| @visulima/deep-clone - strict    | 930,414        | 0.16x                |

### Deep Clone Performance

Performance comparison for deep object cloning (complex nested structure):

| Library                          | Operations/sec | Relative Performance |
| -------------------------------- | -------------- | -------------------- |
| **rfdc - proto**                 | 412,630        | **1.00x** (fastest)  |
| rfdc - default                   | 400,110        | 0.97x                |
| rfdc - circles                   | 369,861        | 0.90x                |
| rfdc - circles and proto         | 367,846        | 0.89x                |
| nanoclone                        | 314,105        | 0.76x                |
| deep-copy                        | 301,609        | 0.73x                |
| plain-object-clone               | 249,603        | 0.60x                |
| nano-copy                        | 248,646        | 0.60x                |
| fast-copy                        | 228,871        | 0.55x                |
| clone-deep                       | 217,880        | 0.53x                |
| **@visulima/deep-clone - loose** | 194,650        | 0.47x                |
| lodash.clonedeep                 | 130,934        | 0.32x                |
| structured-clone                 | 90,495         | 0.22x                |
| @ungap/structured-clone          | 88,710         | 0.21x                |
| ramda.clone                      | 58,385         | 0.14x                |
| fast-copy strict                 | 34,771         | 0.08x                |
| @visulima/deep-clone - strict    | 33,065         | 0.08x                |
| @mfederczuk/deeptools            | 15,357         | 0.04x                |

### Important Notes

- **@visulima/deep-clone** provides the best balance between performance and feature completeness
- **Loose mode** (default): Fast performance with standard cloning behavior
- **Strict mode**: Slower but clones all properties including non-enumerable ones and symbols
- Libraries like `plain-object-clone` and `fastest-json-copy` are faster but have significant limitations:
    - Cannot handle circular references
    - Treat complex objects (Date, Map, etc.) as plain objects
    - Limited type support
- **rfdc** is very fast but has fewer features than @visulima/deep-clone

## Supported Node.js Versions

Libraries in this ecosystem make the best effort to track [Node.js’ release schedule](https://github.com/nodejs/release#release-schedule).
Here’s [a post on why we think this is important](https://medium.com/the-node-js-collection/maintainers-should-consider-following-node-js-release-schedule-ab08ed4de71a).

## Contributing

If you would like to help take a look at the [list of issues](https://github.com/visulima/visulima/issues) and check our [Contributing](.github/CONTRIBUTING.md) guild.

> **Note:** please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.

## Credits

- [Daniel Bannert](https://github.com/prisis)
- [All Contributors](https://github.com/visulima/visulima/graphs/contributors)

## Made with ❤️ at Anolilab

This is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Anolilab](https://www.anolilab.com/open-source) is a Development and AI Studio. Contact us at [hello@anolilab.com](mailto:hello@anolilab.com) if you need any help with these technologies or just want to say hi!

## License

The visulima deep-clone is open-sourced software licensed under the [MIT][license]

<!-- badges -->

[license-badge]: https://img.shields.io/npm/l/@visulima/deep-clone?style=for-the-badge
[license]: https://github.com/visulima/visulima/blob/main/LICENSE
[npm-downloads-badge]: https://img.shields.io/npm/dm/@visulima/deep-clone?style=for-the-badge
[npm-downloads]: https://www.npmjs.com/package/@visulima/deep-clone
[prs-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge
[prs-welcome]: https://github.com/visulima/visulima/blob/main/.github/CONTRIBUTING.md
[chat-badge]: https://img.shields.io/discord/932323359193186354.svg?style=for-the-badge
[chat]: https://discord.gg/TtFJY8xkFK
[typescript-badge]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
[typescript-url]: https://www.typescriptlang.org/
