bunshin-clone 2.0.1 → 2.0.3

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 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>
@@ -147,6 +147,12 @@ function cloneMap(map, settings, refs) {
147
147
  return result;
148
148
  }
149
149
  var OWN_DESCS = Object.getOwnPropertyDescriptors;
150
+ function OWN_ENUM_KEYS(object, symbol) {
151
+ return typeof symbol !== "boolean" || !symbol ? OWN_ENUM_STRING_KEYS(object) : [
152
+ ...OWN_ENUM_STRING_KEYS(object),
153
+ ...OWN_ENUM_SYMBOL_KEYS(object)
154
+ ];
155
+ }
150
156
  function OWN_ENUM_STRING_KEYS(object) {
151
157
  const keys = Object.keys(object);
152
158
  for (const key of keys) {
@@ -165,6 +171,12 @@ function OWN_ENUM_SYMBOL_KEYS(object) {
165
171
  }
166
172
  return keys;
167
173
  }
174
+ function OWN_KEYS(object, symbol) {
175
+ return typeof symbol !== "boolean" || !symbol ? OWN_STRING_KEYS(object) : [
176
+ ...OWN_STRING_KEYS(object),
177
+ ...OWN_SYMBOL_KEYS(object)
178
+ ];
179
+ }
168
180
  function OWN_STRING_KEYS(object) {
169
181
  const keys = Object.getOwnPropertyNames(object);
170
182
  for (const key of keys) {
@@ -183,17 +195,9 @@ function isUnsafeKey(key) {
183
195
  function clonePlainObject(object, settings, refs) {
184
196
  const result = Object.create(Object.getPrototypeOf(object));
185
197
  refs.set(object, result);
186
- function copy(key) {
198
+ for (const key of OWN_ENUM_KEYS(object, settings.preserveSymbolKeys)) {
187
199
  result[key] = clone(object[key], settings, refs);
188
200
  }
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
201
  return result;
198
202
  }
199
203
 
@@ -248,26 +252,12 @@ function cloneWithDescriptors(object, settings, refs) {
248
252
  const result = Object.create(Object.getPrototypeOf(object));
249
253
  refs.set(object, result);
250
254
  const descs = OWN_DESCS(object);
251
- function copy(key) {
255
+ for (const key of OWN_KEYS(object, settings.preserveSymbolKeys)) {
252
256
  const desc = { ...descs[key] };
253
257
  if ("value" in desc) {
254
258
  desc.value = clone(desc.value, settings, refs);
255
259
  }
256
- try {
257
- Object.defineProperty(result, key, desc);
258
- } catch (error) {
259
- if (settings.strictDescriptors) {
260
- throw error;
261
- }
262
- }
263
- }
264
- for (const key of OWN_STRING_KEYS(object)) {
265
- copy(key);
266
- }
267
- if (settings.preserveSymbolKeys) {
268
- for (const key of OWN_SYMBOL_KEYS(object)) {
269
- copy(key);
270
- }
260
+ Object.defineProperty(result, key, desc);
271
261
  }
272
262
  return result;
273
263
  }
@@ -337,13 +327,8 @@ function resolveOptions(options) {
337
327
  let {
338
328
  preserveBufferSharing = false,
339
329
  preserveDescriptors = false,
340
- strictDescriptors = false,
341
330
  preserveSymbolKeys = false
342
331
  } = options;
343
- if (typeof preserveSymbolKeys !== "boolean") {
344
- console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
345
- preserveSymbolKeys = false;
346
- }
347
332
  if (typeof preserveBufferSharing !== "boolean") {
348
333
  console.warn("Invalid preserveBufferSharing option. Fallback: false.");
349
334
  preserveBufferSharing = false;
@@ -352,15 +337,14 @@ function resolveOptions(options) {
352
337
  console.warn("Invalid preserveDescriptors option. Fallback: false.");
353
338
  preserveDescriptors = false;
354
339
  }
355
- if (typeof strictDescriptors !== "boolean") {
356
- console.warn("Invalid strictDescriptors option. Fallback: false.");
357
- strictDescriptors = false;
340
+ if (typeof preserveSymbolKeys !== "boolean") {
341
+ console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
342
+ preserveSymbolKeys = false;
358
343
  }
359
344
  return {
360
345
  preserveBufferSharing,
361
346
  preserveDescriptors,
362
- preserveSymbolKeys,
363
- strictDescriptors
347
+ preserveSymbolKeys
364
348
  };
365
349
  }
366
350
 
@@ -145,6 +145,12 @@ function cloneMap(map, settings, refs) {
145
145
  return result;
146
146
  }
147
147
  var OWN_DESCS = Object.getOwnPropertyDescriptors;
148
+ function OWN_ENUM_KEYS(object, symbol) {
149
+ return typeof symbol !== "boolean" || !symbol ? OWN_ENUM_STRING_KEYS(object) : [
150
+ ...OWN_ENUM_STRING_KEYS(object),
151
+ ...OWN_ENUM_SYMBOL_KEYS(object)
152
+ ];
153
+ }
148
154
  function OWN_ENUM_STRING_KEYS(object) {
149
155
  const keys = Object.keys(object);
150
156
  for (const key of keys) {
@@ -163,6 +169,12 @@ function OWN_ENUM_SYMBOL_KEYS(object) {
163
169
  }
164
170
  return keys;
165
171
  }
172
+ function OWN_KEYS(object, symbol) {
173
+ return typeof symbol !== "boolean" || !symbol ? OWN_STRING_KEYS(object) : [
174
+ ...OWN_STRING_KEYS(object),
175
+ ...OWN_SYMBOL_KEYS(object)
176
+ ];
177
+ }
166
178
  function OWN_STRING_KEYS(object) {
167
179
  const keys = Object.getOwnPropertyNames(object);
168
180
  for (const key of keys) {
@@ -181,17 +193,9 @@ function isUnsafeKey(key) {
181
193
  function clonePlainObject(object, settings, refs) {
182
194
  const result = Object.create(Object.getPrototypeOf(object));
183
195
  refs.set(object, result);
184
- function copy(key) {
196
+ for (const key of OWN_ENUM_KEYS(object, settings.preserveSymbolKeys)) {
185
197
  result[key] = clone(object[key], settings, refs);
186
198
  }
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
199
  return result;
196
200
  }
197
201
 
@@ -246,26 +250,12 @@ function cloneWithDescriptors(object, settings, refs) {
246
250
  const result = Object.create(Object.getPrototypeOf(object));
247
251
  refs.set(object, result);
248
252
  const descs = OWN_DESCS(object);
249
- function copy(key) {
253
+ for (const key of OWN_KEYS(object, settings.preserveSymbolKeys)) {
250
254
  const desc = { ...descs[key] };
251
255
  if ("value" in desc) {
252
256
  desc.value = clone(desc.value, settings, refs);
253
257
  }
254
- try {
255
- Object.defineProperty(result, key, desc);
256
- } catch (error) {
257
- if (settings.strictDescriptors) {
258
- throw error;
259
- }
260
- }
261
- }
262
- for (const key of OWN_STRING_KEYS(object)) {
263
- copy(key);
264
- }
265
- if (settings.preserveSymbolKeys) {
266
- for (const key of OWN_SYMBOL_KEYS(object)) {
267
- copy(key);
268
- }
258
+ Object.defineProperty(result, key, desc);
269
259
  }
270
260
  return result;
271
261
  }
@@ -335,13 +325,8 @@ function resolveOptions(options) {
335
325
  let {
336
326
  preserveBufferSharing = false,
337
327
  preserveDescriptors = false,
338
- strictDescriptors = false,
339
328
  preserveSymbolKeys = false
340
329
  } = options;
341
- if (typeof preserveSymbolKeys !== "boolean") {
342
- console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
343
- preserveSymbolKeys = false;
344
- }
345
330
  if (typeof preserveBufferSharing !== "boolean") {
346
331
  console.warn("Invalid preserveBufferSharing option. Fallback: false.");
347
332
  preserveBufferSharing = false;
@@ -350,15 +335,14 @@ function resolveOptions(options) {
350
335
  console.warn("Invalid preserveDescriptors option. Fallback: false.");
351
336
  preserveDescriptors = false;
352
337
  }
353
- if (typeof strictDescriptors !== "boolean") {
354
- console.warn("Invalid strictDescriptors option. Fallback: false.");
355
- strictDescriptors = false;
338
+ if (typeof preserveSymbolKeys !== "boolean") {
339
+ console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
340
+ preserveSymbolKeys = false;
356
341
  }
357
342
  return {
358
343
  preserveBufferSharing,
359
344
  preserveDescriptors,
360
- preserveSymbolKeys,
361
- strictDescriptors
345
+ preserveSymbolKeys
362
346
  };
363
347
  }
364
348
 
package/dist/index.cjs CHANGED
@@ -151,17 +151,9 @@ function cloneMap(map, settings, refs) {
151
151
  function clonePlainObject(object, settings, refs) {
152
152
  const result = Object.create(Object.getPrototypeOf(object));
153
153
  refs.set(object, result);
154
- function copy(key) {
154
+ for (const key of own.OWN_ENUM_KEYS(object, settings.preserveSymbolKeys)) {
155
155
  result[key] = clone(object[key], settings, refs);
156
156
  }
157
- for (const key of own.OWN_ENUM_STRING_KEYS(object)) {
158
- copy(key);
159
- }
160
- if (settings.preserveSymbolKeys) {
161
- for (const key of own.OWN_ENUM_SYMBOL_KEYS(object)) {
162
- copy(key);
163
- }
164
- }
165
157
  return result;
166
158
  }
167
159
 
@@ -214,26 +206,12 @@ function cloneWithDescriptors(object, settings, refs) {
214
206
  const result = Object.create(Object.getPrototypeOf(object));
215
207
  refs.set(object, result);
216
208
  const descs = own.OWN_DESCS(object);
217
- function copy(key) {
209
+ for (const key of own.OWN_KEYS(object, settings.preserveSymbolKeys)) {
218
210
  const desc = { ...descs[key] };
219
211
  if ("value" in desc) {
220
212
  desc.value = clone(desc.value, settings, refs);
221
213
  }
222
- try {
223
- Object.defineProperty(result, key, desc);
224
- } catch (error) {
225
- if (settings.strictDescriptors) {
226
- throw error;
227
- }
228
- }
229
- }
230
- for (const key of own.OWN_STRING_KEYS(object)) {
231
- copy(key);
232
- }
233
- if (settings.preserveSymbolKeys) {
234
- for (const key of own.OWN_SYMBOL_KEYS(object)) {
235
- copy(key);
236
- }
214
+ Object.defineProperty(result, key, desc);
237
215
  }
238
216
  return result;
239
217
  }
@@ -303,13 +281,8 @@ function resolveOptions(options) {
303
281
  let {
304
282
  preserveBufferSharing = false,
305
283
  preserveDescriptors = false,
306
- strictDescriptors = false,
307
284
  preserveSymbolKeys = false
308
285
  } = options;
309
- if (typeof preserveSymbolKeys !== "boolean") {
310
- console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
311
- preserveSymbolKeys = false;
312
- }
313
286
  if (typeof preserveBufferSharing !== "boolean") {
314
287
  console.warn("Invalid preserveBufferSharing option. Fallback: false.");
315
288
  preserveBufferSharing = false;
@@ -318,15 +291,14 @@ function resolveOptions(options) {
318
291
  console.warn("Invalid preserveDescriptors option. Fallback: false.");
319
292
  preserveDescriptors = false;
320
293
  }
321
- if (typeof strictDescriptors !== "boolean") {
322
- console.warn("Invalid strictDescriptors option. Fallback: false.");
323
- strictDescriptors = false;
294
+ if (typeof preserveSymbolKeys !== "boolean") {
295
+ console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
296
+ preserveSymbolKeys = false;
324
297
  }
325
298
  return {
326
299
  preserveBufferSharing,
327
300
  preserveDescriptors,
328
- preserveSymbolKeys,
329
- strictDescriptors
301
+ preserveSymbolKeys
330
302
  };
331
303
  }
332
304
 
package/dist/index.d.cts CHANGED
@@ -4,7 +4,6 @@ interface BunshinCloneOptions {
4
4
  preserveBufferSharing: boolean;
5
5
  preserveDescriptors: boolean;
6
6
  preserveSymbolKeys: boolean;
7
- strictDescriptors: boolean;
8
7
  }
9
8
  type PlainObject = Record<PropertyKey, unknown>;
10
9
 
package/dist/index.d.ts CHANGED
@@ -4,7 +4,6 @@ interface BunshinCloneOptions {
4
4
  preserveBufferSharing: boolean;
5
5
  preserveDescriptors: boolean;
6
6
  preserveSymbolKeys: boolean;
7
- strictDescriptors: boolean;
8
7
  }
9
8
  type PlainObject = Record<PropertyKey, unknown>;
10
9
 
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { OWN_DESCS, OWN_STRING_KEYS, OWN_SYMBOL_KEYS, OWN_ENUM_STRING_KEYS, OWN_ENUM_SYMBOL_KEYS } from '@y14e/own';
1
+ import { OWN_DESCS, OWN_KEYS, OWN_ENUM_KEYS } from '@y14e/own';
2
2
 
3
3
  // src/utils.ts
4
4
  function isObject(value) {
@@ -149,17 +149,9 @@ function cloneMap(map, settings, refs) {
149
149
  function clonePlainObject(object, settings, refs) {
150
150
  const result = Object.create(Object.getPrototypeOf(object));
151
151
  refs.set(object, result);
152
- function copy(key) {
152
+ for (const key of OWN_ENUM_KEYS(object, settings.preserveSymbolKeys)) {
153
153
  result[key] = clone(object[key], settings, refs);
154
154
  }
155
- for (const key of OWN_ENUM_STRING_KEYS(object)) {
156
- copy(key);
157
- }
158
- if (settings.preserveSymbolKeys) {
159
- for (const key of OWN_ENUM_SYMBOL_KEYS(object)) {
160
- copy(key);
161
- }
162
- }
163
155
  return result;
164
156
  }
165
157
 
@@ -212,26 +204,12 @@ function cloneWithDescriptors(object, settings, refs) {
212
204
  const result = Object.create(Object.getPrototypeOf(object));
213
205
  refs.set(object, result);
214
206
  const descs = OWN_DESCS(object);
215
- function copy(key) {
207
+ for (const key of OWN_KEYS(object, settings.preserveSymbolKeys)) {
216
208
  const desc = { ...descs[key] };
217
209
  if ("value" in desc) {
218
210
  desc.value = clone(desc.value, settings, refs);
219
211
  }
220
- try {
221
- Object.defineProperty(result, key, desc);
222
- } catch (error) {
223
- if (settings.strictDescriptors) {
224
- throw error;
225
- }
226
- }
227
- }
228
- for (const key of OWN_STRING_KEYS(object)) {
229
- copy(key);
230
- }
231
- if (settings.preserveSymbolKeys) {
232
- for (const key of OWN_SYMBOL_KEYS(object)) {
233
- copy(key);
234
- }
212
+ Object.defineProperty(result, key, desc);
235
213
  }
236
214
  return result;
237
215
  }
@@ -301,13 +279,8 @@ function resolveOptions(options) {
301
279
  let {
302
280
  preserveBufferSharing = false,
303
281
  preserveDescriptors = false,
304
- strictDescriptors = false,
305
282
  preserveSymbolKeys = false
306
283
  } = options;
307
- if (typeof preserveSymbolKeys !== "boolean") {
308
- console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
309
- preserveSymbolKeys = false;
310
- }
311
284
  if (typeof preserveBufferSharing !== "boolean") {
312
285
  console.warn("Invalid preserveBufferSharing option. Fallback: false.");
313
286
  preserveBufferSharing = false;
@@ -316,15 +289,14 @@ function resolveOptions(options) {
316
289
  console.warn("Invalid preserveDescriptors option. Fallback: false.");
317
290
  preserveDescriptors = false;
318
291
  }
319
- if (typeof strictDescriptors !== "boolean") {
320
- console.warn("Invalid strictDescriptors option. Fallback: false.");
321
- strictDescriptors = false;
292
+ if (typeof preserveSymbolKeys !== "boolean") {
293
+ console.warn("Invalid preserveSymbolKeys option. Fallback: false.");
294
+ preserveSymbolKeys = false;
322
295
  }
323
296
  return {
324
297
  preserveBufferSharing,
325
298
  preserveDescriptors,
326
- preserveSymbolKeys,
327
- strictDescriptors
299
+ preserveSymbolKeys
328
300
  };
329
301
  }
330
302
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunshin-clone",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "High-performance deep clone utility with descriptor support",
5
5
  "keywords": [
6
6
  "array",
@@ -68,9 +68,6 @@
68
68
  "prepublishOnly": "npm run build",
69
69
  "test": "bun test"
70
70
  },
71
- "dependencies": {
72
- "@y14e/own": "^1.0.1"
73
- },
74
71
  "devDependencies": {
75
72
  "bun-types": "latest",
76
73
  "tsup": "^8.0.0",
@@ -82,5 +79,8 @@
82
79
  },
83
80
  "allowScripts": {
84
81
  "esbuild": true
82
+ },
83
+ "dependencies": {
84
+ "@y14e/own": "^1.0.2"
85
85
  }
86
86
  }