@pfeiferio/object-utils 1.0.0
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/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/isObject.d.ts +7 -0
- package/dist/isObject.d.ts.map +1 -0
- package/dist/isObject.js +11 -0
- package/dist/isObject.js.map +1 -0
- package/dist/isPlainObject.d.ts +17 -0
- package/dist/isPlainObject.d.ts.map +1 -0
- package/dist/isPlainObject.js +22 -0
- package/dist/isPlainObject.js.map +1 -0
- package/dist/mergeObjects.d.ts +31 -0
- package/dist/mergeObjects.d.ts.map +1 -0
- package/dist/mergeObjects.js +48 -0
- package/dist/mergeObjects.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Pascal Pfeifer <pascal@pfeifer.zone>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# @pfeiferio/object-utils
|
|
2
|
+
|
|
3
|
+
Small, predictable utility functions for working with plain JavaScript objects.
|
|
4
|
+
|
|
5
|
+
This package provides a minimal set of helpers for **object inspection and deep merging** with strict, explicit behavior and zero dependencies.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @pfeiferio/object-utils
|
|
13
|
+
````
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import {
|
|
21
|
+
isObject,
|
|
22
|
+
isPlainObject,
|
|
23
|
+
mergeObjects
|
|
24
|
+
} from '@pfeiferio/object-utils'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## `isObject(value)`
|
|
30
|
+
|
|
31
|
+
Checks whether a value is a **non-null object**, excluding arrays.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
isObject({}) // true
|
|
35
|
+
isObject([]) // false
|
|
36
|
+
isObject(null) // false
|
|
37
|
+
isObject('string') // false
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This is a safe alternative to `typeof value === 'object'`.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## `isPlainObject(value)`
|
|
45
|
+
|
|
46
|
+
Checks whether a value is a **plain object**.
|
|
47
|
+
|
|
48
|
+
A plain object is defined as:
|
|
49
|
+
|
|
50
|
+
* created via object literal (`{}`)
|
|
51
|
+
* or `Object.create(null)`
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
isPlainObject({}) // true
|
|
55
|
+
isPlainObject(Object.create(null)) // true
|
|
56
|
+
|
|
57
|
+
isPlainObject([]) // false
|
|
58
|
+
isPlainObject(new Date()) // false
|
|
59
|
+
isPlainObject(null) // false
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Useful for merge logic and configuration handling.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## `mergeObjects(a, b)`
|
|
67
|
+
|
|
68
|
+
Deeply merges two values.
|
|
69
|
+
|
|
70
|
+
* **Only plain objects** are merged recursively
|
|
71
|
+
* All other values **overwrite**
|
|
72
|
+
* Inputs are **never mutated**
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
mergeObjects(
|
|
76
|
+
{ server: { host: 'example.com' } },
|
|
77
|
+
{ server: { port: 80 } }
|
|
78
|
+
)
|
|
79
|
+
// → { server: { host: 'example.com', port: 80 } }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Overwrite behavior
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
mergeObjects(
|
|
86
|
+
{ a: { b: 1 } },
|
|
87
|
+
{ a: 2 }
|
|
88
|
+
)
|
|
89
|
+
// → { a: 2 }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
mergeObjects(
|
|
94
|
+
{ list: [1, 2] },
|
|
95
|
+
{ list: [3] }
|
|
96
|
+
)
|
|
97
|
+
// → { list: [3] }
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Non-plain values always win
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
mergeObjects(
|
|
104
|
+
{ a: { b: 1 } },
|
|
105
|
+
null
|
|
106
|
+
)
|
|
107
|
+
// → null
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Design Principles
|
|
113
|
+
|
|
114
|
+
* Explicit semantics
|
|
115
|
+
* No array merging
|
|
116
|
+
* No mutation
|
|
117
|
+
* Predictable results
|
|
118
|
+
* Suitable for configuration and business logic
|
|
119
|
+
|
|
120
|
+
This package intentionally avoids:
|
|
121
|
+
|
|
122
|
+
* implicit deep cloning
|
|
123
|
+
* class instance merging
|
|
124
|
+
* array merging heuristics
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAC;AACvC,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAC;AACvC,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isObject.d.ts","sourceRoot":"","sources":["../src/isObject.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAMlD,CAAA"}
|
package/dist/isObject.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks whether a value is a non-null object excluding arrays.
|
|
3
|
+
* @param {unknown} value
|
|
4
|
+
* @returns {boolean}
|
|
5
|
+
*/
|
|
6
|
+
export const isObject = (value) => {
|
|
7
|
+
return (typeof value === 'object' &&
|
|
8
|
+
value !== null &&
|
|
9
|
+
!Array.isArray(value));
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=isObject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isObject.js","sourceRoot":"","sources":["../src/isObject.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAmB,EAAE;IAC1D,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CACtB,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type PlainObject = Record<string, unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether a value is a plain object.
|
|
4
|
+
*
|
|
5
|
+
* A plain object is defined as an object created via object literal
|
|
6
|
+
* notation or `Object` constructor. Arrays, null, and all other
|
|
7
|
+
* non-object types are explicitly excluded.
|
|
8
|
+
*
|
|
9
|
+
* @param {*} value
|
|
10
|
+
* The value to check.
|
|
11
|
+
*
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
* `true` if the value is a plain object, otherwise `false`.
|
|
14
|
+
*/
|
|
15
|
+
export declare const isPlainObject: (value: unknown) => value is PlainObject;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=isPlainObject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isPlainObject.d.ts","sourceRoot":"","sources":["../src/isPlainObject.ts"],"names":[],"mappings":"AAEA,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAE1C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,WAQvD,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { isObject } from "./isObject.js";
|
|
2
|
+
/**
|
|
3
|
+
* Checks whether a value is a plain object.
|
|
4
|
+
*
|
|
5
|
+
* A plain object is defined as an object created via object literal
|
|
6
|
+
* notation or `Object` constructor. Arrays, null, and all other
|
|
7
|
+
* non-object types are explicitly excluded.
|
|
8
|
+
*
|
|
9
|
+
* @param {*} value
|
|
10
|
+
* The value to check.
|
|
11
|
+
*
|
|
12
|
+
* @returns {boolean}
|
|
13
|
+
* `true` if the value is a plain object, otherwise `false`.
|
|
14
|
+
*/
|
|
15
|
+
export const isPlainObject = (value) => {
|
|
16
|
+
if (!isObject(value)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const proto = Object.getPrototypeOf(value);
|
|
20
|
+
return proto === Object.prototype || proto === null;
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=isPlainObject.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isPlainObject.js","sourceRoot":"","sources":["../src/isPlainObject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAC;AAIvC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAc,EAAwB,EAAE;IAEpE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;IAC1C,OAAO,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,CAAA;AACrD,CAAC,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deeply merges two objects.
|
|
3
|
+
*
|
|
4
|
+
* Plain objects are merged recursively. For all other value types
|
|
5
|
+
* (arrays, primitives, null, or differing types), the value from
|
|
6
|
+
* the second object overrides the first one.
|
|
7
|
+
*
|
|
8
|
+
* This function does not mutate either input object.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} a
|
|
11
|
+
* Base object.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} b
|
|
14
|
+
* object whose values take precedence.
|
|
15
|
+
*
|
|
16
|
+
* @returns {Object}
|
|
17
|
+
* A new object containing the merged result.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* mergeObjects(
|
|
21
|
+
* { server: { host: 'example.com' } },
|
|
22
|
+
* { server: { port: 80 } }
|
|
23
|
+
* )
|
|
24
|
+
* // → { server: { host: 'example.com', port: 80 } }
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* @param a
|
|
28
|
+
* @param b
|
|
29
|
+
*/
|
|
30
|
+
export declare const mergeObjects: (a: unknown, b: unknown) => unknown;
|
|
31
|
+
//# sourceMappingURL=mergeObjects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeObjects.d.ts","sourceRoot":"","sources":["../src/mergeObjects.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;GAGG;AACH,eAAO,MAAM,YAAY,GAAI,GAAG,OAAO,EAAE,GAAG,OAAO,KAAG,OAmBrD,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { isPlainObject } from "./isPlainObject.js";
|
|
2
|
+
/**
|
|
3
|
+
* Deeply merges two objects.
|
|
4
|
+
*
|
|
5
|
+
* Plain objects are merged recursively. For all other value types
|
|
6
|
+
* (arrays, primitives, null, or differing types), the value from
|
|
7
|
+
* the second object overrides the first one.
|
|
8
|
+
*
|
|
9
|
+
* This function does not mutate either input object.
|
|
10
|
+
*
|
|
11
|
+
* @param {Object} a
|
|
12
|
+
* Base object.
|
|
13
|
+
*
|
|
14
|
+
* @param {Object} b
|
|
15
|
+
* object whose values take precedence.
|
|
16
|
+
*
|
|
17
|
+
* @returns {Object}
|
|
18
|
+
* A new object containing the merged result.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* mergeObjects(
|
|
22
|
+
* { server: { host: 'example.com' } },
|
|
23
|
+
* { server: { port: 80 } }
|
|
24
|
+
* )
|
|
25
|
+
* // → { server: { host: 'example.com', port: 80 } }
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* @param a
|
|
29
|
+
* @param b
|
|
30
|
+
*/
|
|
31
|
+
export const mergeObjects = (a, b) => {
|
|
32
|
+
// if b is not a plain object, it always wins
|
|
33
|
+
if (!isPlainObject(a) || !isPlainObject(b)) {
|
|
34
|
+
return b;
|
|
35
|
+
}
|
|
36
|
+
const result = { ...a };
|
|
37
|
+
for (const [key, valueB] of Object.entries(b)) {
|
|
38
|
+
const valueA = a[key];
|
|
39
|
+
if (isPlainObject(valueA) && isPlainObject(valueB)) {
|
|
40
|
+
result[key] = mergeObjects(valueA, valueB);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
result[key] = valueB;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=mergeObjects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mergeObjects.js","sourceRoot":"","sources":["../src/mergeObjects.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AAEjD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAU,EAAE,CAAU,EAAW,EAAE;IAC9D,6CAA6C;IAC7C,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,OAAO,CAAC,CAAA;IACV,CAAC;IAED,MAAM,MAAM,GAA4B,EAAC,GAAG,CAAC,EAAC,CAAA;IAE9C,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAA;QAErB,IAAI,aAAa,CAAC,MAAM,CAAC,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QAC5C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAA;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC,CAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pfeiferio/object-utils",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Small, predictable utilities for working with plain JavaScript objects",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Pascal Pfeifer <pascal@pfeifer.zone>",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"object",
|
|
24
|
+
"utils",
|
|
25
|
+
"merge",
|
|
26
|
+
"plain-object",
|
|
27
|
+
"deep-merge",
|
|
28
|
+
"utility",
|
|
29
|
+
"configuration"
|
|
30
|
+
],
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "git+https://github.com/pfeiferio/utils-object.git"
|
|
34
|
+
},
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/pfeiferio/utils-object/issues"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/pfeiferio/utils-object#readme",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.0.0"
|
|
41
|
+
},
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc",
|
|
44
|
+
"test": "npm run build && node --test",
|
|
45
|
+
"test:coverage": "npm run build && node --test --experimental-test-coverage",
|
|
46
|
+
"test:watch": "node --test --watch",
|
|
47
|
+
"prepublishOnly": "npm run clean && npm test",
|
|
48
|
+
"clean": "rm -rf dist"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^20.17.9",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
|
+
}
|
|
54
|
+
}
|