@helpers4/object 3.0.0-alpha.1 → 3.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/meta/api.json CHANGED
@@ -1,7 +1,48 @@
1
1
  {
2
2
  "category": "object",
3
- "version": "3.0.0-alpha.1",
3
+ "version": "3.0.0",
4
4
  "functions": [
5
+ {
6
+ "name": "clone",
7
+ "kind": "function",
8
+ "description": "Creates a shallow copy of a value — one level deep, unlike cloneDeep.\n\nUnlike a plain `{ ...value }` spread, this correctly reconstructs `Date`,\n`Map`, `Set`, and arrays instead of producing an empty (or wrong-shaped)\nplain object for them. Primitives are returned as-is. Any other object\n(including class instances not listed above) has its own enumerable string\nkeys shallow-copied into a plain object — the same fallback `cloneDeep`\nuses, so the two stay consistent for types neither one special-cases.",
9
+ "since": "3.0.0",
10
+ "signatures": [
11
+ {
12
+ "signature": "clone<T>(value: T): T",
13
+ "description": "Creates a shallow copy of a value — one level deep, unlike cloneDeep.\n\nUnlike a plain `{ ...value }` spread, this correctly reconstructs `Date`,\n`Map`, `Set`, and arrays instead of producing an empty (or wrong-shaped)\nplain object for them. Primitives are returned as-is. Any other object\n(including class instances not listed above) has its own enumerable string\nkeys shallow-copied into a plain object — the same fallback `cloneDeep`\nuses, so the two stay consistent for types neither one special-cases.",
14
+ "params": [
15
+ {
16
+ "name": "value",
17
+ "type": "T",
18
+ "description": "The value to shallow-clone"
19
+ }
20
+ ],
21
+ "returns": {
22
+ "type": "T",
23
+ "description": "A shallow copy of `value`"
24
+ },
25
+ "typeParameters": [
26
+ {
27
+ "name": "T"
28
+ }
29
+ ]
30
+ }
31
+ ],
32
+ "examples": [
33
+ {
34
+ "title": "Shallow-copy a plain object",
35
+ "description": "Top-level keys are copied; nested values still share references (shallow).",
36
+ "code": "const obj = { a: 1, b: { c: 2 } };\nconst copy = clone(obj);\ncopy.b === obj.b // => true (same nested reference)"
37
+ },
38
+ {
39
+ "title": "Correctly clones Date/Map/Set, unlike a spread",
40
+ "description": "{ ...new Date() } produces {} — clone() doesn't have that problem.",
41
+ "code": "clone(new Map([['a', 1]]))\n// => new Map with the same entries, not {}"
42
+ }
43
+ ],
44
+ "sourceFile": "clone.ts"
45
+ },
5
46
  {
6
47
  "name": "cloneDeep",
7
48
  "kind": "function",
@@ -246,6 +287,42 @@
246
287
  ],
247
288
  "sourceFile": "equalsShallow.ts"
248
289
  },
290
+ {
291
+ "name": "flatten",
292
+ "kind": "function",
293
+ "description": "Flattens a nested object into a single-level object whose keys are the\ndot-notation path to each leaf value. The inverse of unflatten.\n\nOnly **plain objects** are recursed into — arrays, `Date`, `Map`, `RegExp`,\nclass instances, and empty plain objects `{}` are kept as opaque leaf\nvalues. This keeps `flatten`/`unflatten` a clean, invertible pair: arrays\ncan't be losslessly told apart from plain objects once reduced to dotted\nkeys, so this implementation doesn't attempt it.\n\nCaveat shared by every dotted-path flattening scheme: a key that itself\ncontains a literal `.` is indistinguishable from real nesting once\nflattened (`{ 'a.b': 1 }` and `{ a: { b: 1 } }` both produce `{ 'a.b': 1 }`).",
294
+ "since": "3.0.0",
295
+ "signatures": [
296
+ {
297
+ "signature": "flatten(obj: Record<string, unknown>): Record<string, unknown>",
298
+ "description": "Flattens a nested object into a single-level object whose keys are the\ndot-notation path to each leaf value. The inverse of unflatten.\n\nOnly **plain objects** are recursed into — arrays, `Date`, `Map`, `RegExp`,\nclass instances, and empty plain objects `{}` are kept as opaque leaf\nvalues. This keeps `flatten`/`unflatten` a clean, invertible pair: arrays\ncan't be losslessly told apart from plain objects once reduced to dotted\nkeys, so this implementation doesn't attempt it.\n\nCaveat shared by every dotted-path flattening scheme: a key that itself\ncontains a literal `.` is indistinguishable from real nesting once\nflattened (`{ 'a.b': 1 }` and `{ a: { b: 1 } }` both produce `{ 'a.b': 1 }`).",
299
+ "params": [
300
+ {
301
+ "name": "obj",
302
+ "type": "Record<string, unknown>",
303
+ "description": "The object to flatten"
304
+ }
305
+ ],
306
+ "returns": {
307
+ "type": "Record<string, unknown>",
308
+ "description": "A single-level object with dot-notation keys"
309
+ }
310
+ }
311
+ ],
312
+ "examples": [
313
+ {
314
+ "title": "Flatten a nested config object",
315
+ "description": "Each key in the result is the full dot-notation path to a leaf value.",
316
+ "code": "flatten({ server: { host: 'localhost', port: 8080 } })\n// => { 'server.host': 'localhost', 'server.port': 8080 }"
317
+ },
318
+ {
319
+ "title": "Arrays and special objects are kept as leaves",
320
+ "description": "Only plain objects are recursed into — arrays stay intact.",
321
+ "code": "flatten({ tags: ['a', 'b'], meta: { owner: 'x' } })\n// => { tags: ['a', 'b'], 'meta.owner': 'x' }"
322
+ }
323
+ ],
324
+ "sourceFile": "flatten.ts"
325
+ },
249
326
  {
250
327
  "name": "get",
251
328
  "kind": "function",
@@ -722,6 +799,89 @@
722
799
  ],
723
800
  "sourceFile": "omit.ts"
724
801
  },
