kist 0.1.30 → 0.1.31

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,68 +1,52 @@
1
1
  import { ConfigInterface } from "../../interface/ConfigInterface";
2
2
  import { AbstractProcess } from "../abstract/AbstractProcess";
3
3
  /**
4
- * ConfigStore is a singleton that loads and manages the application's
5
- * configuration.
4
+ * ConfigStore is a singleton that loads and manages the application's configuration.
6
5
  * It prioritizes CLI arguments over configuration file values.
7
6
  */
8
7
  export declare class ConfigStore extends AbstractProcess {
9
- /**
10
- * Singleton instance of the ConfigStore.
11
- */
12
8
  private static instance;
13
- /**
14
- * The current configuration stored in the ConfigStore.
15
- */
16
9
  private config;
17
- /**
18
- * Private constructor to enforce the singleton pattern.
19
- * Initializes the store with the default configuration.
20
- */
21
10
  private constructor();
22
11
  /**
23
- * Retrieves the singleton instance of ConfigStore, initializing it if
24
- * necessary.
12
+ * Retrieves the singleton instance of ConfigStore.
25
13
  * @returns The singleton instance of ConfigStore.
26
14
  */
27
15
  static getInstance(): ConfigStore;
28
16
  /**
29
- * Retrieves a value from the configuration by key.
30
- * Supports nested keys using dot notation (e.g., "options.logLevel").
17
+ * Retrieves a value from the configuration using dot notation.
31
18
  *
32
19
  * @param key - The key of the configuration to retrieve.
33
20
  * @returns The configuration value or undefined if not found.
34
21
  */
35
22
  get<T>(key: string): T | undefined;
36
23
  /**
37
- * Sets a value in the configuration by key.
38
- * Supports nested keys using dot notation (e.g., "options.logLevel").
24
+ * Sets a value in the configuration using dot notation.
39
25
  *
40
26
  * @param key - The key of the configuration to set.
41
27
  * @param value - The value to set.
42
28
  */
43
29
  set(key: string, value: unknown): void;
44
30
  /**
45
- * Merges the provided configuration into the existing configuration.
46
- * Uses a deep merge strategy to combine objects and overwrite primitives.
31
+ * Merges the provided configuration into the existing configuration using deep merge.
47
32
  *
48
33
  * @param newConfig - The new configuration to merge.
49
34
  */
50
35
  merge(newConfig: Partial<ConfigInterface>): void;
51
36
  /**
52
- * Retrieves the current configuration.
53
- *
54
- * @returns The current configuration object.
37
+ * Retrieves the current configuration object.
38
+ * @returns The current configuration.
55
39
  */
56
40
  getConfig(): ConfigInterface;
57
41
  /**
58
- * Prints the current configuration to the console in a readable format.
42
+ * Prints the current configuration to the console.
59
43
  */
60
44
  print(): void;
61
45
  /**
62
- * Deeply merges two objects.
46
+ * Deeply merges two objects, preventing prototype pollution.
63
47
  *
64
- * @param target - The target object to merge into.
65
- * @param source - The source object to merge from.
48
+ * @param target - The target object.
49
+ * @param source - The source object.
66
50
  * @returns The merged object.
67
51
  */
68
52
  private deepMerge;
@@ -10,27 +10,18 @@ const defaultConfig_1 = require("./defaultConfig");
10
10
  // Class
11
11
  // ============================================================================
12
12
  /**
13
- * ConfigStore is a singleton that loads and manages the application's
14
- * configuration.
13
+ * ConfigStore is a singleton that loads and manages the application's configuration.
15
14
  * It prioritizes CLI arguments over configuration file values.
16
15
  */
17
16
  class ConfigStore extends AbstractProcess_1.AbstractProcess {
18
- // Constructor
19
- // ========================================================================
20
- /**
21
- * Private constructor to enforce the singleton pattern.
22
- * Initializes the store with the default configuration.
23
- */
17
+ // Constructor (Private to enforce Singleton Pattern)
24
18
  constructor() {
25
19
  super();
26
20
  this.config = defaultConfig_1.defaultConfig;
27
21
  this.logDebug("ConfigStore initialized with default configuration.");
28
22
  }
29
- // Static Methods
30
- // ========================================================================
31
23
  /**
32
- * Retrieves the singleton instance of ConfigStore, initializing it if
33
- * necessary.
24
+ * Retrieves the singleton instance of ConfigStore.
34
25
  * @returns The singleton instance of ConfigStore.
35
26
  */
36
27
  static getInstance() {
@@ -39,11 +30,8 @@ class ConfigStore extends AbstractProcess_1.AbstractProcess {
39
30
  }
40
31
  return ConfigStore.instance;
41
32
  }
42
- // Instance Methods
43
- // ========================================================================
44
33
  /**
45
- * Retrieves a value from the configuration by key.
46
- * Supports nested keys using dot notation (e.g., "options.logLevel").
34
+ * Retrieves a value from the configuration using dot notation.
47
35
  *
48
36
  * @param key - The key of the configuration to retrieve.
49
37
  * @returns The configuration value or undefined if not found.
@@ -57,12 +45,11 @@ class ConfigStore extends AbstractProcess_1.AbstractProcess {
57
45
  }
58
46
  current = current[k];
59
47
  }
60
- this.logDebug(`Configuration key "${key}" retrieved with value: ${JSON.stringify(current)}`);
48
+ this.logDebug(`Configuration key "${key}" retrieved.`);
61
49
  return current;
62
50
  }
63
51
  /**
64
- * Sets a value in the configuration by key.
65
- * Supports nested keys using dot notation (e.g., "options.logLevel").
52
+ * Sets a value in the configuration using dot notation.
66
53
  *
67
54
  * @param key - The key of the configuration to set.
68
55
  * @param value - The value to set.
@@ -72,17 +59,28 @@ class ConfigStore extends AbstractProcess_1.AbstractProcess {
72
59
  let current = this.config;
73
60
  for (let i = 0; i < keys.length - 1; i++) {
74
61
  const k = keys[i];
75
- if (!current[k] || typeof current[k] !== "object") {
76
- current[k] = {};
62
+ // Prevent prototype pollution by blocking reserved keywords
63
+ if (["__proto__", "constructor", "prototype"].includes(k)) {
64
+ this.logWarn(`Attempted prototype pollution detected: "${k}"`);
65
+ return;
66
+ }
67
+ // Ensure property exists and is an object
68
+ if (!Object.prototype.hasOwnProperty.call(current, k) || typeof current[k] !== "object") {
69
+ current[k] = Object.create(null); // Use a null prototype object
77
70
  }
78
71
  current = current[k];
79
72
  }
80
- current[keys[keys.length - 1]] = value;
73
+ const finalKey = keys[keys.length - 1];
74
+ // Prevent prototype pollution at the final assignment
75
+ if (["__proto__", "constructor", "prototype"].includes(finalKey)) {
76
+ this.logWarn(`Attempted prototype pollution detected: "${finalKey}"`);
77
+ return;
78
+ }
79
+ current[finalKey] = value;
81
80
  this.logDebug(`Set configuration key "${key}" to: ${JSON.stringify(value)}`);
82
81
  }
83
82
  /**
84
- * Merges the provided configuration into the existing configuration.
85
- * Uses a deep merge strategy to combine objects and overwrite primitives.
83
+ * Merges the provided configuration into the existing configuration using deep merge.
86
84
  *
87
85
  * @param newConfig - The new configuration to merge.
88
86
  */
@@ -91,64 +89,38 @@ class ConfigStore extends AbstractProcess_1.AbstractProcess {
91
89
  this.logDebug("Configuration successfully merged.");
92
90
  }
93
91
  /**
94
- * Retrieves the current configuration.
95
- *
96
- * @returns The current configuration object.
92
+ * Retrieves the current configuration object.
93
+ * @returns The current configuration.
97
94
  */
98
95
  getConfig() {
99
96
  return this.config;
100
97
  }
101
98
  /**
102
- * Prints the current configuration to the console in a readable format.
99
+ * Prints the current configuration to the console.
103
100
  */
104
101
  print() {
105
- console.log("Current Configuration:");
106
- console.log(JSON.stringify(this.config, null, 2));
102
+ console.log("Current Configuration:", JSON.stringify(this.config, null, 2));
107
103
  }
108
104
  /**
109
- * Deeply merges two objects.
105
+ * Deeply merges two objects, preventing prototype pollution.
110
106
  *
111
- * @param target - The target object to merge into.
112
- * @param source - The source object to merge from.
107
+ * @param target - The target object.
108
+ * @param source - The source object.
113
109
  * @returns The merged object.
114
110
  */
115
- // private deepMerge(target: any, source: any): any {
116
- // if (typeof target !== "object" || target === null) {
117
- // return source;
118
- // }
119
- // for (const key of Object.keys(source)) {
120
- // if (
121
- // source[key] &&
122
- // typeof source[key] === "object" &&
123
- // !Array.isArray(source[key])
124
- // ) {
125
- // if (!target[key] || typeof target[key] !== "object") {
126
- // target[key] = {};
127
- // }
128
- // target[key] = this.deepMerge(target[key], source[key]);
129
- // } else {
130
- // target[key] = source[key];
131
- // }
132
- // }
133
- // return target;
134
- // }
135
111
  deepMerge(target, source) {
136
112
  if (typeof target !== "object" || target === null) {
137
113
  return source;
138
114
  }
139
115
  for (const key of Object.keys(source)) {
140
116
  // Prevent prototype pollution
141
- if (key === "__proto__" ||
142
- key === "constructor" ||
143
- key === "prototype") {
144
- this.logWarn(`Skipping potentially unsafe key: "${key}"`);
117
+ if (["__proto__", "constructor", "prototype"].includes(key)) {
118
+ this.logWarn(`Skipping unsafe key during merge: "${key}"`);
145
119
  continue;
146
120
  }
147
- if (source[key] &&
148
- typeof source[key] === "object" &&
149
- !Array.isArray(source[key])) {
150
- if (!target[key] || typeof target[key] !== "object") {
151
- target[key] = {};
121
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
122
+ if (!Object.prototype.hasOwnProperty.call(target, key) || typeof target[key] !== "object") {
123
+ target[key] = Object.create(null);
152
124
  }
153
125
  target[key] = this.deepMerge(target[key], source[key]);
154
126
  }
@@ -160,9 +132,5 @@ class ConfigStore extends AbstractProcess_1.AbstractProcess {
160
132
  }
161
133
  }
162
134
  exports.ConfigStore = ConfigStore;
163
- // Parameters
164
- // ========================================================================
165
- /**
166
- * Singleton instance of the ConfigStore.
167
- */
135
+ // Singleton instance
168
136
  ConfigStore.instance = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kist",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "Package Pipeline Processor",
5
5
  "keywords": [
6
6
  "kist",
@@ -0,0 +1,201 @@
1
+ // ============================================================================
2
+ // Import
3
+ // ============================================================================
4
+
5
+ import { ConfigInterface } from "../../interface/ConfigInterface";
6
+ import { AbstractProcess } from "../abstract/AbstractProcess";
7
+ import { defaultConfig } from "./defaultConfig";
8
+
9
+ // ============================================================================
10
+ // Class
11
+ // ============================================================================
12
+
13
+ /**
14
+ * ConfigStore is a singleton that loads and manages the application's
15
+ * configuration.
16
+ * It prioritizes CLI arguments over configuration file values.
17
+ */
18
+ export class ConfigStore extends AbstractProcess {
19
+ // Parameters
20
+ // ========================================================================
21
+
22
+ /**
23
+ * Singleton instance of the ConfigStore.
24
+ */
25
+ private static instance: ConfigStore | null = null;
26
+
27
+ /**
28
+ * The current configuration stored in the ConfigStore.
29
+ */
30
+ private config: ConfigInterface;
31
+
32
+ // Constructor
33
+ // ========================================================================
34
+
35
+ /**
36
+ * Private constructor to enforce the singleton pattern.
37
+ * Initializes the store with the default configuration.
38
+ */
39
+ private constructor() {
40
+ super();
41
+ this.config = defaultConfig;
42
+ this.logDebug("ConfigStore initialized with default configuration.");
43
+ }
44
+
45
+ // Static Methods
46
+ // ========================================================================
47
+
48
+ /**
49
+ * Retrieves the singleton instance of ConfigStore, initializing it if
50
+ * necessary.
51
+ * @returns The singleton instance of ConfigStore.
52
+ */
53
+ public static getInstance(): ConfigStore {
54
+ if (!ConfigStore.instance) {
55
+ ConfigStore.instance = new ConfigStore();
56
+ }
57
+ return ConfigStore.instance;
58
+ }
59
+
60
+ // Instance Methods
61
+ // ========================================================================
62
+
63
+ /**
64
+ * Retrieves a value from the configuration by key.
65
+ * Supports nested keys using dot notation (e.g., "options.logLevel").
66
+ *
67
+ * @param key - The key of the configuration to retrieve.
68
+ * @returns The configuration value or undefined if not found.
69
+ */
70
+ public get<T>(key: string): T | undefined {
71
+ const keys = key.split(".");
72
+ let current: any = this.config;
73
+
74
+ for (const k of keys) {
75
+ if (current[k] === undefined) {
76
+ return undefined;
77
+ }
78
+ current = current[k];
79
+ }
80
+
81
+ this.logDebug(
82
+ `Configuration key "${key}" retrieved with value: ${JSON.stringify(current)}`,
83
+ );
84
+ return current as T;
85
+ }
86
+
87
+ /**
88
+ * Sets a value in the configuration by key.
89
+ * Supports nested keys using dot notation (e.g., "options.logLevel").
90
+ *
91
+ * @param key - The key of the configuration to set.
92
+ * @param value - The value to set.
93
+ */
94
+ public set(key: string, value: unknown): void {
95
+ const keys = key.split(".");
96
+ let current: any = this.config;
97
+
98
+ for (let i = 0; i < keys.length - 1; i++) {
99
+ const k = keys[i];
100
+ if (!current[k] || typeof current[k] !== "object") {
101
+ current[k] = {};
102
+ }
103
+ current = current[k];
104
+ }
105
+
106
+ current[keys[keys.length - 1]] = value;
107
+ this.logDebug(
108
+ `Set configuration key "${key}" to: ${JSON.stringify(value)}`,
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Merges the provided configuration into the existing configuration.
114
+ * Uses a deep merge strategy to combine objects and overwrite primitives.
115
+ *
116
+ * @param newConfig - The new configuration to merge.
117
+ */
118
+ public merge(newConfig: Partial<ConfigInterface>): void {
119
+ this.config = this.deepMerge(this.config, newConfig);
120
+ this.logDebug("Configuration successfully merged.");
121
+ }
122
+
123
+ /**
124
+ * Retrieves the current configuration.
125
+ *
126
+ * @returns The current configuration object.
127
+ */
128
+ public getConfig(): ConfigInterface {
129
+ return this.config;
130
+ }
131
+
132
+ /**
133
+ * Prints the current configuration to the console in a readable format.
134
+ */
135
+ public print(): void {
136
+ console.log("Current Configuration:");
137
+ console.log(JSON.stringify(this.config, null, 2));
138
+ }
139
+
140
+ /**
141
+ * Deeply merges two objects.
142
+ *
143
+ * @param target - The target object to merge into.
144
+ * @param source - The source object to merge from.
145
+ * @returns The merged object.
146
+ */
147
+ // private deepMerge(target: any, source: any): any {
148
+ // if (typeof target !== "object" || target === null) {
149
+ // return source;
150
+ // }
151
+
152
+ // for (const key of Object.keys(source)) {
153
+ // if (
154
+ // source[key] &&
155
+ // typeof source[key] === "object" &&
156
+ // !Array.isArray(source[key])
157
+ // ) {
158
+ // if (!target[key] || typeof target[key] !== "object") {
159
+ // target[key] = {};
160
+ // }
161
+ // target[key] = this.deepMerge(target[key], source[key]);
162
+ // } else {
163
+ // target[key] = source[key];
164
+ // }
165
+ // }
166
+
167
+ // return target;
168
+ // }
169
+ private deepMerge(target: any, source: any): any {
170
+ if (typeof target !== "object" || target === null) {
171
+ return source;
172
+ }
173
+
174
+ for (const key of Object.keys(source)) {
175
+ // Prevent prototype pollution
176
+ if (
177
+ key === "__proto__" ||
178
+ key === "constructor" ||
179
+ key === "prototype"
180
+ ) {
181
+ this.logWarn(`Skipping potentially unsafe key: "${key}"`);
182
+ continue;
183
+ }
184
+
185
+ if (
186
+ source[key] &&
187
+ typeof source[key] === "object" &&
188
+ !Array.isArray(source[key])
189
+ ) {
190
+ if (!target[key] || typeof target[key] !== "object") {
191
+ target[key] = {};
192
+ }
193
+ target[key] = this.deepMerge(target[key], source[key]);
194
+ } else {
195
+ target[key] = source[key];
196
+ }
197
+ }
198
+
199
+ return target;
200
+ }
201
+ }
@@ -11,43 +11,25 @@ import { defaultConfig } from "./defaultConfig";
11
11
  // ============================================================================
12
12
 
13
13
  /**
14
- * ConfigStore is a singleton that loads and manages the application's
15
- * configuration.
14
+ * ConfigStore is a singleton that loads and manages the application's configuration.
16
15
  * It prioritizes CLI arguments over configuration file values.
17
16
  */
18
17
  export class ConfigStore extends AbstractProcess {
19
- // Parameters
20
- // ========================================================================
21
-
22
- /**
23
- * Singleton instance of the ConfigStore.
24
- */
18
+ // Singleton instance
25
19
  private static instance: ConfigStore | null = null;
26
20
 
27
- /**
28
- * The current configuration stored in the ConfigStore.
29
- */
21
+ // The current configuration stored in the ConfigStore.
30
22
  private config: ConfigInterface;
31
23
 
32
- // Constructor
33
- // ========================================================================
34
-
35
- /**
36
- * Private constructor to enforce the singleton pattern.
37
- * Initializes the store with the default configuration.
38
- */
24
+ // Constructor (Private to enforce Singleton Pattern)
39
25
  private constructor() {
40
26
  super();
41
27
  this.config = defaultConfig;
42
28
  this.logDebug("ConfigStore initialized with default configuration.");
43
29
  }
44
30
 
45
- // Static Methods
46
- // ========================================================================
47
-
48
31
  /**
49
- * Retrieves the singleton instance of ConfigStore, initializing it if
50
- * necessary.
32
+ * Retrieves the singleton instance of ConfigStore.
51
33
  * @returns The singleton instance of ConfigStore.
52
34
  */
53
35
  public static getInstance(): ConfigStore {
@@ -57,12 +39,8 @@ export class ConfigStore extends AbstractProcess {
57
39
  return ConfigStore.instance;
58
40
  }
59
41
 
60
- // Instance Methods
61
- // ========================================================================
62
-
63
42
  /**
64
- * Retrieves a value from the configuration by key.
65
- * Supports nested keys using dot notation (e.g., "options.logLevel").
43
+ * Retrieves a value from the configuration using dot notation.
66
44
  *
67
45
  * @param key - The key of the configuration to retrieve.
68
46
  * @returns The configuration value or undefined if not found.
@@ -78,15 +56,12 @@ export class ConfigStore extends AbstractProcess {
78
56
  current = current[k];
79
57
  }
80
58
 
81
- this.logDebug(
82
- `Configuration key "${key}" retrieved with value: ${JSON.stringify(current)}`,
83
- );
59
+ this.logDebug(`Configuration key "${key}" retrieved.`);
84
60
  return current as T;
85
61
  }
86
62
 
87
63
  /**
88
- * Sets a value in the configuration by key.
89
- * Supports nested keys using dot notation (e.g., "options.logLevel").
64
+ * Sets a value in the configuration using dot notation.
90
65
  *
91
66
  * @param key - The key of the configuration to set.
92
67
  * @param value - The value to set.
@@ -97,21 +72,34 @@ export class ConfigStore extends AbstractProcess {
97
72
 
98
73
  for (let i = 0; i < keys.length - 1; i++) {
99
74
  const k = keys[i];
100
- if (!current[k] || typeof current[k] !== "object") {
101
- current[k] = {};
75
+
76
+ // Prevent prototype pollution by blocking reserved keywords
77
+ if (["__proto__", "constructor", "prototype"].includes(k)) {
78
+ this.logWarn(`Attempted prototype pollution detected: "${k}"`);
79
+ return;
80
+ }
81
+
82
+ // Ensure property exists and is an object
83
+ if (!Object.prototype.hasOwnProperty.call(current, k) || typeof current[k] !== "object") {
84
+ current[k] = Object.create(null); // Use a null prototype object
102
85
  }
103
86
  current = current[k];
104
87
  }
105
88
 
106
- current[keys[keys.length - 1]] = value;
107
- this.logDebug(
108
- `Set configuration key "${key}" to: ${JSON.stringify(value)}`,
109
- );
89
+ const finalKey = keys[keys.length - 1];
90
+
91
+ // Prevent prototype pollution at the final assignment
92
+ if (["__proto__", "constructor", "prototype"].includes(finalKey)) {
93
+ this.logWarn(`Attempted prototype pollution detected: "${finalKey}"`);
94
+ return;
95
+ }
96
+
97
+ current[finalKey] = value;
98
+ this.logDebug(`Set configuration key "${key}" to: ${JSON.stringify(value)}`);
110
99
  }
111
100
 
112
101
  /**
113
- * Merges the provided configuration into the existing configuration.
114
- * Uses a deep merge strategy to combine objects and overwrite primitives.
102
+ * Merges the provided configuration into the existing configuration using deep merge.
115
103
  *
116
104
  * @param newConfig - The new configuration to merge.
117
105
  */
@@ -121,51 +109,27 @@ export class ConfigStore extends AbstractProcess {
121
109
  }
122
110
 
123
111
  /**
124
- * Retrieves the current configuration.
125
- *
126
- * @returns The current configuration object.
112
+ * Retrieves the current configuration object.
113
+ * @returns The current configuration.
127
114
  */
128
115
  public getConfig(): ConfigInterface {
129
116
  return this.config;
130
117
  }
131
118
 
132
119
  /**
133
- * Prints the current configuration to the console in a readable format.
120
+ * Prints the current configuration to the console.
134
121
  */
135
122
  public print(): void {
136
- console.log("Current Configuration:");
137
- console.log(JSON.stringify(this.config, null, 2));
123
+ console.log("Current Configuration:", JSON.stringify(this.config, null, 2));
138
124
  }
139
125
 
140
126
  /**
141
- * Deeply merges two objects.
127
+ * Deeply merges two objects, preventing prototype pollution.
142
128
  *
143
- * @param target - The target object to merge into.
144
- * @param source - The source object to merge from.
129
+ * @param target - The target object.
130
+ * @param source - The source object.
145
131
  * @returns The merged object.
146
132
  */
147
- // private deepMerge(target: any, source: any): any {
148
- // if (typeof target !== "object" || target === null) {
149
- // return source;
150
- // }
151
-
152
- // for (const key of Object.keys(source)) {
153
- // if (
154
- // source[key] &&
155
- // typeof source[key] === "object" &&
156
- // !Array.isArray(source[key])
157
- // ) {
158
- // if (!target[key] || typeof target[key] !== "object") {
159
- // target[key] = {};
160
- // }
161
- // target[key] = this.deepMerge(target[key], source[key]);
162
- // } else {
163
- // target[key] = source[key];
164
- // }
165
- // }
166
-
167
- // return target;
168
- // }
169
133
  private deepMerge(target: any, source: any): any {
170
134
  if (typeof target !== "object" || target === null) {
171
135
  return source;
@@ -173,22 +137,14 @@ export class ConfigStore extends AbstractProcess {
173
137
 
174
138
  for (const key of Object.keys(source)) {
175
139
  // Prevent prototype pollution
176
- if (
177
- key === "__proto__" ||
178
- key === "constructor" ||
179
- key === "prototype"
180
- ) {
181
- this.logWarn(`Skipping potentially unsafe key: "${key}"`);
140
+ if (["__proto__", "constructor", "prototype"].includes(key)) {
141
+ this.logWarn(`Skipping unsafe key during merge: "${key}"`);
182
142
  continue;
183
143
  }
184
144
 
185
- if (
186
- source[key] &&
187
- typeof source[key] === "object" &&
188
- !Array.isArray(source[key])
189
- ) {
190
- if (!target[key] || typeof target[key] !== "object") {
191
- target[key] = {};
145
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
146
+ if (!Object.prototype.hasOwnProperty.call(target, key) || typeof target[key] !== "object") {
147
+ target[key] = Object.create(null);
192
148
  }
193
149
  target[key] = this.deepMerge(target[key], source[key]);
194
150
  } else {