assign-gingerly 0.0.32 → 0.0.33

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
@@ -110,7 +110,7 @@ console.log(obj);
110
110
 
111
111
  When the right hand side of an expression is an object, assignGingerly behavior depends on the context:
112
112
  - For **nested paths** (starting with `?.`): recursively merges into nested objects, creating them if needed
113
- - For **plain keys**: performs simple assignment (like `Object.assign`), unless the target property is readonly or a class instance (see Examples 3a and 3b below)
113
+ - For **plain keys**: performs simple assignment (like `Object.assign`), unless the target property is readonly or an accessor (see Examples 3a and 3b below)
114
114
 
115
115
  Of course, just as Object.assign led to object spread notation, assignGingerly could lead to some sort of deep structural JavaScript syntax, but that is outside the scope of this polyfill package.
116
116
 
@@ -144,38 +144,40 @@ console.log(obj.config); // { theme: 'dark' } - intermediate object created
144
144
 
145
145
  ## Example 3a - Automatic Readonly Property Detection
146
146
 
147
- assignGingerly automatically detects readonly properties and merges into them instead of attempting to replace them. This makes working with DOM properties like `style` and `dataset` much more ergonomic:
147
+ assignGingerly automatically detects readonly properties and merges into them instead of attempting to replace them. This makes working with DOM properties like `dataset` ergonomic:
148
148
 
149
149
  ```TypeScript
150
- // Instead of this verbose syntax:
151
150
  const div = document.createElement('div');
152
151
  assignGingerly(div, {
153
- '?.style?.height': '15px',
154
- '?.style?.width': '20px'
155
- });
156
-
157
- // You can now use this cleaner syntax:
158
- assignGingerly(div, {
159
- style: {
160
- height: '15px',
161
- width: '20px'
152
+ dataset: {
153
+ userId: '123',
154
+ userName: 'Alice'
162
155
  }
163
156
  });
164
- console.log(div.style.height); // '15px'
165
- console.log(div.style.width); // '20px'
157
+ console.log(div.dataset.userId); // '123'
158
+ console.log(div.dataset.userName); // 'Alice'
166
159
  ```
167
160
 
168
161
  **How it works:**
169
162
 
170
163
  When assignGingerly encounters an object value being assigned to an existing property, it checks if that property is readonly:
171
164
  - **Data properties** with `writable: false`
172
- - **Accessor properties** with a getter but no setter
165
+ - **Accessor properties** with a getter but no setter (e.g., `dataset`, `shadowRoot`)
173
166
 
174
167
  If the property is readonly and its current value is an object, assignGingerly automatically merges into it recursively.
175
168
 
176
- **Examples of readonly properties:**
177
- - `HTMLElement.style` - The CSSStyleDeclaration object
178
- - `HTMLElement.dataset` - The DOMStringMap object
169
+ **Note on `element.style`:** The `style` property has both a getter and a setter, so it is *not* treated as readonly. Use nested path syntax instead:
170
+
171
+ ```TypeScript
172
+ // Use nested path syntax for style
173
+ assignGingerly(div, {
174
+ '?.style?.height': '15px',
175
+ '?.style?.width': '20px'
176
+ });
177
+ ```
178
+
179
+ **Examples of readonly properties that trigger merging:**
180
+ - `HTMLElement.dataset` - getter only, no setter
179
181
  - Custom objects with `Object.defineProperty(obj, 'prop', { value: {}, writable: false })`
180
182
  - Accessor properties with getter only: `Object.defineProperty(obj, 'prop', { get() { return {}; } })`
181
183
 