802
+ {
803
+ "name": "omitBy",
804
+ "kind": "function",
805
+ "description": "Creates a new object without the own enumerable entries for which\n`predicate` returns `true`.\n\nComplements omit for when the keys to remove aren't known ahead of\ntime — `omit` takes an explicit key list, `omitBy` takes a predicate.",
806
+ "since": "3.0.0",
807
+ "signatures": [
808
+ {
809
+ "signature": "omitBy<T extends Record<string, unknown>>(obj: T | null | undefined, predicate: function): Partial<T> | null | undefined",
810
+ "description": "Creates a new object without the own enumerable entries for which\n`predicate` returns `true`.\n\nComplements omit for when the keys to remove aren't known ahead of\ntime — `omit` takes an explicit key list, `omitBy` takes a predicate.",
811
+ "params": [
812
+ {
813
+ "name": "obj",
814
+ "type": "T | null | undefined",
815
+ "description": "The source object. `null`/`undefined` pass through unchanged."
816
+ },
817
+ {
818
+ "name": "predicate",
819
+ "type": "function",
820
+ "description": "Called with `(value, key)` for each own enumerable entry"
821
+ }
822
+ ],
823
+ "returns": {
824
+ "type": "Partial<T> | null | undefined",
825
+ "description": "A new object without the matching entries"
826
+ },
827
+ "typeParameters": [
828
+ {
829
+ "name": "T",
830
+ "constraint": "Record<string, unknown>"
831
+ }
832
+ ]
833
+ }
834
+ ],
835
+ "examples": [
836
+ {
837
+ "title": "Drop entries matching a predicate",
838
+ "description": "Unlike omit(), the keys to drop don’t need to be known ahead of time.",
839
+ "code": "omitBy({ a: 1, b: undefined, c: 2 }, (value) => value === undefined)\n// => { a: 1, c: 2 }"
840
+ },
841
+ {
842
+ "title": "Drop entries by key name",
843
+ "description": "The predicate also receives the key, so filtering by name works too.",
844
+ "code": "omitBy({ id: 1, name: 'x', _internal: true }, (_v, key) => key.startsWith('_'))\n// => { id: 1, name: 'x' }"
845
+ }
846
+ ],
847
+ "sourceFile": "omitBy.ts"
848
+ },
849
+ {
850
+ "name": "parsePropertyPath",
851
+ "kind": "function",
852
+ "description": "Parses a dot/bracket-notation property path into an array of string/number\nkey segments — the same notation accepted by get and set.\n\n- Dot separators (`.`) split segments; each segment becomes a string key.\n- Bracket indices (`[n]`) become number keys.\n- A leading `.` is treated as \"current level\" and stripped before parsing,\n so `.[0]` ≡ `[0]` and `.a.b` ≡ `a.b`.\n- Empty string (or a bare `.`) returns `['']` (addresses the `''` key on the root object).\n- Consecutive dots (`a..b`) produce an empty-string segment: `['a', '', 'b']`.\n\nResults are cached (up to 500 distinct path strings, oldest evicted first)\nsince real-world callers tend to reuse a small, fixed set of literal paths.",
853
+ "since": "3.0.0",
854
+ "signatures": [
855
+ {
856
+ "signature": "parsePropertyPath(path: string): readonly string | number[]",
857
+ "description": "Parses a dot/bracket-notation property path into an array of string/number\nkey segments — the same notation accepted by get and set.\n\n- Dot separators (`.`) split segments; each segment becomes a string key.\n- Bracket indices (`[n]`) become number keys.\n- A leading `.` is treated as \"current level\" and stripped before parsing,\n so `.[0]` ≡ `[0]` and `.a.b` ≡ `a.b`.\n- Empty string (or a bare `.`) returns `['']` (addresses the `''` key on the root object).\n- Consecutive dots (`a..b`) produce an empty-string segment: `['a', '', 'b']`.\n\nResults are cached (up to 500 distinct path strings, oldest evicted first)\nsince real-world callers tend to reuse a small, fixed set of literal paths.",
858
+ "params": [
859
+ {
860
+ "name": "path",
861
+ "type": "string",
862
+ "description": "The dot/bracket-notation path string to parse"
863
+ }
864
+ ],
865
+ "returns": {
866
+ "type": "readonly string | number[]",
867
+ "description": "The parsed key segments"
868
+ }
869
+ }
870
+ ],
871
+ "examples": [
872
+ {
873
+ "title": "Parse a dot/bracket-notation path into key segments",
874
+ "description": "The same notation accepted by get() and set() — bracket indices become numbers.",
875
+ "code": "parsePropertyPath('layers[1].name')\n// => ['layers', 1, 'name']"
876
+ },
877
+ {
878
+ "title": "Malformed paths throw instead of silently misparsing",
879
+ "description": "Text trailing a closing bracket within a segment is ambiguous — use 'a[0].b' instead.",
880
+ "code": "parsePropertyPath('a[0]b')\n// => throws RangeError"
881
+ }
882
+ ],
883
+ "sourceFile": "parsePropertyPath.ts"
884
+ },
725
885
  {
726
886
  "name": "pick",
727
887
  "kind": "function",
@@ -813,6 +973,53 @@
813
973
  ],
