bunshin-clone 2.0.0 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -153
- package/dist/index.bundle.cjs +359 -0
- package/dist/index.bundle.js +353 -0
- package/dist/index.cjs +5 -17
- package/dist/index.d.cts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +5 -17
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -37,7 +37,6 @@ interface BunshinCloneOptions {
|
|
|
37
37
|
preserveBufferSharing: boolean; // default: false
|
|
38
38
|
preserveDescriptors: boolean; // default: false
|
|
39
39
|
preserveSymbolKeys: boolean; // default: false
|
|
40
|
-
strictDescriptors: boolean; // default: false
|
|
41
40
|
}
|
|
42
41
|
```
|
|
43
42
|
|
|
@@ -52,155 +51,3 @@ If `true`, preserves property descriptors, including non-enumerable properties.
|
|
|
52
51
|
### `preserveSymbolKeys`
|
|
53
52
|
|
|
54
53
|
If `true`, preserves symbol keys (slower).
|
|
55
|
-
|
|
56
|
-
### `strictDescriptors`
|
|
57
|
-
|
|
58
|
-
If `true`, throws if property descriptor cannot be copied (e.g. non-configurable or non-writable).
|
|
59
|
-
|
|
60
|
-
## 📖 Details
|
|
61
|
-
|
|
62
|
-
<details>
|
|
63
|
-
<summary>Read more</summary>
|
|
64
|
-
|
|
65
|
-
### Example
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
const source = { foo: 1, nested: { x: 1 } };
|
|
69
|
-
|
|
70
|
-
const result = bunshinClone(source);
|
|
71
|
-
|
|
72
|
-
console.log(result);
|
|
73
|
-
// { foo: 1, nested: { x: 1 } }
|
|
74
|
-
|
|
75
|
-
console.log(result === source); // false
|
|
76
|
-
console.log(result.nested === source.nested); // false
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### Supported Types
|
|
80
|
-
|
|
81
|
-
bunshin-clone correctly handles:
|
|
82
|
-
|
|
83
|
-
- Object (plain + prototype preserved)
|
|
84
|
-
- Array
|
|
85
|
-
- Map
|
|
86
|
-
- Set
|
|
87
|
-
- Date
|
|
88
|
-
- RegExp
|
|
89
|
-
- ArrayBuffer
|
|
90
|
-
- DataView
|
|
91
|
-
- TypedArray (Uint8Array, etc.)
|
|
92
|
-
- DOMException / Error
|
|
93
|
-
- Blob
|
|
94
|
-
- ImageData
|
|
95
|
-
- URL
|
|
96
|
-
- URLSearchParams
|
|
97
|
-
|
|
98
|
-
### Circular ref
|
|
99
|
-
|
|
100
|
-
```ts
|
|
101
|
-
const a: any = { x: 1 };
|
|
102
|
-
a.self = a;
|
|
103
|
-
|
|
104
|
-
const result = bunshinClone(a);
|
|
105
|
-
|
|
106
|
-
result.self === result; // true
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Descriptor Behavior
|
|
110
|
-
|
|
111
|
-
#### Default (fast path)
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
const source = {
|
|
115
|
-
get x() {
|
|
116
|
-
return 42;
|
|
117
|
-
},
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const result = bunshinClone(source);
|
|
121
|
-
|
|
122
|
-
result.x; // 42
|
|
123
|
-
// getter is NOT preserved
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
#### preserveDescriptors: true
|
|
127
|
-
|
|
128
|
-
```ts
|
|
129
|
-
const source = {};
|
|
130
|
-
Object.defineProperty(source, 'x', {
|
|
131
|
-
get: () => 42,
|
|
132
|
-
enumerable: true,
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
const result = bunshinClone(source, {
|
|
136
|
-
preserveDescriptors: true,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
Object.getOwnPropertyDescriptor(result, 'x')?.get;
|
|
140
|
-
// => preserved
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
#### Unsupported / Pass-through Types
|
|
144
|
-
|
|
145
|
-
Some values are returned as-is:
|
|
146
|
-
|
|
147
|
-
- Function
|
|
148
|
-
- WeakMap / WeakSet
|
|
149
|
-
- Proxy (not cloned)
|
|
150
|
-
- Other non-cloneable host objects
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
const fn = () => {};
|
|
154
|
-
|
|
155
|
-
bunshinClone(fn) === fn; // true
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Design Notes
|
|
159
|
-
|
|
160
|
-
#### Deep clone (no structural sharing)
|
|
161
|
-
|
|
162
|
-
Unlike merge utilities, bunshin-clone always produces a new structure:
|
|
163
|
-
|
|
164
|
-
```ts
|
|
165
|
-
const source = { a: { b: 1 } };
|
|
166
|
-
|
|
167
|
-
const result = bunshinClone(source);
|
|
168
|
-
|
|
169
|
-
result !== source; // true
|
|
170
|
-
result.a !== source.a; // true
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
#### Getter / Setter behavior
|
|
174
|
-
|
|
175
|
-
- Default: evaluated and converted to value
|
|
176
|
-
- preserveDescriptors: preserved as-is
|
|
177
|
-
|
|
178
|
-
#### Descriptor safety
|
|
179
|
-
|
|
180
|
-
When `preserveDescriptors` is enabled:
|
|
181
|
-
|
|
182
|
-
- descriptors are cloned safely
|
|
183
|
-
- original object is never mutated
|
|
184
|
-
- errors are controlled via `strictDescriptors`
|
|
185
|
-
|
|
186
|
-
### Performance
|
|
187
|
-
|
|
188
|
-
- No proxy / no diffing
|
|
189
|
-
- Minimal branching
|
|
190
|
-
- Fast path for plain objects and arrays
|
|
191
|
-
- Competitive with structuredClone in many cases
|
|
192
|
-
|
|
193
|
-
### Comparison
|
|
194
|
-
|
|
195
|
-
| Feature | Bunshin Clone | structuredClone | lodash.clonedeep |
|
|
196
|
-
| ------------------- | ------------- | --------------- | ---------------- |
|
|
197
|
-
| Circular refs | ✅ | ✅ | ✅ |
|
|
198
|
-
| Map / Set | ✅ | ✅ | ⚠️ (partial) |
|
|
199
|
-
| TypedArray | ✅ | ✅ | ⚠️ (shallow) |
|
|
200
|
-
| Descriptor support | ✅ | ❌ | ❌ |
|
|
201
|
-
| Functions | pass-through | ❌ (throws) | pass-through |
|
|
202
|
-
| Prototype preserved | ✅ | ❌ | ⚠️ |
|
|
203
|
-
| Custom control | ✅ | ❌ | ❌ |
|
|
204
|
-
| Performance | ⚡ fast | ⚡ fast | 🐢 slower |
|
|
205
|
-
|
|
206
|
-
</details>
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/utils.ts
|
|
4
|
+
function isObject(value) {
|
|
5
|
+
return typeof value === "object" && value !== null;
|
|
6
|
+
}
|
|
7
|
+
function isPlainObject(value) {
|
|
8
|
+
if (!isObject(value)) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
const proto = Object.getPrototypeOf(value);
|
|
12
|
+
return proto === Object.prototype || proto === null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/clone/array.ts
|
|
16
|
+
function cloneArray(array, settings, refs) {
|
|
17
|
+
const result = isPrimitiveArray(array) ? array.slice() : array.map((i) => clone(i, settings, refs));
|
|
18
|
+
refs.set(array, result);
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
function isPrimitiveArray(array) {
|
|
22
|
+
return array.every((i) => !isObject(i));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/clone/array-buffer.ts
|
|
26
|
+
function cloneArrayBuffer(buffer, refs) {
|
|
27
|
+
const result = buffer.slice(0);
|
|
28
|
+
refs.set(buffer, result);
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/clone/array-buffer-view.ts
|
|
33
|
+
function cloneArrayBufferView(view, settings, refs) {
|
|
34
|
+
if (view instanceof DataView) {
|
|
35
|
+
const { buffer: buffer2, byteOffset: byteOffset2, byteLength } = view;
|
|
36
|
+
const result2 = new DataView(
|
|
37
|
+
cloneBuffer(buffer2, refs),
|
|
38
|
+
byteOffset2,
|
|
39
|
+
byteLength
|
|
40
|
+
);
|
|
41
|
+
refs.set(view, result2);
|
|
42
|
+
return result2;
|
|
43
|
+
}
|
|
44
|
+
if (!settings.preserveBufferSharing) {
|
|
45
|
+
const Ctor2 = view.constructor;
|
|
46
|
+
const result2 = new Ctor2(view);
|
|
47
|
+
refs.set(view, result2);
|
|
48
|
+
return result2;
|
|
49
|
+
}
|
|
50
|
+
const Ctor = view.constructor;
|
|
51
|
+
const { buffer, byteOffset, length } = view;
|
|
52
|
+
const result = new Ctor(cloneBuffer(buffer, refs), byteOffset, length);
|
|
53
|
+
refs.set(view, result);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function cloneBuffer(buffer, refs) {
|
|
57
|
+
if (refs.has(buffer)) {
|
|
58
|
+
return refs.get(buffer);
|
|
59
|
+
}
|
|
60
|
+
const result = buffer.slice(0);
|
|
61
|
+
refs.set(buffer, result);
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// src/clone/blob.ts
|
|
66
|
+
function cloneBlob(blob, refs) {
|
|
67
|
+
const { size, type } = blob;
|
|
68
|
+
const result = blob.slice(0, size, type);
|
|
69
|
+
refs.set(blob, result);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/clone/date.ts
|
|
74
|
+
function cloneDate(date, refs) {
|
|
75
|
+
const result = new Date(date.getTime());
|
|
76
|
+
refs.set(date, result);
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/clone/error.ts
|
|
81
|
+
function cloneError(error, settings, refs) {
|
|
82
|
+
const result = createErrorInstance(error, settings, refs);
|
|
83
|
+
refs.set(error, result);
|
|
84
|
+
if (error instanceof DOMException) {
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
const { stack, cause } = error;
|
|
88
|
+
if (stack) {
|
|
89
|
+
try {
|
|
90
|
+
result.stack = stack;
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if ("cause" in error && cause !== void 0) {
|
|
95
|
+
result.cause = clone(cause, settings, refs);
|
|
96
|
+
}
|
|
97
|
+
for (const key of Object.keys(error)) {
|
|
98
|
+
Reflect.set(result, key, clone(Reflect.get(error, key), settings, refs));
|
|
99
|
+
}
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
var ERROR_CTORS = {
|
|
103
|
+
EvalError,
|
|
104
|
+
RangeError,
|
|
105
|
+
ReferenceError,
|
|
106
|
+
SyntaxError,
|
|
107
|
+
TypeError,
|
|
108
|
+
URIError
|
|
109
|
+
};
|
|
110
|
+
function createErrorInstance(error, settings, refs) {
|
|
111
|
+
const { message, name } = error;
|
|
112
|
+
if (error instanceof DOMException) {
|
|
113
|
+
return new DOMException(message, name);
|
|
114
|
+
}
|
|
115
|
+
if (error instanceof AggregateError) {
|
|
116
|
+
return new AggregateError(
|
|
117
|
+
error.errors.map((e) => clone(e, settings, refs)),
|
|
118
|
+
message
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const Ctor = ERROR_CTORS[name];
|
|
122
|
+
if (Ctor) {
|
|
123
|
+
return new Ctor(message);
|
|
124
|
+
}
|
|
125
|
+
const result = new Error(message);
|
|
126
|
+
result.name = name;
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/clone/image-data.ts
|
|
131
|
+
function cloneImageData(data, refs) {
|
|
132
|
+
const { data: d, width, height, colorSpace } = data;
|
|
133
|
+
const result = new ImageData(new Uint8ClampedArray(d), width, height, {
|
|
134
|
+
colorSpace
|
|
135
|
+
});
|
|
136
|
+
refs.set(data, result);
|
|
137
|
+
return result;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/clone/map.ts
|
|
141
|
+
function cloneMap(map, settings, refs) {
|
|
142
|
+
const result = /* @__PURE__ */ new Map();
|
|
143
|
+
refs.set(map, result);
|
|
144
|
+
for (const [key, value] of map) {
|
|
145
|
+
result.set(clone(key, settings, refs), clone(value, settings, refs));
|
|
146
|
+
}
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
149
|
+
var OWN_DESCS = Object.getOwnPropertyDescriptors;
|
|
150
|
+
function OWN_ENUM_STRING_KEYS(object) {
|
|
151
|
+
const keys = Object.keys(object);
|
|
152
|
+
for (const key of keys) {
|
|
153
|
+
if (isUnsafeKey(key)) {
|
|
154
|
+
keys.splice(keys.indexOf(key), 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return keys;
|
|
158
|
+
}
|
|
159
|
+
function OWN_ENUM_SYMBOL_KEYS(object) {
|
|
160
|
+
const keys = OWN_SYMBOL_KEYS(object);
|
|
161
|
+
for (const key of keys) {
|
|
162
|
+
if (!Object.prototype.propertyIsEnumerable.call(object, key)) {
|
|
163
|
+
keys.splice(keys.indexOf(key), 1);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return keys;
|
|
167
|
+
}
|
|
168
|
+
function OWN_STRING_KEYS(object) {
|
|
169
|
+
const keys = Object.getOwnPropertyNames(object);
|
|
170
|
+
for (const key of keys) {
|
|
171
|
+
if (isUnsafeKey(key)) {
|
|
172
|
+
keys.splice(keys.indexOf(key), 1);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return keys;
|
|
176
|
+
}
|
|
177
|
+
var OWN_SYMBOL_KEYS = Object.getOwnPropertySymbols;
|
|
178
|
+
function isUnsafeKey(key) {
|
|
179
|
+
return typeof key === "string" && (key === "__proto__" || key === "prototype" || key === "constructor");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// src/clone/plain-object.ts
|
|
183
|
+
function clonePlainObject(object, settings, refs) {
|
|
184
|
+
const result = Object.create(Object.getPrototypeOf(object));
|
|
185
|
+
refs.set(object, result);
|
|
186
|
+
function copy(key) {
|
|
187
|
+
result[key] = clone(object[key], settings, refs);
|
|
188
|
+
}
|
|
189
|
+
for (const key of OWN_ENUM_STRING_KEYS(object)) {
|
|
190
|
+
copy(key);
|
|
191
|
+
}
|
|
192
|
+
if (settings.preserveSymbolKeys) {
|
|
193
|
+
for (const key of OWN_ENUM_SYMBOL_KEYS(object)) {
|
|
194
|
+
copy(key);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// src/clone/regexp.ts
|
|
201
|
+
function cloneRegExp(re, refs) {
|
|
202
|
+
const { source, flags, lastIndex } = re;
|
|
203
|
+
const result = new RegExp(source, flags);
|
|
204
|
+
refs.set(re, result);
|
|
205
|
+
result.lastIndex = lastIndex;
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/clone/set.ts
|
|
210
|
+
function cloneSet(set, settings, refs) {
|
|
211
|
+
if (isPrimitiveSet(set)) {
|
|
212
|
+
const result2 = new Set(set);
|
|
213
|
+
refs.set(set, result2);
|
|
214
|
+
return result2;
|
|
215
|
+
}
|
|
216
|
+
const result = /* @__PURE__ */ new Set();
|
|
217
|
+
refs.set(set, result);
|
|
218
|
+
for (const item of set) {
|
|
219
|
+
result.add(clone(item, settings, refs));
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
function isPrimitiveSet(set) {
|
|
224
|
+
for (const value of set) {
|
|
225
|
+
if (isObject(value)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// src/clone/url.ts
|
|
233
|
+
function cloneURL(url, refs) {
|
|
234
|
+
const result = new URL(url.href);
|
|
235
|
+
refs.set(url, result);
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/clone/url-search-params.ts
|
|
240
|
+
function cloneURLSearchParams(params, refs) {
|
|
241
|
+
const result = new URLSearchParams(params);
|
|
242
|
+
refs.set(params, result);
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// src/clone/with-descriptors.ts
|
|
247
|
+
function cloneWithDescriptors(object, settings, refs) {
|
|
248
|
+
const result = Object.create(Object.getPrototypeOf(object));
|
|
249
|
+
refs.set(object, result);
|
|
250
|
+
const descs = OWN_DESCS(object);
|
|
251
|
+
function copy(key) {
|
|
252
|
+
const desc = { ...descs[key] };
|
|
253
|
+
if ("value" in desc) {
|
|
254
|
+
desc.value = clone(desc.value, settings, refs);
|
|
255
|
+
}
|
|
256
|
+
Object.defineProperty(result, key, desc);
|
|
257
|
+
}
|
|
258
|
+
for (const key of OWN_STRING_KEYS(object)) {
|
|
259
|
+
copy(key);
|
|
260
|
+
}
|
|
261
|
+
if (settings.preserveSymbolKeys) {
|
|
262
|
+
for (const key of OWN_SYMBOL_KEYS(object)) {
|
|
263
|
+
copy(key);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/index.ts
|
|
270
|
+
function bunshinClone(value, options = {}) {
|
|
271
|
+
return clone(value, resolveOptions(options), /* @__PURE__ */ new WeakMap());
|
|
272
|
+
}
|
|
273
|
+
function clone(value, settings, refs) {
|
|
274
|
+
if (!isObject(value)) {
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
if (refs.has(value)) {
|
|
278
|
+
return refs.get(value);
|
|
279
|
+
}
|
|
280
|
+
const isClonePlainObject = isPlainObject(value);
|
|
281
|
+
if (settings.preserveDescriptors && isClonePlainObject) {
|
|
282
|
+
return cloneWithDescriptors(value, settings, refs);
|
|
283
|
+
}
|
|
284
|
+
if (Array.isArray(value)) {
|
|
285
|
+
return cloneArray(value, settings, refs);
|
|
286
|
+
}
|
|
287
|
+
if (isClonePlainObject) {
|
|
288
|
+
return clonePlainObject(value, settings, refs);
|
|
289
|
+
}
|
|
290
|
+
if (value instanceof Map) {
|
|
291
|
+
return cloneMap(value, settings, refs);
|
|
292
|
+
}
|
|
293
|
+
if (value instanceof Set) {
|
|
294
|
+
return cloneSet(value, settings, refs);
|
|
295
|
+
}
|
|
296
|
+
if (value instanceof Date) {
|
|
297
|
+
return cloneDate(value, refs);
|
|
298
|
+
}
|
|
299
|
+
if (value instanceof RegExp) {
|
|
300
|
+
return cloneRegExp(value, refs);
|
|
301
|
+
}
|
|
302
|
+
if (value instanceof ArrayBuffer) {
|
|
303
|
+
return cloneArrayBuffer(value, refs);
|
|
304
|
+
}
|
|
305
|
+
if (ArrayBuffer.isView(value)) {
|
|
306
|
+
return cloneArrayBufferView(
|
|
307
|
+
value,
|
|
308
|
+
settings,
|
|
309
|
+
refs
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
if (value instanceof Error) {
|
|
313
|
+
return cloneError(value, settings, refs);
|
|
314
|
+
}
|
|
315
|
+
if (value instanceof Blob) {
|
|
316
|
+
return cloneBlob(value, refs);
|
|
317
|
+
}
|
|
318
|
+
if (typeof ImageData !== "undefined" && value instanceof ImageData) {
|
|
319
|
+
return cloneImageData(value, refs);
|
|
320
|
+
}
|
|
321
|
+
if (value instanceof URL) {
|
|
322
|
+
return cloneURL(value, refs);
|
|
323
|
+
}
|
|
324
|
+
if (value instanceof URLSearchParams) {
|
|
325
|
+
return cloneURLSearchParams(value, refs);
|
|
326
|
+
}
|
|
327
|
+
refs.set(value, value);
|
|
328
|
+
return value;
|
|
329
|
+
}
|
|
330
|
+
function resolveOptions(options) {
|
|
331
|
+
let {
|
|
332
|
+
preserveBufferSharing = false,
|
|
333
|
+
preserveDescriptors = false,
|
|
334
|
+
preserveSymbolKeys = false
|
|
335
|
+
} = options;
|
|
336
|
+
if (typeof preserveBufferSharing !== "boolean") {
|
|
337
|
+
console.warn("Invalid preserveBufferSharing option. Fallback: false.");
|
|
338
|
+
preserveBufferSharing = false;
|
|
339
|
+
}
|
|
340
|
+
if (typeof preserveDescriptors !== "boolean") {
|
|
341
|
+
console.warn("Invalid preserveDescriptors option. Fallback: false.");
|
|
342
|
+
preserveDescriptors = false;
|
|
343
|
+
}
|
|
344
|
+
if (typeof preserveSymbolKeys !== "boolean") {
|
|
345
|
+
console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
|
|
346
|
+
preserveSymbolKeys = false;
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
preserveBufferSharing,
|
|
350
|
+
preserveDescriptors,
|
|
351
|
+
preserveSymbolKeys
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
exports.bunshinClone = bunshinClone;
|
|
356
|
+
exports.clone = clone;
|
|
357
|
+
exports.isObject = isObject;
|
|
358
|
+
exports.isPlainObject = isPlainObject;
|
|
359
|
+
exports.isPrimitiveArray = isPrimitiveArray;
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
// src/utils.ts
|
|
2
|
+
function isObject(value) {
|
|
3
|
+
return typeof value === "object" && value !== null;
|
|
4
|
+
}
|
|
5
|
+
function isPlainObject(value) {
|
|
6
|
+
if (!isObject(value)) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const proto = Object.getPrototypeOf(value);
|
|
10
|
+
return proto === Object.prototype || proto === null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// src/clone/array.ts
|
|
14
|
+
function cloneArray(array, settings, refs) {
|
|
15
|
+
const result = isPrimitiveArray(array) ? array.slice() : array.map((i) => clone(i, settings, refs));
|
|
16
|
+
refs.set(array, result);
|
|
17
|
+
return result;
|
|
18
|
+
}
|
|
19
|
+
function isPrimitiveArray(array) {
|
|
20
|
+
return array.every((i) => !isObject(i));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/clone/array-buffer.ts
|
|
24
|
+
function cloneArrayBuffer(buffer, refs) {
|
|
25
|
+
const result = buffer.slice(0);
|
|
26
|
+
refs.set(buffer, result);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/clone/array-buffer-view.ts
|
|
31
|
+
function cloneArrayBufferView(view, settings, refs) {
|
|
32
|
+
if (view instanceof DataView) {
|
|
33
|
+
const { buffer: buffer2, byteOffset: byteOffset2, byteLength } = view;
|
|
34
|
+
const result2 = new DataView(
|
|
35
|
+
cloneBuffer(buffer2, refs),
|
|
36
|
+
byteOffset2,
|
|
37
|
+
byteLength
|
|
38
|
+
);
|
|
39
|
+
refs.set(view, result2);
|
|
40
|
+
return result2;
|
|
41
|
+
}
|
|
42
|
+
if (!settings.preserveBufferSharing) {
|
|
43
|
+
const Ctor2 = view.constructor;
|
|
44
|
+
const result2 = new Ctor2(view);
|
|
45
|
+
refs.set(view, result2);
|
|
46
|
+
return result2;
|
|
47
|
+
}
|
|
48
|
+
const Ctor = view.constructor;
|
|
49
|
+
const { buffer, byteOffset, length } = view;
|
|
50
|
+
const result = new Ctor(cloneBuffer(buffer, refs), byteOffset, length);
|
|
51
|
+
refs.set(view, result);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
function cloneBuffer(buffer, refs) {
|
|
55
|
+
if (refs.has(buffer)) {
|
|
56
|
+
return refs.get(buffer);
|
|
57
|
+
}
|
|
58
|
+
const result = buffer.slice(0);
|
|
59
|
+
refs.set(buffer, result);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/clone/blob.ts
|
|
64
|
+
function cloneBlob(blob, refs) {
|
|
65
|
+
const { size, type } = blob;
|
|
66
|
+
const result = blob.slice(0, size, type);
|
|
67
|
+
refs.set(blob, result);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/clone/date.ts
|
|
72
|
+
function cloneDate(date, refs) {
|
|
73
|
+
const result = new Date(date.getTime());
|
|
74
|
+
refs.set(date, result);
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/clone/error.ts
|
|
79
|
+
function cloneError(error, settings, refs) {
|
|
80
|
+
const result = createErrorInstance(error, settings, refs);
|
|
81
|
+
refs.set(error, result);
|
|
82
|
+
if (error instanceof DOMException) {
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
const { stack, cause } = error;
|
|
86
|
+
if (stack) {
|
|
87
|
+
try {
|
|
88
|
+
result.stack = stack;
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if ("cause" in error && cause !== void 0) {
|
|
93
|
+
result.cause = clone(cause, settings, refs);
|
|
94
|
+
}
|
|
95
|
+
for (const key of Object.keys(error)) {
|
|
96
|
+
Reflect.set(result, key, clone(Reflect.get(error, key), settings, refs));
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
var ERROR_CTORS = {
|
|
101
|
+
EvalError,
|
|
102
|
+
RangeError,
|
|
103
|
+
ReferenceError,
|
|
104
|
+
SyntaxError,
|
|
105
|
+
TypeError,
|
|
106
|
+
URIError
|
|
107
|
+
};
|
|
108
|
+
function createErrorInstance(error, settings, refs) {
|
|
109
|
+
const { message, name } = error;
|
|
110
|
+
if (error instanceof DOMException) {
|
|
111
|
+
return new DOMException(message, name);
|
|
112
|
+
}
|
|
113
|
+
if (error instanceof AggregateError) {
|
|
114
|
+
return new AggregateError(
|
|
115
|
+
error.errors.map((e) => clone(e, settings, refs)),
|
|
116
|
+
message
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
const Ctor = ERROR_CTORS[name];
|
|
120
|
+
if (Ctor) {
|
|
121
|
+
return new Ctor(message);
|
|
122
|
+
}
|
|
123
|
+
const result = new Error(message);
|
|
124
|
+
result.name = name;
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/clone/image-data.ts
|
|
129
|
+
function cloneImageData(data, refs) {
|
|
130
|
+
const { data: d, width, height, colorSpace } = data;
|
|
131
|
+
const result = new ImageData(new Uint8ClampedArray(d), width, height, {
|
|
132
|
+
colorSpace
|
|
133
|
+
});
|
|
134
|
+
refs.set(data, result);
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/clone/map.ts
|
|
139
|
+
function cloneMap(map, settings, refs) {
|
|
140
|
+
const result = /* @__PURE__ */ new Map();
|
|
141
|
+
refs.set(map, result);
|
|
142
|
+
for (const [key, value] of map) {
|
|
143
|
+
result.set(clone(key, settings, refs), clone(value, settings, refs));
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
var OWN_DESCS = Object.getOwnPropertyDescriptors;
|
|
148
|
+
function OWN_ENUM_STRING_KEYS(object) {
|
|
149
|
+
const keys = Object.keys(object);
|
|
150
|
+
for (const key of keys) {
|
|
151
|
+
if (isUnsafeKey(key)) {
|
|
152
|
+
keys.splice(keys.indexOf(key), 1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return keys;
|
|
156
|
+
}
|
|
157
|
+
function OWN_ENUM_SYMBOL_KEYS(object) {
|
|
158
|
+
const keys = OWN_SYMBOL_KEYS(object);
|
|
159
|
+
for (const key of keys) {
|
|
160
|
+
if (!Object.prototype.propertyIsEnumerable.call(object, key)) {
|
|
161
|
+
keys.splice(keys.indexOf(key), 1);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return keys;
|
|
165
|
+
}
|
|
166
|
+
function OWN_STRING_KEYS(object) {
|
|
167
|
+
const keys = Object.getOwnPropertyNames(object);
|
|
168
|
+
for (const key of keys) {
|
|
169
|
+
if (isUnsafeKey(key)) {
|
|
170
|
+
keys.splice(keys.indexOf(key), 1);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return keys;
|
|
174
|
+
}
|
|
175
|
+
var OWN_SYMBOL_KEYS = Object.getOwnPropertySymbols;
|
|
176
|
+
function isUnsafeKey(key) {
|
|
177
|
+
return typeof key === "string" && (key === "__proto__" || key === "prototype" || key === "constructor");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/clone/plain-object.ts
|
|
181
|
+
function clonePlainObject(object, settings, refs) {
|
|
182
|
+
const result = Object.create(Object.getPrototypeOf(object));
|
|
183
|
+
refs.set(object, result);
|
|
184
|
+
function copy(key) {
|
|
185
|
+
result[key] = clone(object[key], settings, refs);
|
|
186
|
+
}
|
|
187
|
+
for (const key of OWN_ENUM_STRING_KEYS(object)) {
|
|
188
|
+
copy(key);
|
|
189
|
+
}
|
|
190
|
+
if (settings.preserveSymbolKeys) {
|
|
191
|
+
for (const key of OWN_ENUM_SYMBOL_KEYS(object)) {
|
|
192
|
+
copy(key);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/clone/regexp.ts
|
|
199
|
+
function cloneRegExp(re, refs) {
|
|
200
|
+
const { source, flags, lastIndex } = re;
|
|
201
|
+
const result = new RegExp(source, flags);
|
|
202
|
+
refs.set(re, result);
|
|
203
|
+
result.lastIndex = lastIndex;
|
|
204
|
+
return result;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// src/clone/set.ts
|
|
208
|
+
function cloneSet(set, settings, refs) {
|
|
209
|
+
if (isPrimitiveSet(set)) {
|
|
210
|
+
const result2 = new Set(set);
|
|
211
|
+
refs.set(set, result2);
|
|
212
|
+
return result2;
|
|
213
|
+
}
|
|
214
|
+
const result = /* @__PURE__ */ new Set();
|
|
215
|
+
refs.set(set, result);
|
|
216
|
+
for (const item of set) {
|
|
217
|
+
result.add(clone(item, settings, refs));
|
|
218
|
+
}
|
|
219
|
+
return result;
|
|
220
|
+
}
|
|
221
|
+
function isPrimitiveSet(set) {
|
|
222
|
+
for (const value of set) {
|
|
223
|
+
if (isObject(value)) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/clone/url.ts
|
|
231
|
+
function cloneURL(url, refs) {
|
|
232
|
+
const result = new URL(url.href);
|
|
233
|
+
refs.set(url, result);
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/clone/url-search-params.ts
|
|
238
|
+
function cloneURLSearchParams(params, refs) {
|
|
239
|
+
const result = new URLSearchParams(params);
|
|
240
|
+
refs.set(params, result);
|
|
241
|
+
return result;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/clone/with-descriptors.ts
|
|
245
|
+
function cloneWithDescriptors(object, settings, refs) {
|
|
246
|
+
const result = Object.create(Object.getPrototypeOf(object));
|
|
247
|
+
refs.set(object, result);
|
|
248
|
+
const descs = OWN_DESCS(object);
|
|
249
|
+
function copy(key) {
|
|
250
|
+
const desc = { ...descs[key] };
|
|
251
|
+
if ("value" in desc) {
|
|
252
|
+
desc.value = clone(desc.value, settings, refs);
|
|
253
|
+
}
|
|
254
|
+
Object.defineProperty(result, key, desc);
|
|
255
|
+
}
|
|
256
|
+
for (const key of OWN_STRING_KEYS(object)) {
|
|
257
|
+
copy(key);
|
|
258
|
+
}
|
|
259
|
+
if (settings.preserveSymbolKeys) {
|
|
260
|
+
for (const key of OWN_SYMBOL_KEYS(object)) {
|
|
261
|
+
copy(key);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/index.ts
|
|
268
|
+
function bunshinClone(value, options = {}) {
|
|
269
|
+
return clone(value, resolveOptions(options), /* @__PURE__ */ new WeakMap());
|
|
270
|
+
}
|
|
271
|
+
function clone(value, settings, refs) {
|
|
272
|
+
if (!isObject(value)) {
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
if (refs.has(value)) {
|
|
276
|
+
return refs.get(value);
|
|
277
|
+
}
|
|
278
|
+
const isClonePlainObject = isPlainObject(value);
|
|
279
|
+
if (settings.preserveDescriptors && isClonePlainObject) {
|
|
280
|
+
return cloneWithDescriptors(value, settings, refs);
|
|
281
|
+
}
|
|
282
|
+
if (Array.isArray(value)) {
|
|
283
|
+
return cloneArray(value, settings, refs);
|
|
284
|
+
}
|
|
285
|
+
if (isClonePlainObject) {
|
|
286
|
+
return clonePlainObject(value, settings, refs);
|
|
287
|
+
}
|
|
288
|
+
if (value instanceof Map) {
|
|
289
|
+
return cloneMap(value, settings, refs);
|
|
290
|
+
}
|
|
291
|
+
if (value instanceof Set) {
|
|
292
|
+
return cloneSet(value, settings, refs);
|
|
293
|
+
}
|
|
294
|
+
if (value instanceof Date) {
|
|
295
|
+
return cloneDate(value, refs);
|
|
296
|
+
}
|
|
297
|
+
if (value instanceof RegExp) {
|
|
298
|
+
return cloneRegExp(value, refs);
|
|
299
|
+
}
|
|
300
|
+
if (value instanceof ArrayBuffer) {
|
|
301
|
+
return cloneArrayBuffer(value, refs);
|
|
302
|
+
}
|
|
303
|
+
if (ArrayBuffer.isView(value)) {
|
|
304
|
+
return cloneArrayBufferView(
|
|
305
|
+
value,
|
|
306
|
+
settings,
|
|
307
|
+
refs
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
if (value instanceof Error) {
|
|
311
|
+
return cloneError(value, settings, refs);
|
|
312
|
+
}
|
|
313
|
+
if (value instanceof Blob) {
|
|
314
|
+
return cloneBlob(value, refs);
|
|
315
|
+
}
|
|
316
|
+
if (typeof ImageData !== "undefined" && value instanceof ImageData) {
|
|
317
|
+
return cloneImageData(value, refs);
|
|
318
|
+
}
|
|
319
|
+
if (value instanceof URL) {
|
|
320
|
+
return cloneURL(value, refs);
|
|
321
|
+
}
|
|
322
|
+
if (value instanceof URLSearchParams) {
|
|
323
|
+
return cloneURLSearchParams(value, refs);
|
|
324
|
+
}
|
|
325
|
+
refs.set(value, value);
|
|
326
|
+
return value;
|
|
327
|
+
}
|
|
328
|
+
function resolveOptions(options) {
|
|
329
|
+
let {
|
|
330
|
+
preserveBufferSharing = false,
|
|
331
|
+
preserveDescriptors = false,
|
|
332
|
+
preserveSymbolKeys = false
|
|
333
|
+
} = options;
|
|
334
|
+
if (typeof preserveBufferSharing !== "boolean") {
|
|
335
|
+
console.warn("Invalid preserveBufferSharing option. Fallback: false.");
|
|
336
|
+
preserveBufferSharing = false;
|
|
337
|
+
}
|
|
338
|
+
if (typeof preserveDescriptors !== "boolean") {
|
|
339
|
+
console.warn("Invalid preserveDescriptors option. Fallback: false.");
|
|
340
|
+
preserveDescriptors = false;
|
|
341
|
+
}
|
|
342
|
+
if (typeof preserveSymbolKeys !== "boolean") {
|
|
343
|
+
console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
|
|
344
|
+
preserveSymbolKeys = false;
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
preserveBufferSharing,
|
|
348
|
+
preserveDescriptors,
|
|
349
|
+
preserveSymbolKeys
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export { bunshinClone, clone, isObject, isPlainObject, isPrimitiveArray };
|
package/dist/index.cjs
CHANGED
|
@@ -219,13 +219,7 @@ function cloneWithDescriptors(object, settings, refs) {
|
|
|
219
219
|
if ("value" in desc) {
|
|
220
220
|
desc.value = clone(desc.value, settings, refs);
|
|
221
221
|
}
|
|
222
|
-
|
|
223
|
-
Object.defineProperty(result, key, desc);
|
|
224
|
-
} catch (error) {
|
|
225
|
-
if (settings.strictDescriptors) {
|
|
226
|
-
throw error;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
222
|
+
Object.defineProperty(result, key, desc);
|
|
229
223
|
}
|
|
230
224
|
for (const key of own.OWN_STRING_KEYS(object)) {
|
|
231
225
|
copy(key);
|
|
@@ -303,13 +297,8 @@ function resolveOptions(options) {
|
|
|
303
297
|
let {
|
|
304
298
|
preserveBufferSharing = false,
|
|
305
299
|
preserveDescriptors = false,
|
|
306
|
-
strictDescriptors = false,
|
|
307
300
|
preserveSymbolKeys = false
|
|
308
301
|
} = options;
|
|
309
|
-
if (typeof preserveSymbolKeys !== "boolean") {
|
|
310
|
-
console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
|
|
311
|
-
preserveSymbolKeys = false;
|
|
312
|
-
}
|
|
313
302
|
if (typeof preserveBufferSharing !== "boolean") {
|
|
314
303
|
console.warn("Invalid preserveBufferSharing option. Fallback: false.");
|
|
315
304
|
preserveBufferSharing = false;
|
|
@@ -318,15 +307,14 @@ function resolveOptions(options) {
|
|
|
318
307
|
console.warn("Invalid preserveDescriptors option. Fallback: false.");
|
|
319
308
|
preserveDescriptors = false;
|
|
320
309
|
}
|
|
321
|
-
if (typeof
|
|
322
|
-
console.warn("Invalid
|
|
323
|
-
|
|
310
|
+
if (typeof preserveSymbolKeys !== "boolean") {
|
|
311
|
+
console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
|
|
312
|
+
preserveSymbolKeys = false;
|
|
324
313
|
}
|
|
325
314
|
return {
|
|
326
315
|
preserveBufferSharing,
|
|
327
316
|
preserveDescriptors,
|
|
328
|
-
preserveSymbolKeys
|
|
329
|
-
strictDescriptors
|
|
317
|
+
preserveSymbolKeys
|
|
330
318
|
};
|
|
331
319
|
}
|
|
332
320
|
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -217,13 +217,7 @@ function cloneWithDescriptors(object, settings, refs) {
|
|
|
217
217
|
if ("value" in desc) {
|
|
218
218
|
desc.value = clone(desc.value, settings, refs);
|
|
219
219
|
}
|
|
220
|
-
|
|
221
|
-
Object.defineProperty(result, key, desc);
|
|
222
|
-
} catch (error) {
|
|
223
|
-
if (settings.strictDescriptors) {
|
|
224
|
-
throw error;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
220
|
+
Object.defineProperty(result, key, desc);
|
|
227
221
|
}
|
|
228
222
|
for (const key of OWN_STRING_KEYS(object)) {
|
|
229
223
|
copy(key);
|
|
@@ -301,13 +295,8 @@ function resolveOptions(options) {
|
|
|
301
295
|
let {
|
|
302
296
|
preserveBufferSharing = false,
|
|
303
297
|
preserveDescriptors = false,
|
|
304
|
-
strictDescriptors = false,
|
|
305
298
|
preserveSymbolKeys = false
|
|
306
299
|
} = options;
|
|
307
|
-
if (typeof preserveSymbolKeys !== "boolean") {
|
|
308
|
-
console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
|
|
309
|
-
preserveSymbolKeys = false;
|
|
310
|
-
}
|
|
311
300
|
if (typeof preserveBufferSharing !== "boolean") {
|
|
312
301
|
console.warn("Invalid preserveBufferSharing option. Fallback: false.");
|
|
313
302
|
preserveBufferSharing = false;
|
|
@@ -316,15 +305,14 @@ function resolveOptions(options) {
|
|
|
316
305
|
console.warn("Invalid preserveDescriptors option. Fallback: false.");
|
|
317
306
|
preserveDescriptors = false;
|
|
318
307
|
}
|
|
319
|
-
if (typeof
|
|
320
|
-
console.warn("Invalid
|
|
321
|
-
|
|
308
|
+
if (typeof preserveSymbolKeys !== "boolean") {
|
|
309
|
+
console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
|
|
310
|
+
preserveSymbolKeys = false;
|
|
322
311
|
}
|
|
323
312
|
return {
|
|
324
313
|
preserveBufferSharing,
|
|
325
314
|
preserveDescriptors,
|
|
326
|
-
preserveSymbolKeys
|
|
327
|
-
strictDescriptors
|
|
315
|
+
preserveSymbolKeys
|
|
328
316
|
};
|
|
329
317
|
}
|
|
330
318
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bunshin-clone",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "High-performance deep clone utility with descriptor support",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"array",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"test": "bun test"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@y14e/own": "^1.0.
|
|
72
|
+
"@y14e/own": "^1.0.1"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"bun-types": "latest",
|