https://github.com/y14e/bunshin-clone
High-performance deep clone utility with descriptor support. Supports circular ref and complex built-in types.
https://github.com/y14e/bunshin-clone
circular-reference clone deep-clone property-descriptor structured-clone typescript utility
Last synced: 1 day ago
JSON representation
High-performance deep clone utility with descriptor support. Supports circular ref and complex built-in types.
- Host: GitHub
- URL: https://github.com/y14e/bunshin-clone
- Owner: y14e
- License: mit
- Created: 2026-04-24T00:19:39.000Z (3 days ago)
- Default Branch: main
- Last Pushed: 2026-04-24T01:42:05.000Z (3 days ago)
- Last Synced: 2026-04-24T02:28:07.282Z (3 days ago)
- Topics: circular-reference, clone, deep-clone, property-descriptor, structured-clone, typescript, utility
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/bunshin-clone
- Size: 30.3 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Bunshin Clone
High-performance deep clone utility with descriptor support. Supports circular references and complex built-in types.
* ⚡ Fast (no unnecessary overhead)
* ♻️ Deep clone (no structural sharing)
* 🔁 Supports circular ref
* 🧠 Handles Map, Set, Array, TypedArray, Date, RegExp, etc.
* 🧩 Optional descriptor preservation
---
## Usage
```ts
import bunshinClone from 'bunshin-clone';
const source = { foo: 1, nested: { x: 1 } };
const result = bunshinClone(source);
console.log(result);
// { foo: 1, nested: { x: 1 } }
console.log(result === source); // false
console.log(result.nested === source.nested); // false
```
## API
```ts
bunshinClone(target)
bunshinClone(target, options)
```
## Options
```ts
interface BunshinCloneOptions {
preserveDescriptors?: boolean;
strictDescriptors?: boolean;
}
```
### preserveDescriptors
* `false` (default): use standard merge (faster, ignores property descriptors)
* `true`: preserve property descriptors (getters/setters, etc.)
Example
```ts
const source = {};
Object.defineProperty(source, 'x', {
get: () => 42,
enumerable: true,
});
const result = bunshinClone(source, { preserveDescriptors: true });
Object.getOwnPropertyDescriptor(result, 'x')?.get;
// => function
```
### strictDescriptors
* `false` (default): skip incompatible descriptors
* `true`: throw if descriptor cannot be merged (e.g. non-configurable or non-writable)
Example
```ts
Object.freeze(obj);
bunshinClone(obj, { strictDescriptors: true });
// => may throw TypeError
```
## Supported Types
bunshin-clone correctly handles:
* Object (plain + prototype preserved)
* Array
* Map
* Set
* Date
* RegExp
* ArrayBuffer
* DataView
* TypedArray (Uint8Array, etc.)
* Error / DOMException
* Blob
* ImageData
* URL
* URLSearchParams
## Circular ref
```ts
const a: any = { x: 1 };
a.self = a;
const result = bunshinClone(a);
result.self === result; // true
```
## Descriptor Behavior
### Default (fast path)
```ts
const source = {
get x() {
return 42;
}
};
const result = bunshinClone(source);
result.x; // 42
// getter is NOT preserved
```
### preserveDescriptors: true
```ts
const source = {};
Object.defineProperty(source, 'x', {
get: () => 42,
enumerable: true,
});
const result = bunshinClone(source, {
preserveDescriptors: true,
});
Object.getOwnPropertyDescriptor(result, 'x')?.get;
// => preserved
```
### Unsupported / Pass-through Types
Some values are returned as-is:
* Function
* WeakMap / WeakSet
* Proxy (not cloned)
* Other non-cloneable host objects
```ts
const fn = () => {};
bunshinClone(fn) === fn; // true
```
## Design Notes
### Deep clone (no structural sharing)
Unlike merge utilities, bunshin-clone always produces a new structure:
```ts
const source = { a: { b: 1 } };
const result = bunshinClone(source);
result !== source; // true
result.a !== source.a; // true
```
### Getter / Setter behavior
* Default: evaluated and converted to value
* preserveDescriptors: preserved as-is
### Descriptor safety
When `preserveDescriptors` is enabled:
* descriptors are cloned safely
* original object is never mutated
* errors are controlled via `strictDescriptors`
## Performance
* No proxy / no diffing
* Minimal branching
* Fast path for plain objects and arrays
* Competitive with structuredClone in many cases
## Comparison
| Feature | Bunshin Clone | structuredClone | lodash.clonedeep |
|---------------------|--------------|----------------|------------------|
| Circular refs | ✅ | ✅ | ✅ |
| Map / Set | ✅ | ✅ | ⚠️ (partial) |
| TypedArray | ✅ | ✅ | ⚠️ (shallow) |
| Descriptor support | ✅ | ❌ | ❌ |
| Functions | pass-through | ❌ (throws) | pass-through |
| Prototype preserved | ✅ | ❌ | ⚠️ |
| Custom control | ✅ | ❌ | ❌ |
| Performance | ⚡ fast | ⚡ fast | 🐢 slower |