814
974
  "sourceFile": "pick.ts"
815
975
  },
976
+ {
977
+ "name": "pickBy",
978
+ "kind": "function",
979
+ "description": "Creates a new object with only the own enumerable entries for which\n`predicate` returns `true`.\n\nComplements pick for when the keys to keep aren't known ahead of\ntime — `pick` takes an explicit key list, `pickBy` takes a predicate.",
980
+ "since": "3.0.0",
981
+ "signatures": [
982
+ {
983
+ "signature": "pickBy<T extends Record<string, unknown>>(obj: T | null | undefined, predicate: function): Partial<T> | null | undefined",
984
+ "description": "Creates a new object with only the own enumerable entries for which\n`predicate` returns `true`.\n\nComplements pick for when the keys to keep aren't known ahead of\ntime — `pick` takes an explicit key list, `pickBy` takes a predicate.",
985
+ "params": [
986
+ {
987
+ "name": "obj",
988
+ "type": "T | null | undefined",
989
+ "description": "The source object. `null`/`undefined` pass through unchanged."
990
+ },
991
+ {
992
+ "name": "predicate",
993
+ "type": "function",
994
+ "description": "Called with `(value, key)` for each own enumerable entry"
995
+ }
996
+ ],
997
+ "returns": {
998
+ "type": "Partial<T> | null | undefined",
999
+ "description": "A new object with only the matching entries"
1000
+ },
1001
+ "typeParameters": [
1002
+ {
1003
+ "name": "T",
1004
+ "constraint": "Record<string, unknown>"
1005
+ }
1006
+ ]
1007
+ }
1008
+ ],
1009
+ "examples": [
1010
+ {
1011
+ "title": "Keep only entries matching a predicate",
1012
+ "description": "Unlike pick(), the keys to keep don’t need to be known ahead of time.",
1013
+ "code": "pickBy({ a: 1, b: 0, c: 2 }, (value) => value > 0)\n// => { a: 1, c: 2 }"
1014
+ },
1015
+ {
1016
+ "title": "Keep entries by key name",
1017
+ "description": "The predicate also receives the key, so filtering by name works too.",
1018
+ "code": "pickBy({ _id: 1, name: 'x', _internal: true }, (_v, key) => !key.startsWith('_'))\n// => { name: 'x' }"
1019
+ }
1020
+ ],
1021
+ "sourceFile": "pickBy.ts"
1022
+ },
816
1023
  {
817
1024
  "name": "removeUndefinedNull",
818
1025
  "kind": "function",
@@ -1015,6 +1222,149 @@
1015
1222
  }
