@tanstack/form-core 0.42.0 → 0.42.1

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.
@@ -1,22 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function isValidKey(key) {
4
+ const dangerousProps = ["__proto__", "constructor", "prototype"];
5
+ return !dangerousProps.includes(String(key));
6
+ }
3
7
  function mutateMergeDeep(target, source) {
8
+ if (target === null || target === void 0 || typeof target !== "object")
9
+ return {};
10
+ if (source === null || source === void 0 || typeof source !== "object")
11
+ return target;
4
12
  const targetKeys = Object.keys(target);
5
13
  const sourceKeys = Object.keys(source);
6
14
  const keySet = /* @__PURE__ */ new Set([...targetKeys, ...sourceKeys]);
7
15
  for (const key of keySet) {
16
+ if (!isValidKey(key)) continue;
8
17
  const targetKey = key;
9
18
  const sourceKey = key;
10
- if (Array.isArray(target[targetKey]) && Array.isArray(source[sourceKey])) {
11
- target[targetKey] = source[sourceKey];
12
- } else if (typeof target[targetKey] === "object" && typeof source[sourceKey] === "object") {
13
- mutateMergeDeep(target[targetKey], source[sourceKey]);
14
- } else {
15
- if (!(sourceKey in source) && source[sourceKey] === void 0) {
16
- continue;
17
- }
18
- target[targetKey] = source[sourceKey];
19
+ if (!Object.hasOwn(source, sourceKey)) continue;
20
+ const sourceValue = source[sourceKey];
21
+ const targetValue = target[targetKey];
22
+ if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
23
+ Object.defineProperty(target, key, {
24
+ value: [...sourceValue],
25
+ enumerable: true,
26
+ writable: true,
27
+ configurable: true
28
+ });
29
+ continue;
30
+ }
31
+ const isTargetObj = typeof targetValue === "object" && targetValue !== null;
32
+ const isSourceObj = typeof sourceValue === "object" && sourceValue !== null;
33
+ const areObjects = isTargetObj && isSourceObj && !Array.isArray(targetValue) && !Array.isArray(sourceValue);
34
+ if (areObjects) {
35
+ mutateMergeDeep(targetValue, sourceValue);
36
+ continue;
19
37
  }
38
+ Object.defineProperty(target, key, {
39
+ value: sourceValue,
40
+ enumerable: true,
41
+ writable: true,
42
+ configurable: true
43
+ });
20
44
  }
21
45
  return target;
22
46
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mergeForm.cjs","sources":["../../src/mergeForm.ts"],"sourcesContent":["import type { FormApi } from './FormApi'\nimport type { Validator } from './types'\nimport type { NoInfer } from './util-types'\n\n/**\n * @private\n */\nexport function mutateMergeDeep(target: object, source: object): object {\n const targetKeys = Object.keys(target)\n const sourceKeys = Object.keys(source)\n const keySet = new Set([...targetKeys, ...sourceKeys])\n for (const key of keySet) {\n const targetKey = key as never as keyof typeof target\n const sourceKey = key as never as keyof typeof source\n\n if (Array.isArray(target[targetKey]) && Array.isArray(source[sourceKey])) {\n // always use the source array to prevent array fields from multiplying\n target[targetKey] = source[sourceKey] as [] as never\n } else if (\n typeof target[targetKey] === 'object' &&\n typeof source[sourceKey] === 'object'\n ) {\n mutateMergeDeep(target[targetKey] as {}, source[sourceKey] as {})\n } else {\n // Prevent assigning undefined to target, only if undefined is not explicitly set on source\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!(sourceKey in source) && source[sourceKey] === undefined) {\n continue\n }\n target[targetKey] = source[sourceKey] as never\n }\n }\n return target\n}\n\nexport function mergeForm<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>,\n state: Partial<FormApi<TFormData, TFormValidator>['state']>,\n) {\n mutateMergeDeep(baseForm.state, state)\n return baseForm\n}\n"],"names":[],"mappings":";;AAOgB,SAAA,gBAAgB,QAAgB,QAAwB;AAChE,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,6BAAa,IAAI,CAAC,GAAG,YAAY,GAAG,UAAU,CAAC;AACrD,aAAW,OAAO,QAAQ;AACxB,UAAM,YAAY;AAClB,UAAM,YAAY;AAEd,QAAA,MAAM,QAAQ,OAAO,SAAS,CAAC,KAAK,MAAM,QAAQ,OAAO,SAAS,CAAC,GAAG;AAEjE,aAAA,SAAS,IAAI,OAAO,SAAS;AAAA,IAAA,WAEpC,OAAO,OAAO,SAAS,MAAM,YAC7B,OAAO,OAAO,SAAS,MAAM,UAC7B;AACA,sBAAgB,OAAO,SAAS,GAAS,OAAO,SAAS,CAAO;AAAA,IAAA,OAC3D;AAGL,UAAI,EAAE,aAAa,WAAW,OAAO,SAAS,MAAM,QAAW;AAC7D;AAAA,MAAA;AAEK,aAAA,SAAS,IAAI,OAAO,SAAS;AAAA,IAAA;AAAA,EACtC;AAEK,SAAA;AACT;AAEgB,SAAA,UAId,UACA,OACA;AACgB,kBAAA,SAAS,OAAO,KAAK;AAC9B,SAAA;AACT;;;"}
1
+ {"version":3,"file":"mergeForm.cjs","sources":["../../src/mergeForm.ts"],"sourcesContent":["import type { FormApi } from './FormApi'\nimport type { Validator } from './types'\nimport type { NoInfer } from './util-types'\n\nfunction isValidKey(key: string | number | symbol): boolean {\n const dangerousProps = ['__proto__', 'constructor', 'prototype']\n return !dangerousProps.includes(String(key))\n}\n\n/**\n * @private\n */\nexport function mutateMergeDeep(\n target: object | null | undefined,\n source: object | null | undefined,\n): object {\n // Early return if either is not an object\n if (target === null || target === undefined || typeof target !== 'object')\n return {} as object\n if (source === null || source === undefined || typeof source !== 'object')\n return target\n\n const targetKeys = Object.keys(target)\n const sourceKeys = Object.keys(source)\n const keySet = new Set([...targetKeys, ...sourceKeys])\n\n for (const key of keySet) {\n if (!isValidKey(key)) continue\n\n const targetKey = key as keyof typeof target\n const sourceKey = key as keyof typeof source\n\n if (!Object.hasOwn(source, sourceKey)) continue\n\n const sourceValue = source[sourceKey] as unknown\n const targetValue = target[targetKey] as unknown\n\n // Handle arrays\n if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {\n Object.defineProperty(target, key, {\n value: [...sourceValue],\n enumerable: true,\n writable: true,\n configurable: true,\n })\n continue\n }\n\n // Handle nested objects (type assertion to satisfy ESLint)\n const isTargetObj = typeof targetValue === 'object' && targetValue !== null\n const isSourceObj = typeof sourceValue === 'object' && sourceValue !== null\n const areObjects =\n isTargetObj &&\n isSourceObj &&\n !Array.isArray(targetValue) &&\n !Array.isArray(sourceValue)\n\n if (areObjects) {\n mutateMergeDeep(targetValue as object, sourceValue as object)\n continue\n }\n\n // Handle all other cases\n Object.defineProperty(target, key, {\n value: sourceValue,\n enumerable: true,\n writable: true,\n configurable: true,\n })\n }\n\n return target\n}\n\nexport function mergeForm<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>,\n state: Partial<FormApi<TFormData, TFormValidator>['state']>,\n) {\n mutateMergeDeep(baseForm.state, state)\n return baseForm\n}\n"],"names":[],"mappings":";;AAIA,SAAS,WAAW,KAAwC;AAC1D,QAAM,iBAAiB,CAAC,aAAa,eAAe,WAAW;AAC/D,SAAO,CAAC,eAAe,SAAS,OAAO,GAAG,CAAC;AAC7C;AAKgB,SAAA,gBACd,QACA,QACQ;AAER,MAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW;AAC/D,WAAO,CAAC;AACV,MAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW;AACxD,WAAA;AAEH,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,6BAAa,IAAI,CAAC,GAAG,YAAY,GAAG,UAAU,CAAC;AAErD,aAAW,OAAO,QAAQ;AACpB,QAAA,CAAC,WAAW,GAAG,EAAG;AAEtB,UAAM,YAAY;AAClB,UAAM,YAAY;AAElB,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAS,EAAG;AAEjC,UAAA,cAAc,OAAO,SAAS;AAC9B,UAAA,cAAc,OAAO,SAAS;AAGpC,QAAI,MAAM,QAAQ,WAAW,KAAK,MAAM,QAAQ,WAAW,GAAG;AACrD,aAAA,eAAe,QAAQ,KAAK;AAAA,QACjC,OAAO,CAAC,GAAG,WAAW;AAAA,QACtB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,cAAc;AAAA,MAAA,CACf;AACD;AAAA,IAAA;AAIF,UAAM,cAAc,OAAO,gBAAgB,YAAY,gBAAgB;AACvE,UAAM,cAAc,OAAO,gBAAgB,YAAY,gBAAgB;AACjE,UAAA,aACJ,eACA,eACA,CAAC,MAAM,QAAQ,WAAW,KAC1B,CAAC,MAAM,QAAQ,WAAW;AAE5B,QAAI,YAAY;AACd,sBAAgB,aAAuB,WAAqB;AAC5D;AAAA,IAAA;AAIK,WAAA,eAAe,QAAQ,KAAK;AAAA,MACjC,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,cAAc;AAAA,IAAA,CACf;AAAA,EAAA;AAGI,SAAA;AACT;AAEgB,SAAA,UAId,UACA,OACA;AACgB,kBAAA,SAAS,OAAO,KAAK;AAC9B,SAAA;AACT;;;"}
@@ -4,5 +4,5 @@ import { NoInfer } from './util-types.cjs';
4
4
  /**
5
5
  * @private
6
6
  */
7
- export declare function mutateMergeDeep(target: object, source: object): object;
7
+ export declare function mutateMergeDeep(target: object | null | undefined, source: object | null | undefined): object;
8
8
  export declare function mergeForm<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined>(baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>, state: Partial<FormApi<TFormData, TFormValidator>['state']>): FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>;
@@ -4,5 +4,5 @@ import { NoInfer } from './util-types.js';
4
4
  /**
5
5
  * @private
6
6
  */
7
- export declare function mutateMergeDeep(target: object, source: object): object;
7
+ export declare function mutateMergeDeep(target: object | null | undefined, source: object | null | undefined): object;
8
8
  export declare function mergeForm<TFormData, TFormValidator extends Validator<TFormData, unknown> | undefined = undefined>(baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>, state: Partial<FormApi<TFormData, TFormValidator>['state']>): FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>;
@@ -1,20 +1,44 @@
1
+ function isValidKey(key) {
2
+ const dangerousProps = ["__proto__", "constructor", "prototype"];
3
+ return !dangerousProps.includes(String(key));
4
+ }
1
5
  function mutateMergeDeep(target, source) {
6
+ if (target === null || target === void 0 || typeof target !== "object")
7
+ return {};
8
+ if (source === null || source === void 0 || typeof source !== "object")
9
+ return target;
2
10
  const targetKeys = Object.keys(target);
3
11
  const sourceKeys = Object.keys(source);
4
12
  const keySet = /* @__PURE__ */ new Set([...targetKeys, ...sourceKeys]);
5
13
  for (const key of keySet) {
14
+ if (!isValidKey(key)) continue;
6
15
  const targetKey = key;
7
16
  const sourceKey = key;
8
- if (Array.isArray(target[targetKey]) && Array.isArray(source[sourceKey])) {
9
- target[targetKey] = source[sourceKey];
10
- } else if (typeof target[targetKey] === "object" && typeof source[sourceKey] === "object") {
11
- mutateMergeDeep(target[targetKey], source[sourceKey]);
12
- } else {
13
- if (!(sourceKey in source) && source[sourceKey] === void 0) {
14
- continue;
15
- }
16
- target[targetKey] = source[sourceKey];
17
+ if (!Object.hasOwn(source, sourceKey)) continue;
18
+ const sourceValue = source[sourceKey];
19
+ const targetValue = target[targetKey];
20
+ if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
21
+ Object.defineProperty(target, key, {
22
+ value: [...sourceValue],
23
+ enumerable: true,
24
+ writable: true,
25
+ configurable: true
26
+ });
27
+ continue;
28
+ }
29
+ const isTargetObj = typeof targetValue === "object" && targetValue !== null;
30
+ const isSourceObj = typeof sourceValue === "object" && sourceValue !== null;
31
+ const areObjects = isTargetObj && isSourceObj && !Array.isArray(targetValue) && !Array.isArray(sourceValue);
32
+ if (areObjects) {
33
+ mutateMergeDeep(targetValue, sourceValue);
34
+ continue;
17
35
  }
36
+ Object.defineProperty(target, key, {
37
+ value: sourceValue,
38
+ enumerable: true,
39
+ writable: true,
40
+ configurable: true
41
+ });
18
42
  }
19
43
  return target;
20
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mergeForm.js","sources":["../../src/mergeForm.ts"],"sourcesContent":["import type { FormApi } from './FormApi'\nimport type { Validator } from './types'\nimport type { NoInfer } from './util-types'\n\n/**\n * @private\n */\nexport function mutateMergeDeep(target: object, source: object): object {\n const targetKeys = Object.keys(target)\n const sourceKeys = Object.keys(source)\n const keySet = new Set([...targetKeys, ...sourceKeys])\n for (const key of keySet) {\n const targetKey = key as never as keyof typeof target\n const sourceKey = key as never as keyof typeof source\n\n if (Array.isArray(target[targetKey]) && Array.isArray(source[sourceKey])) {\n // always use the source array to prevent array fields from multiplying\n target[targetKey] = source[sourceKey] as [] as never\n } else if (\n typeof target[targetKey] === 'object' &&\n typeof source[sourceKey] === 'object'\n ) {\n mutateMergeDeep(target[targetKey] as {}, source[sourceKey] as {})\n } else {\n // Prevent assigning undefined to target, only if undefined is not explicitly set on source\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!(sourceKey in source) && source[sourceKey] === undefined) {\n continue\n }\n target[targetKey] = source[sourceKey] as never\n }\n }\n return target\n}\n\nexport function mergeForm<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>,\n state: Partial<FormApi<TFormData, TFormValidator>['state']>,\n) {\n mutateMergeDeep(baseForm.state, state)\n return baseForm\n}\n"],"names":[],"mappings":"AAOgB,SAAA,gBAAgB,QAAgB,QAAwB;AAChE,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,6BAAa,IAAI,CAAC,GAAG,YAAY,GAAG,UAAU,CAAC;AACrD,aAAW,OAAO,QAAQ;AACxB,UAAM,YAAY;AAClB,UAAM,YAAY;AAEd,QAAA,MAAM,QAAQ,OAAO,SAAS,CAAC,KAAK,MAAM,QAAQ,OAAO,SAAS,CAAC,GAAG;AAEjE,aAAA,SAAS,IAAI,OAAO,SAAS;AAAA,IAAA,WAEpC,OAAO,OAAO,SAAS,MAAM,YAC7B,OAAO,OAAO,SAAS,MAAM,UAC7B;AACA,sBAAgB,OAAO,SAAS,GAAS,OAAO,SAAS,CAAO;AAAA,IAAA,OAC3D;AAGL,UAAI,EAAE,aAAa,WAAW,OAAO,SAAS,MAAM,QAAW;AAC7D;AAAA,MAAA;AAEK,aAAA,SAAS,IAAI,OAAO,SAAS;AAAA,IAAA;AAAA,EACtC;AAEK,SAAA;AACT;AAEgB,SAAA,UAId,UACA,OACA;AACgB,kBAAA,SAAS,OAAO,KAAK;AAC9B,SAAA;AACT;"}
1
+ {"version":3,"file":"mergeForm.js","sources":["../../src/mergeForm.ts"],"sourcesContent":["import type { FormApi } from './FormApi'\nimport type { Validator } from './types'\nimport type { NoInfer } from './util-types'\n\nfunction isValidKey(key: string | number | symbol): boolean {\n const dangerousProps = ['__proto__', 'constructor', 'prototype']\n return !dangerousProps.includes(String(key))\n}\n\n/**\n * @private\n */\nexport function mutateMergeDeep(\n target: object | null | undefined,\n source: object | null | undefined,\n): object {\n // Early return if either is not an object\n if (target === null || target === undefined || typeof target !== 'object')\n return {} as object\n if (source === null || source === undefined || typeof source !== 'object')\n return target\n\n const targetKeys = Object.keys(target)\n const sourceKeys = Object.keys(source)\n const keySet = new Set([...targetKeys, ...sourceKeys])\n\n for (const key of keySet) {\n if (!isValidKey(key)) continue\n\n const targetKey = key as keyof typeof target\n const sourceKey = key as keyof typeof source\n\n if (!Object.hasOwn(source, sourceKey)) continue\n\n const sourceValue = source[sourceKey] as unknown\n const targetValue = target[targetKey] as unknown\n\n // Handle arrays\n if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {\n Object.defineProperty(target, key, {\n value: [...sourceValue],\n enumerable: true,\n writable: true,\n configurable: true,\n })\n continue\n }\n\n // Handle nested objects (type assertion to satisfy ESLint)\n const isTargetObj = typeof targetValue === 'object' && targetValue !== null\n const isSourceObj = typeof sourceValue === 'object' && sourceValue !== null\n const areObjects =\n isTargetObj &&\n isSourceObj &&\n !Array.isArray(targetValue) &&\n !Array.isArray(sourceValue)\n\n if (areObjects) {\n mutateMergeDeep(targetValue as object, sourceValue as object)\n continue\n }\n\n // Handle all other cases\n Object.defineProperty(target, key, {\n value: sourceValue,\n enumerable: true,\n writable: true,\n configurable: true,\n })\n }\n\n return target\n}\n\nexport function mergeForm<\n TFormData,\n TFormValidator extends Validator<TFormData, unknown> | undefined = undefined,\n>(\n baseForm: FormApi<NoInfer<TFormData>, NoInfer<TFormValidator>>,\n state: Partial<FormApi<TFormData, TFormValidator>['state']>,\n) {\n mutateMergeDeep(baseForm.state, state)\n return baseForm\n}\n"],"names":[],"mappings":"AAIA,SAAS,WAAW,KAAwC;AAC1D,QAAM,iBAAiB,CAAC,aAAa,eAAe,WAAW;AAC/D,SAAO,CAAC,eAAe,SAAS,OAAO,GAAG,CAAC;AAC7C;AAKgB,SAAA,gBACd,QACA,QACQ;AAER,MAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW;AAC/D,WAAO,CAAC;AACV,MAAI,WAAW,QAAQ,WAAW,UAAa,OAAO,WAAW;AACxD,WAAA;AAEH,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,aAAa,OAAO,KAAK,MAAM;AAC/B,QAAA,6BAAa,IAAI,CAAC,GAAG,YAAY,GAAG,UAAU,CAAC;AAErD,aAAW,OAAO,QAAQ;AACpB,QAAA,CAAC,WAAW,GAAG,EAAG;AAEtB,UAAM,YAAY;AAClB,UAAM,YAAY;AAElB,QAAI,CAAC,OAAO,OAAO,QAAQ,SAAS,EAAG;AAEjC,UAAA,cAAc,OAAO,SAAS;AAC9B,UAAA,cAAc,OAAO,SAAS;AAGpC,QAAI,MAAM,QAAQ,WAAW,KAAK,MAAM,QAAQ,WAAW,GAAG;AACrD,aAAA,eAAe,QAAQ,KAAK;AAAA,QACjC,OAAO,CAAC,GAAG,WAAW;AAAA,QACtB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,cAAc;AAAA,MAAA,CACf;AACD;AAAA,IAAA;AAIF,UAAM,cAAc,OAAO,gBAAgB,YAAY,gBAAgB;AACvE,UAAM,cAAc,OAAO,gBAAgB,YAAY,gBAAgB;AACjE,UAAA,aACJ,eACA,eACA,CAAC,MAAM,QAAQ,WAAW,KAC1B,CAAC,MAAM,QAAQ,WAAW;AAE5B,QAAI,YAAY;AACd,sBAAgB,aAAuB,WAAqB;AAC5D;AAAA,IAAA;AAIK,WAAA,eAAe,QAAQ,KAAK;AAAA,MACjC,OAAO;AAAA,MACP,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,cAAc;AAAA,IAAA,CACf;AAAA,EAAA;AAGI,SAAA;AACT;AAEgB,SAAA,UAId,UACA,OACA;AACgB,kBAAA,SAAS,OAAO,KAAK;AAC9B,SAAA;AACT;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/form-core",
3
- "version": "0.42.0",
3
+ "version": "0.42.1",
4
4
  "description": "Powerful, type-safe, framework agnostic forms.",
5
5
  "author": "tannerlinsley",
6
6
  "license": "MIT",
package/src/mergeForm.ts CHANGED
@@ -2,34 +2,73 @@ import type { FormApi } from './FormApi'
2
2
  import type { Validator } from './types'
3
3
  import type { NoInfer } from './util-types'
4
4
 
5
+ function isValidKey(key: string | number | symbol): boolean {
6
+ const dangerousProps = ['__proto__', 'constructor', 'prototype']
7
+ return !dangerousProps.includes(String(key))
8
+ }
9
+
5
10
  /**
6
11
  * @private
7
12
  */
8
- export function mutateMergeDeep(target: object, source: object): object {
13
+ export function mutateMergeDeep(
14
+ target: object | null | undefined,
15
+ source: object | null | undefined,
16
+ ): object {
17
+ // Early return if either is not an object
18
+ if (target === null || target === undefined || typeof target !== 'object')
19
+ return {} as object
20
+ if (source === null || source === undefined || typeof source !== 'object')
21
+ return target
22
+
9
23
  const targetKeys = Object.keys(target)
10
24
  const sourceKeys = Object.keys(source)
11
25
  const keySet = new Set([...targetKeys, ...sourceKeys])
26
+
12
27
  for (const key of keySet) {
13
- const targetKey = key as never as keyof typeof target
14
- const sourceKey = key as never as keyof typeof source
15
-
16
- if (Array.isArray(target[targetKey]) && Array.isArray(source[sourceKey])) {
17
- // always use the source array to prevent array fields from multiplying
18
- target[targetKey] = source[sourceKey] as [] as never
19
- } else if (
20
- typeof target[targetKey] === 'object' &&
21
- typeof source[sourceKey] === 'object'
22
- ) {
23
- mutateMergeDeep(target[targetKey] as {}, source[sourceKey] as {})
24
- } else {
25
- // Prevent assigning undefined to target, only if undefined is not explicitly set on source
26
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
27
- if (!(sourceKey in source) && source[sourceKey] === undefined) {
28
- continue
29
- }
30
- target[targetKey] = source[sourceKey] as never
28
+ if (!isValidKey(key)) continue
29
+
30
+ const targetKey = key as keyof typeof target
31
+ const sourceKey = key as keyof typeof source
32
+
33
+ if (!Object.hasOwn(source, sourceKey)) continue
34
+
35
+ const sourceValue = source[sourceKey] as unknown
36
+ const targetValue = target[targetKey] as unknown
37
+
38
+ // Handle arrays
39
+ if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
40
+ Object.defineProperty(target, key, {
41
+ value: [...sourceValue],
42
+ enumerable: true,
43
+ writable: true,
44
+ configurable: true,
45
+ })
46
+ continue
47
+ }
48
+
49
+ // Handle nested objects (type assertion to satisfy ESLint)
50
+ const isTargetObj = typeof targetValue === 'object' && targetValue !== null
51
+ const isSourceObj = typeof sourceValue === 'object' && sourceValue !== null
52
+ const areObjects =
53
+ isTargetObj &&
54
+ isSourceObj &&
55
+ !Array.isArray(targetValue) &&
56
+ !Array.isArray(sourceValue)
57
+
58
+ if (areObjects) {
59
+ mutateMergeDeep(targetValue as object, sourceValue as object)
60
+ continue
31
61
  }
62
+
63
+ // Handle all other cases
64
+ Object.defineProperty(target, key, {
65
+ value: sourceValue,
66
+ enumerable: true,
67
+ writable: true,
68
+ configurable: true,
69
+ })
32
70
  }
71
+
33
72
  return target
34
73
  }
35
74