@@ -225,134 +227,54 @@ assignGingerly(config, {
225
227
  console.log(config.settings.theme); // 'dark'
226
228
  ```
227
229
 
228
- ## Example 3b - Automatic Class Instance Preservation
230
+ ## Example 3b - Class Instances Are Replaced
229
231
 
230
- In addition to readonly property detection, assignGingerly automatically preserves class instances when merging. This is particularly useful when working with enhancement instances:
232
+ Unlike readonly/accessor properties, class instances on writable properties are **replaced** by simple assignment, just like plain objects. This allows you to swap one object for another without unexpected merging:
231
233
 
232
234
  ```TypeScript
233
- import 'assign-gingerly/object-extension.js';
234
-
235
- // Define an enhancement class
236
- class MyEnhancement {
237
- constructor(element, ctx, initVals) {
238
- this.element = element;
239
- this.instanceId = Math.random(); // Track instance identity
240
- if (initVals) {
241
- Object.assign(this, initVals);
242
- }
235
+ class FakeDocumentFragment {
236
+ constructor() {
237
+ this.nodeType = 11;
238
+ this.childNodes = [];
243
239
  }
244
- prop1 = null;
245
- prop2 = null;
246
240
  }
247
241
 
248
- const element = document.createElement('div');
249
- element.enh = {
250
- myEnh: new MyEnhancement(element, {}, {})
242
+ const obj = {
243
+ clone: new FakeDocumentFragment()
251
244
  };
252
245
 
253
- const originalId = element.enh.myEnh.instanceId;
254
-
255
- // Clean syntax - no need for ?.myEnh?.prop1 notation
256
- assignGingerly(element, {
257
- enh: {
258
- myEnh: {
259
- prop1: 'value1',
260
- prop2: 'value2'
261
- }
262
- }
263
- });
264
-
265
- console.log(element.enh.myEnh.instanceId === originalId); // true - instance preserved!
266
- console.log(element.enh.myEnh.prop1); // 'value1'
267
- console.log(element.enh.myEnh.prop2); // 'value2'
268
- ```
269
-
270
- **How it works:**
271
-
272
- When assignGingerly encounters an object value being assigned to an existing property, it checks if the current value is a class instance (not a plain object):
273
-
274
- - **Class instances** are detected by checking if their prototype is something other than `Object.prototype` or `null`
275
- - **Plain objects** `{}` have `Object.prototype` as their prototype
276
- - **Class instances** have their class's prototype
277
-
278
- If the existing value is a class instance, assignGingerly merges into it instead of replacing it.
279
-
280
- **What counts as a class instance:**
281
- - Custom class instances: `new MyClass()`
282
- - Built-in class instances: `new Date()`, `new Map()`, `new Set()`, etc.
283
- - Enhancement instances on the `enh` property
284
- - Any object whose prototype is not `Object.prototype` or `null`
285
-
286
- **What doesn't count:**
287
- - Plain objects: `{}`, `{ a: 1 }`
288
- - Arrays: `[]`, `[1, 2, 3]` (arrays are replaced, not merged)
289
- - Primitives: strings, numbers, booleans
290
-
291
- **Benefits:**
292
-
293
- This feature enables clean, framework-friendly syntax for updating enhancements:
246
+ const element = document.createElement('div');
294
247
 
295
- ```TypeScript
296
- // Before: Verbose nested path syntax
297
- assignGingerly(element, {
298
- '?.enh?.mellowYellow?.madAboutFourteen': true
248
+ // Replace the DocumentFragment with the actual element
249
+ assignGingerly(obj, {
250
+ clone: element
299
251
  });
300
252
 
301
- // After: Clean object syntax
302
- assignGingerly(element, {
303
- enh: {
304
- mellowYellow: {
305
- madAboutFourteen: true
306
- }
307
- }
308
- });
253
+ console.log(obj.clone === element); // true - replaced, not merged
309
254
  ```
310
255
 
311
- **Additional examples:**
312
-
313
- ```TypeScript
314
- // Multiple enhancements at once
315
- assignGingerly(element, {
316
- enh: {
317
- enhancement1: { prop: 'value1' },
318
- enhancement2: { prop: 'value2' }
319
- }
320
- });
321
-
322
- // Works with built-in classes too
323
- const obj = {
324
- timestamp: new Date('2024-01-01')
325
- };
256
+ **Why replacement instead of merging?**
326
257
 
327
- assignGingerly(obj, {
328
- timestamp: {
329
- customProp: 'metadata'
330
- }
331
- });
258
+ In real-world use cases, you often need to replace one object with another of a completely different type. For example, replacing a cloned DocumentFragment with the actual web component element. Automatic merging would corrupt the target by mixing properties from incompatible types.
332
259
 
333
- console.log(obj.timestamp instanceof Date); // true - Date instance preserved
334
- console.log(obj.timestamp.customProp); // 'metadata'
335
- ```
336
-
337
- **Combined with readonly detection:**
260
+ **Readonly/accessor properties are still merged:**
338
261
 
339
- Both readonly properties and class instances are preserved:
262
+ The distinction is clear:
263
+ - **Writable data properties**: always replaced (whether holding a plain object or class instance)
264
+ - **Readonly data properties** (`writable: false`): merged into
265
+ - **Getter-only accessor properties** (no setter): merged into
266
+ - **Getter+setter accessor properties** (e.g., `style`): setter runs with the value as-is
340
267
 
341
268
  ```TypeScript
342
269
  const div = document.createElement('div');
343
- div.enh = {
344
- myEnh: new MyEnhancement(div, {}, {})
345
- };
346
270
 
347
271
  assignGingerly(div, {
348
- style: { height: '100px' }, // Readonly - merged
349
- enh: {
350
- myEnh: { prop: 'value' } // Class instance - merged
351
- },
352
- dataset: { userId: '123' } // Readonly - merged
272
+ dataset: { userId: '123' }, // Getter-only - merged
273
+ '?.style?.height': '100px' // Use nested path for style
353
274
  });
354
275
 
355
- // All instances and readonly objects preserved
276
+ console.log(div.dataset.userId); // '123'
277
+ console.log(div.style.height); // '100px'
356
278
  ```
357
279
 
358
280
  ## Example 3c - Method Calls with withMethods
package/assignGingerly.js CHANGED
@@ -228,11 +228,14 @@ function ensureNestedPath(obj, pathParts) {
228
228
  return current;
229
229
  }
230
230
  /**
231
- * Helper function to check if a property is readonly
232
- * A property is readonly if:
231
+ * Helper function to check if a property should be merged into rather than replaced.
232
+ * A property is non-replaceable if:
233
233
  * - It's a data property with writable: false, OR
234
234
  * - It's an accessor property with a getter but no setter
235
235
  *
236
+ * Properties with both a getter and setter (e.g., element.style) are treated as
237
+ * replaceable — the setter runs with whatever value is provided (garbage in, garbage out).
238
+ *
236
239
  * Exported for use by eachTime.ts
237
240
  */
238
241
  export function isReadonlyProperty(obj, propName) {
@@ -253,8 +256,8 @@ export function isReadonlyProperty(obj, propName) {
253
256
  if ('value' in descriptor) {
254
257
  return descriptor.writable === false;
255
258
  }
256
- // If it's an accessor property, check if it has only a getter (no setter)
257
- if ('get' in descriptor) {
259
+ // If it's an accessor property with a getter but no setter, it's readonly
260
+ if ('get' in descriptor && descriptor.get !== undefined) {
258
261
  return descriptor.set === undefined;
259
262
  }
260
263
  return false;
@@ -439,10 +442,10 @@ function applyToEach(iterable, remainingPath, value, withMethods, aliasMap, opti
439
442
  const lastKey = result.lastKey;
440
443
  const parent = result.target;
441
444
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
442
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
445
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
443
446
  const currentValue = parent[lastKey];
444
447
  if (typeof currentValue !== 'object' || currentValue === null) {
445
- throw new Error(`Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`);
448
+ throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
446
449
  }
447
450
  assignGingerly(currentValue, value, options);
448
451
  }
@@ -729,16 +732,16 @@ export function assignGingerly(target, source, options) {
729
732
  const lastKey = result.lastKey;
730
733
  const parent = result.target;
731
734
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
732
- // Check if property exists and is readonly OR is a class instance
733
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
735
+ // Check if property exists and is readonly
736
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
734
737
  const currentValue = parent[lastKey];
735
738
  if (typeof currentValue !== 'object' || currentValue === null) {
736
- throw new Error(`Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`);
739
+ throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
737
740
  }
738
741
  assignGingerly(currentValue, value, options);
739
742
  }
740
743
  else {
741
- // Property is writable and not a class instance - replace it
744
+ // Property is writable - replace it
742
745
  parent[lastKey] = value;
743
746
  }
744
747
  }
@@ -751,18 +754,18 @@ export function assignGingerly(target, source, options) {
751
754
  const lastKey = pathParts[pathParts.length - 1];
752
755
  const parent = ensureNestedPath(target, pathParts);
753
756
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
754
- // Check if property exists and is readonly OR is a class instance
755
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
756
- // Property is readonly or a class instance - check if current value is an object
757
+ // Check if property exists and is readonly
758
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
759
+ // Property is readonly - check if current value is an object
757
760
  const currentValue = parent[lastKey];
758
761
  if (typeof currentValue !== 'object' || currentValue === null) {
759
- throw new Error(`Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`);
762
+ throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
760
763
  }
761
- // Recursively apply assignGingerly to the readonly object or class instance
764
+ // Recursively apply assignGingerly to the readonly object
762
765
  assignGingerly(currentValue, value, options);
763
766
  }
764
767
  else {
765
- // Property is writable and not a class instance - replace it
768
+ // Property is writable - replace it
766
769
  parent[lastKey] = value;
767
770
  }
768
771
  }
@@ -789,18 +792,18 @@ export function assignGingerly(target, source, options) {
789
792
  }
790
793
  // Normal assignment
791
794
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
792
- // Check if property exists and is readonly OR is a class instance
793
- if (key in target && (isReadonlyProperty(target, key) || isClassInstance(target[key]))) {
794
- // Property is readonly or a class instance - check if current value is an object
795
+ // Check if property exists and is readonly
796
+ if (key in target && isReadonlyProperty(target, key)) {
797
+ // Property is readonly - check if current value is an object
795
798
  const currentValue = target[key];
796
799
  if (typeof currentValue !== 'object' || currentValue === null) {
797
- throw new Error(`Cannot merge object into ${isReadonlyProperty(target, key) ? 'readonly ' : ''}primitive property '${String(key)}'`);
800
+ throw new Error(`Cannot merge object into readonly primitive property '${String(key)}'`);
798
801
  }
799
- // Recursively apply assignGingerly to the readonly object or class instance
802
+ // Recursively apply assignGingerly to the readonly object
800
803
  assignGingerly(currentValue, value, options);
801
804
  }
802
805
  else {
803
- // Property is writable and not a class instance - replace it
806
+ // Property is writable - replace it
804
807
  target[key] = value;
805
808
  }
806
809
  }
package/assignGingerly.ts CHANGED
@@ -356,11 +356,14 @@ function ensureNestedPath(obj: any, pathParts: string[]): any {
356
356
  }
357
357
 
358
358
  /**
359
- * Helper function to check if a property is readonly
360
- * A property is readonly if:
359
+ * Helper function to check if a property should be merged into rather than replaced.
360
+ * A property is non-replaceable if:
361
361
  * - It's a data property with writable: false, OR
362
362
  * - It's an accessor property with a getter but no setter
363
363
  *
364
+ * Properties with both a getter and setter (e.g., element.style) are treated as
365
+ * replaceable — the setter runs with whatever value is provided (garbage in, garbage out).
366
+ *
364
367
  * Exported for use by eachTime.ts
365
368
  */
366
369
  export function isReadonlyProperty(obj: any, propName: string | symbol): boolean {
@@ -383,8 +386,8 @@ export function isReadonlyProperty(obj: any, propName: string | symbol): boolean
383
386
  return descriptor.writable === false;
384
387
  }
385
388
 
386
- // If it's an accessor property, check if it has only a getter (no setter)
387
- if ('get' in descriptor) {
389
+ // If it's an accessor property with a getter but no setter, it's readonly
390
+ if ('get' in descriptor && descriptor.get !== undefined) {
388
391
  return descriptor.set === undefined;
389
392
  }
390
393
 
@@ -589,10 +592,10 @@ function applyToEach(
589
592
  const parent = result.target;
590
593
 
591
594
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
592
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
595
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
593
596
  const currentValue = parent[lastKey];
594
597
  if (typeof currentValue !== 'object' || currentValue === null) {
595
- throw new Error(`Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`);
598
+ throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
596
599
  }
597
600
  assignGingerly(currentValue, value, options);
598
601
  } else {
@@ -918,15 +921,15 @@ export function assignGingerly(
918
921
  const parent = result.target;
919
922
 
920
923
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
921
- // Check if property exists and is readonly OR is a class instance
922
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
924
+ // Check if property exists and is readonly
925
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
923
926
  const currentValue = parent[lastKey];
924
927
  if (typeof currentValue !== 'object' || currentValue === null) {
925
- throw new Error(`Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`);
928
+ throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
926
929
  }
927
930
  assignGingerly(currentValue, value, options);
928
931
  } else {
929
- // Property is writable and not a class instance - replace it
932
+ // Property is writable - replace it
930
933
  parent[lastKey] = value;
931
934
  }
932
935
  } else {
@@ -938,17 +941,17 @@ export function assignGingerly(
938
941
  const parent = ensureNestedPath(target, pathParts);
939
942
 
940
943
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
941
- // Check if property exists and is readonly OR is a class instance
942
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
943
- // Property is readonly or a class instance - check if current value is an object
944
+ // Check if property exists and is readonly
945
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
946
+ // Property is readonly - check if current value is an object
944
947
  const currentValue = parent[lastKey];
945
948
  if (typeof currentValue !== 'object' || currentValue === null) {
946
- throw new Error(`Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`);
949
+ throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
947
950
  }
948
- // Recursively apply assignGingerly to the readonly object or class instance
951
+ // Recursively apply assignGingerly to the readonly object
949
952
  assignGingerly(currentValue, value, options);
950
953
  } else {
951
- // Property is writable and not a class instance - replace it
954
+ // Property is writable - replace it
952
955
  parent[lastKey] = value;
953
956
  }
954
957
  } else {
@@ -974,17 +977,17 @@ export function assignGingerly(
974
977
 
975
978
  // Normal assignment
976
979
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
977
- // Check if property exists and is readonly OR is a class instance
978
- if (key in target && (isReadonlyProperty(target, key) || isClassInstance(target[key]))) {
979
- // Property is readonly or a class instance - check if current value is an object
980
+ // Check if property exists and is readonly
981
+ if (key in target && isReadonlyProperty(target, key)) {
982
+ // Property is readonly - check if current value is an object
980
983
  const currentValue = target[key];
981
984
  if (typeof currentValue !== 'object' || currentValue === null) {
982
- throw new Error(`Cannot merge object into ${isReadonlyProperty(target, key) ? 'readonly ' : ''}primitive property '${String(key)}'`);
985
+ throw new Error(`Cannot merge object into readonly primitive property '${String(key)}'`);
983
986
  }
984
- // Recursively apply assignGingerly to the readonly object or class instance
987
+ // Recursively apply assignGingerly to the readonly object
985
988
  assignGingerly(currentValue, value, options);
986
989
  } else {
987
- // Property is writable and not a class instance - replace it
990
+ // Property is writable - replace it
988
991
  target[key] = value;
989
992
  }
990
993
  } else {
package/eachTime.js CHANGED
@@ -58,7 +58,7 @@ export async function handleEachTime(target, pathParts, forEachIndex, value, wit
58
58
  (async () => {
59
59
  try {
60
60
  // Import needed functions from assignGingerly
61
- const { evaluatePathWithMethods, assignGingerly, isReadonlyProperty, isClassInstance } = await import('./assignGingerly.js');
61
+ const { evaluatePathWithMethods, assignGingerly, isReadonlyProperty } = await import('./assignGingerly.js');
62
62
  if (pathAfterForEach.length > 0) {
63
63
  const result = evaluatePathWithMethods(mountedElement, pathAfterForEach, value, withMethods || new Set());
64
64
  if (result.isMethod) {
@@ -78,17 +78,17 @@ export async function handleEachTime(target, pathParts, forEachIndex, value, wit
78
78
  const lastKey = result.lastKey;
79
79
  const parent = result.target;
80
80
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
81
- // Check if property exists and is readonly OR is a class instance
82
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
81
+ // Check if property exists and is readonly
82
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
83
83
  const currentValue = parent[lastKey];
84
84
  if (typeof currentValue !== 'object' || currentValue === null) {
85
- throw new Error(`Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`);
85
+ throw new Error(`Cannot merge object into readonly primitive property '${String(lastKey)}'`);
86
86
  }
87
87
  // Recursively apply assignGingerly
88
88
  assignGingerly(currentValue, value, options);
89
89
  }
90
90
  else {
91
- // Property is writable and not a class instance - replace it
91
+ // Property is writable - replace it
92
92
  parent[lastKey] = value;
93
93
  }
94
94
  }
package/eachTime.ts CHANGED
@@ -77,8 +77,7 @@ export async function handleEachTime(
77
77
  const {
78
78
  evaluatePathWithMethods,
79
79
  assignGingerly,
80
- isReadonlyProperty,
81
- isClassInstance
80
+ isReadonlyProperty
82
81
  } = await import('./assignGingerly.js');
83
82
 
84
83
  if (pathAfterForEach.length > 0) {
@@ -105,18 +104,18 @@ export async function handleEachTime(
105
104
  const parent = result.target;
106
105
 
107
106
  if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
108
- // Check if property exists and is readonly OR is a class instance
109
- if (lastKey in parent && (isReadonlyProperty(parent, lastKey) || isClassInstance(parent[lastKey]))) {
107
+ // Check if property exists and is readonly
108
+ if (lastKey in parent && isReadonlyProperty(parent, lastKey)) {
110
109
  const currentValue = parent[lastKey];
111
110
  if (typeof currentValue !== 'object' || currentValue === null) {
112
111
  throw new Error(
113
- `Cannot merge object into ${isReadonlyProperty(parent, lastKey) ? 'readonly ' : ''}primitive property '${String(lastKey)}'`
112
+ `Cannot merge object into readonly primitive property '${String(lastKey)}'`
114
113
  );
115
114
  }
116
115
  // Recursively apply assignGingerly
117
116
  assignGingerly(currentValue, value, options);
118
117
  } else {
119
- // Property is writable and not a class instance - replace it
118
+ // Property is writable - replace it
120
119
  parent[lastKey] = value;
121
120
  }
122
121
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assign-gingerly",
3
- "version": "0.0.32",
3
+ "version": "0.0.33",
4
4
  "description": "This package provides a utility function for carefully merging one object into another.",
5
5
  "homepage": "https://github.com/bahrus/assign-gingerly#readme",
6
6
  "bugs": {