1016
1223
  ],
1017
1224
  "sourceFile": "set.ts"
1225
+ },
1226
+ {
1227
+ "name": "unflatten",
1228
+ "kind": "function",
1229
+ "description": "Rebuilds a nested object from a single-level object whose keys are\ndot-notation paths. The inverse of flatten.\n\nUses set internally, so intermediate nodes are always created as\nplain objects (never arrays — see flatten's doc for why), and any\nkey segment equal to `__proto__`, `constructor`, or `prototype` is silently\nrejected (same prototype-pollution guard as `set`).",
1230
+ "since": "3.0.0",
1231
+ "signatures": [
1232
+ {
1233
+ "signature": "unflatten(obj: Record<string, unknown>): Record<string, unknown>",
1234
+ "description": "Rebuilds a nested object from a single-level object whose keys are\ndot-notation paths. The inverse of flatten.\n\nUses set internally, so intermediate nodes are always created as\nplain objects (never arrays — see flatten's doc for why), and any\nkey segment equal to `__proto__`, `constructor`, or `prototype` is silently\nrejected (same prototype-pollution guard as `set`).",
1235
+ "params": [
1236
+ {
1237
+ "name": "obj",
1238
+ "type": "Record<string, unknown>",
1239
+ "description": "A single-level object with dot-notation keys"
1240
+ }
1241
+ ],
1242
+ "returns": {
1243
+ "type": "Record<string, unknown>",
1244
+ "description": "The rebuilt nested object"
1245
+ }
1246
+ }
1247
+ ],
1248
+ "examples": [
1249
+ {
1250
+ "title": "Rebuild a nested object from dotted keys",
1251
+ "description": "The inverse of flatten() — useful for parsing flat config sources (env vars, .ini files).",
1252
+ "code": "unflatten({ 'server.host': 'localhost', 'server.port': 8080 })\n// => { server: { host: 'localhost', port: 8080 } }"
1253
+ },
1254
+ {
1255
+ "title": "Multiple keys under the same parent are merged",
1256
+ "description": "Every dotted key contributes to the same nested structure.",
1257
+ "code": "unflatten({ 'a.x': 1, 'a.y': 2 })\n// => { a: { x: 1, y: 2 } }"
1258
+ }
1259
+ ],
1260
+ "sourceFile": "unflatten.ts"
1261
+ },
1262
+ {
1263
+ "name": "unset",
1264
+ "kind": "function",
1265
+ "description": "Removes the value at a dot/bracket-notation path or explicit key array,\nmutating the object in place. Uses the same path syntax as get/set.\n\nA missing intermediate segment is a no-op (nothing to remove), not an error.\nAs with `set`, any path containing a string segment equal to `__proto__`,\n`constructor`, or `prototype` is rejected and the object is returned unchanged.\n\nThe removed key stops appearing in `Object.keys`/`for...in` — unlike setting\nit to `undefined`, which would keep the key present.",
1266
+ "since": "3.0.0",
1267
+ "signatures": [
1268
+ {
1269
+ "signature": "unset<T extends object>(obj: T, path: string | readonly PropertyKey[]): T",
1270
+ "description": "Removes the value at a dot/bracket-notation path or explicit key array,\nmutating the object in place. Uses the same path syntax as get/set.\n\nA missing intermediate segment is a no-op (nothing to remove), not an error.\nAs with `set`, any path containing a string segment equal to `__proto__`,\n`constructor`, or `prototype` is rejected and the object is returned unchanged.\n\nThe removed key stops appearing in `Object.keys`/`for...in` — unlike setting\nit to `undefined`, which would keep the key present.",
1271
+ "params": [
1272
+ {
1273
+ "name": "obj",
1274
+ "type": "T",
1275
+ "description": "The object to mutate"
1276
+ },
1277
+ {
1278
+ "name": "path",
1279
+ "type": "string | readonly PropertyKey[]",
1280
+ "description": "Dot/bracket-notation string, or explicit `PropertyKey[]`"
1281
+ }
1282
+ ],
1283
+ "returns": {
1284
+ "type": "T",
1285
+ "description": "The same object reference, with the key removed if it existed"
1286
+ },
1287
+ "typeParameters": [
1288
+ {
1289
+ "name": "T",
1290
+ "constraint": "object"
1291
+ }
1292
+ ]
1293
+ }
1294
+ ],
1295
+ "examples": [
1296
+ {
1297
+ "title": "Remove a nested value by path",
1298
+ "description": "Uses the same dot/bracket path syntax as get() and set().",
1299
+ "code": "const config = { server: { host: 'localhost', debug: true } };\nunset(config, 'server.debug')\n// => { server: { host: 'localhost' } }"
1300
+ },
1301
+ {
1302
+ "title": "Missing paths are a safe no-op",
1303
+ "description": "Unlike set(), unset() never creates intermediate objects — there is nothing to remove.",
1304
+ "code": "unset({ a: 1 }, 'x.y.z')\n// => { a: 1 } (unchanged)"
1305
+ }
1306
+ ],
1307
+ "sourceFile": "unset.ts"
1308
+ },
1309
+ {
1310
+ "name": "update",
1311
+ "kind": "function",
1312
+ "description": "Updates the value at a path by applying a function to its current value,\ncreating intermediate objects as needed. Equivalent to\n`set(obj, path, updater(get(obj, path)))` in a single call.\n\nUses the same path syntax and type-inference rules as get and\nset — see those for the full behavior (string vs. `PropertyKey[]`\npaths, prototype-pollution guarding, etc.).",
1313
+ "since": "3.0.0",
1314
+ "signatures": [
1315
+ {
1316
+ "signature": "update<T extends object, P extends string | readonly PropertyKey[], V extends unknown>(obj: T, path: P, updater: function): conditional",
1317
+ "description": "Updates the value at a path by applying a function to its current value,\ncreating intermediate objects as needed. Equivalent to\n`set(obj, path, updater(get(obj, path)))` in a single call.\n\nUses the same path syntax and type-inference rules as get and\nset — see those for the full behavior (string vs. `PropertyKey[]`\npaths, prototype-pollution guarding, etc.).",
1318
+ "params": [
1319
+ {
1320
+ "name": "obj",
1321
+ "type": "T",
1322
+ "description": "The object to mutate"
1323
+ },
1324
+ {
1325
+ "name": "path",
1326
+ "type": "P",
1327
+ "description": "Dot/bracket-notation string literal or explicit `PropertyKey[]`"
1328
+ },
1329
+ {
1330
+ "name": "updater",
1331
+ "type": "function",
1332
+ "description": "Called with the current value (`undefined` if the path is\n absent); its return value is written back at the path"
1333
+ }
1334
+ ],
1335
+ "returns": {
1336
+ "type": "conditional",
1337
+ "description": "The mutated object (same reference, narrowed type)"
1338
+ },
1339
+ "typeParameters": [
1340
+ {
1341
+ "name": "T",
1342
+ "constraint": "object"
1343
+ },
1344
+ {
1345
+ "name": "P",
1346
+ "constraint": "string | readonly PropertyKey[]"
1347
+ },
1348
+ {
1349
+ "name": "V",
1350
+ "constraint": "unknown"
1351
+ }
1352
+ ]
1353
+ }
1354
+ ],
1355
+ "examples": [
1356
+ {
1357
+ "title": "Increment a counter in one call",
1358
+ "description": "Equivalent to set(obj, path, updater(get(obj, path))), without repeating the path.",
1359
+ "code": "const state = { count: 1 };\nupdate(state, 'count', (n) => (n ?? 0) + 1)\n// => { count: 2 }"
1360
+ },
1361
+ {
1362
+ "title": "Missing paths create intermediate objects, like set()",
1363
+ "description": "The updater receives undefined when the path does not exist yet.",
1364
+ "code": "const stats: Record<string, unknown> = {};\nupdate(stats, 'hits.total', (n: number | undefined) => (n ?? 0) + 1)\n// => { hits: { total: 1 } }"
1365
+ }
1366
+ ],
1367
+ "sourceFile": "update.ts"
1018
1368
  }
1019
1369
  ]
1020
1370
  }
@@ -1,6 +1,21 @@
1
1
  {
2
2
  "category": "object",
3
3
  "functions": [
4
+ {
5
+ "name": "clone",
6
+ "examples": [
7
+ {
8
+ "title": "Shallow-copy a plain object",
9
+ "description": "Top-level keys are copied; nested values still share references (shallow).",
10
+ "code": "const obj = { a: 1, b: { c: 2 } };\nconst copy = clone(obj);\ncopy.b === obj.b // => true (same nested reference)"
11
+ },
12
+ {
13
+ "title": "Correctly clones Date/Map/Set, unlike a spread",
14
+ "description": "{ ...new Date() } produces {} — clone() doesn't have that problem.",
15
+ "code": "clone(new Map([['a', 1]]))\n// => new Map with the same entries, not {}"
16
+ }
17
+ ]
18
+ },
4
19
  {
5
20
  "name": "cloneDeep",
6
21
  "examples": [
@@ -66,6 +81,21 @@
66
81
  }
67
82
  ]
68
83
  },
84
+ {
85
+ "name": "flatten",
86
+ "examples": [
87
+ {
88
+ "title": "Flatten a nested config object",
89
+ "description": "Each key in the result is the full dot-notation path to a leaf value.",
90
+ "code": "flatten({ server: { host: 'localhost', port: 8080 } })\n// => { 'server.host': 'localhost', 'server.port': 8080 }"
91
+ },
92
+ {
93
+ "title": "Arrays and special objects are kept as leaves",
94
+ "description": "Only plain objects are recursed into — arrays stay intact.",
95
+ "code": "flatten({ tags: ['a', 'b'], meta: { owner: 'x' } })\n// => { tags: ['a', 'b'], 'meta.owner': 'x' }"
96
+ }
97
+ ]
98
+ },
69
99
  {
70
100
  "name": "get",
71
101
  "examples": [
@@ -191,6 +221,36 @@
191
221
  }
192
222
  ]
193
223
  },
224
+ {
225
+ "name": "omitBy",
226
+ "examples": [
227
+ {
228
+ "title": "Drop entries matching a predicate",
229
+ "description": "Unlike omit(), the keys to drop don’t need to be known ahead of time.",
230
+ "code": "omitBy({ a: 1, b: undefined, c: 2 }, (value) => value === undefined)\n// => { a: 1, c: 2 }"
231
+ },
232
+ {
233
+ "title": "Drop entries by key name",
234
+ "description": "The predicate also receives the key, so filtering by name works too.",
235
+ "code": "omitBy({ id: 1, name: 'x', _internal: true }, (_v, key) => key.startsWith('_'))\n// => { id: 1, name: 'x' }"
236
+ }
237
+ ]
238
+ },
239
+ {
240
+ "name": "parsePropertyPath",
241
+ "examples": [
242
+ {
243
+ "title": "Parse a dot/bracket-notation path into key segments",
244
+ "description": "The same notation accepted by get() and set() — bracket indices become numbers.",
245
+ "code": "parsePropertyPath('layers[1].name')\n// => ['layers', 1, 'name']"
246
+ },
247
+ {
248
+ "title": "Malformed paths throw instead of silently misparsing",
249
+ "description": "Text trailing a closing bracket within a segment is ambiguous — use 'a[0].b' instead.",
250
+ "code": "parsePropertyPath('a[0]b')\n// => throws RangeError"
251
+ }
252
+ ]
253
+ },
194
254
  {
195
255
  "name": "pick",
196
256
  "examples": [
@@ -206,6 +266,21 @@
206
266
  }
207
267
  ]
208
268
  },
269
+ {
270
+ "name": "pickBy",
271
+ "examples": [
272
+ {
273
+ "title": "Keep only entries matching a predicate",
274
+ "description": "Unlike pick(), the keys to keep don’t need to be known ahead of time.",
275
+ "code": "pickBy({ a: 1, b: 0, c: 2 }, (value) => value > 0)\n// => { a: 1, c: 2 }"
276
+ },
277
+ {
278
+ "title": "Keep entries by key name",
279
+ "description": "The predicate also receives the key, so filtering by name works too.",
280
+ "code": "pickBy({ _id: 1, name: 'x', _internal: true }, (_v, key) => !key.startsWith('_'))\n// => { name: 'x' }"
281
+ }
282
+ ]
283
+ },
209
284
  {
210
285
  "name": "removeUndefinedNull",
211
286
  "examples": [
@@ -255,6 +330,51 @@
255
330
  "code": "const id = Symbol('id')\nset({}, ['user', id], 'alice')\n// => { user: { [id]: 'alice' } }"
256
331
  }
257
332
  ]
333
+ },
334
+ {
335
+ "name": "unflatten",
336
+ "examples": [
337
+ {
338
+ "title": "Rebuild a nested object from dotted keys",
339
+ "description": "The inverse of flatten() — useful for parsing flat config sources (env vars, .ini files).",
340
+ "code": "unflatten({ 'server.host': 'localhost', 'server.port': 8080 })\n// => { server: { host: 'localhost', port: 8080 } }"
341
+ },
342
+ {
343
+ "title": "Multiple keys under the same parent are merged",
344
+ "description": "Every dotted key contributes to the same nested structure.",
345
+ "code": "unflatten({ 'a.x': 1, 'a.y': 2 })\n// => { a: { x: 1, y: 2 } }"
346
+ }
347
+ ]
348
+ },
349
+ {
350
+ "name": "unset",
351
+ "examples": [
352
+ {
353
+ "title": "Remove a nested value by path",
354
+ "description": "Uses the same dot/bracket path syntax as get() and set().",
355
+ "code": "const config = { server: { host: 'localhost', debug: true } };\nunset(config, 'server.debug')\n// => { server: { host: 'localhost' } }"
356
+ },
357
+ {
358
+ "title": "Missing paths are a safe no-op",
359
+ "description": "Unlike set(), unset() never creates intermediate objects — there is nothing to remove.",
360
+ "code": "unset({ a: 1 }, 'x.y.z')\n// => { a: 1 } (unchanged)"
361
+ }
362
+ ]
363
+ },
364
+ {
365
+ "name": "update",
366
+ "examples": [
367
+ {
368
+ "title": "Increment a counter in one call",
369
+ "description": "Equivalent to set(obj, path, updater(get(obj, path))), without repeating the path.",
370
+ "code": "const state = { count: 1 };\nupdate(state, 'count', (n) => (n ?? 0) + 1)\n// => { count: 2 }"
371
+ },
372
+ {
373
+ "title": "Missing paths create intermediate objects, like set()",
374
+ "description": "The updater receives undefined when the path does not exist yet.",
375
+ "code": "const stats: Record<string, unknown> = {};\nupdate(stats, 'hits.total', (n: number | undefined) => (n ?? 0) + 1)\n// => { hits: { total: 1 } }"
376
+ }
377
+ ]
258
378
  }
259
379
  ]
260
380
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helpers4/object",
3
- "version": "3.0.0-alpha.1",
3
+ "version": "3.0.0",
4
4
  "description": "A set of helpers in TS/JS, compatible with tree-shaking, for object.",
5
5
  "author": "baxyz <baxy@etik.com>",
6
6
  "license": "LGPL-3.0",
@@ -27,11 +27,13 @@
27
27
  "keywords": [
28
28
  "helpers",
29
29
  "object",
30
+ "clone",
30
31
  "cloneDeep",
31
32
  "compact",
32
33
  "diff",
33
34
  "equalsDeep",
34
35
  "equalsShallow",
36
+ "flatten",
35
37
  "get",
36
38
  "groupBy",
37
39
  "invert",
@@ -40,10 +42,16 @@
40
42
  "map",
41
43
  "mergeDeep",
42
44
  "omit",
45
+ "omitBy",
46
+ "parsePropertyPath",
43
47
  "pick",
48
+ "pickBy",
44
49
  "removeUndefinedNull",
45
50
  "safeJsonParse",
46
- "set"
51
+ "set",
52
+ "unflatten",
53
+ "unset",
54
+ "update"
47
55
  ],
48
56
  "files": [
49
57
  "lib/index.js",
@@ -56,6 +64,6 @@
56
64
  "README.md"
57
65
  ],
58
66
  "engines": {
59
- "node": ">=18.0.0"
67
+ "node": ">=26.0.0"
60
68
  }
61
69
  }