@fluixi/utils 1.0.0-alpha.53

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.
Files changed (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/index.cjs +1 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/index.mjs +1 -0
  8. package/dist/lib/array/advanced.cjs +1 -0
  9. package/dist/lib/array/advanced.d.ts +1116 -0
  10. package/dist/lib/array/advanced.d.ts.map +1 -0
  11. package/dist/lib/array/advanced.js +2429 -0
  12. package/dist/lib/array/advanced.mjs +1 -0
  13. package/dist/lib/array/array.cjs +1 -0
  14. package/dist/lib/array/array.d.ts +6 -0
  15. package/dist/lib/array/array.d.ts.map +1 -0
  16. package/dist/lib/array/array.js +31 -0
  17. package/dist/lib/array/array.mjs +1 -0
  18. package/dist/lib/array/index.cjs +1 -0
  19. package/dist/lib/array/index.d.ts +4 -0
  20. package/dist/lib/array/index.d.ts.map +1 -0
  21. package/dist/lib/array/index.js +3 -0
  22. package/dist/lib/array/index.mjs +1 -0
  23. package/dist/lib/array/utils.cjs +1 -0
  24. package/dist/lib/array/utils.d.ts +100 -0
  25. package/dist/lib/array/utils.d.ts.map +1 -0
  26. package/dist/lib/array/utils.js +165 -0
  27. package/dist/lib/array/utils.mjs +1 -0
  28. package/dist/lib/color/color.cjs +1 -0
  29. package/dist/lib/color/color.d.ts +366 -0
  30. package/dist/lib/color/color.d.ts.map +1 -0
  31. package/dist/lib/color/color.js +722 -0
  32. package/dist/lib/color/color.mjs +1 -0
  33. package/dist/lib/color/constants.cjs +1 -0
  34. package/dist/lib/color/constants.d.ts +361 -0
  35. package/dist/lib/color/constants.d.ts.map +1 -0
  36. package/dist/lib/color/constants.js +358 -0
  37. package/dist/lib/color/constants.mjs +1 -0
  38. package/dist/lib/color/conversions.cjs +1 -0
  39. package/dist/lib/color/conversions.d.ts +166 -0
  40. package/dist/lib/color/conversions.d.ts.map +1 -0
  41. package/dist/lib/color/conversions.js +766 -0
  42. package/dist/lib/color/conversions.mjs +1 -0
  43. package/dist/lib/color/helpers.cjs +1 -0
  44. package/dist/lib/color/helpers.d.ts +75 -0
  45. package/dist/lib/color/helpers.d.ts.map +1 -0
  46. package/dist/lib/color/helpers.js +419 -0
  47. package/dist/lib/color/helpers.mjs +1 -0
  48. package/dist/lib/color/index.cjs +1 -0
  49. package/dist/lib/color/index.d.ts +12 -0
  50. package/dist/lib/color/index.d.ts.map +1 -0
  51. package/dist/lib/color/index.js +15 -0
  52. package/dist/lib/color/index.mjs +1 -0
  53. package/dist/lib/color/types.cjs +1 -0
  54. package/dist/lib/color/types.d.ts +179 -0
  55. package/dist/lib/color/types.d.ts.map +1 -0
  56. package/dist/lib/color/types.js +1 -0
  57. package/dist/lib/color/types.mjs +0 -0
  58. package/dist/lib/color/utils.cjs +1 -0
  59. package/dist/lib/color/utils.d.ts +199 -0
  60. package/dist/lib/color/utils.d.ts.map +1 -0
  61. package/dist/lib/color/utils.js +858 -0
  62. package/dist/lib/color/utils.mjs +1 -0
  63. package/dist/lib/compare/comparators.cjs +1 -0
  64. package/dist/lib/compare/comparators.d.ts +65 -0
  65. package/dist/lib/compare/comparators.d.ts.map +1 -0
  66. package/dist/lib/compare/comparators.js +210 -0
  67. package/dist/lib/compare/comparators.mjs +1 -0
  68. package/dist/lib/compare/index.cjs +1 -0
  69. package/dist/lib/compare/index.d.ts +2 -0
  70. package/dist/lib/compare/index.d.ts.map +1 -0
  71. package/dist/lib/compare/index.js +1 -0
  72. package/dist/lib/compare/index.mjs +1 -0
  73. package/dist/lib/crypto/hash.cjs +1 -0
  74. package/dist/lib/crypto/hash.d.ts +6 -0
  75. package/dist/lib/crypto/hash.d.ts.map +1 -0
  76. package/dist/lib/crypto/hash.js +53 -0
  77. package/dist/lib/crypto/hash.mjs +1 -0
  78. package/dist/lib/crypto/index.cjs +1 -0
  79. package/dist/lib/crypto/index.d.ts +2 -0
  80. package/dist/lib/crypto/index.d.ts.map +1 -0
  81. package/dist/lib/crypto/index.js +1 -0
  82. package/dist/lib/crypto/index.mjs +1 -0
  83. package/dist/lib/dom/events.cjs +1 -0
  84. package/dist/lib/dom/events.d.ts +159 -0
  85. package/dist/lib/dom/events.d.ts.map +1 -0
  86. package/dist/lib/dom/events.js +50 -0
  87. package/dist/lib/dom/events.mjs +1 -0
  88. package/dist/lib/dom/index.cjs +1 -0
  89. package/dist/lib/dom/index.d.ts +3 -0
  90. package/dist/lib/dom/index.d.ts.map +1 -0
  91. package/dist/lib/dom/index.js +2 -0
  92. package/dist/lib/dom/index.mjs +1 -0
  93. package/dist/lib/dom/utils.cjs +1 -0
  94. package/dist/lib/dom/utils.d.ts +6 -0
  95. package/dist/lib/dom/utils.d.ts.map +1 -0
  96. package/dist/lib/dom/utils.js +37 -0
  97. package/dist/lib/dom/utils.mjs +1 -0
  98. package/dist/lib/functions/functions.cjs +1 -0
  99. package/dist/lib/functions/functions.d.ts +329 -0
  100. package/dist/lib/functions/functions.d.ts.map +1 -0
  101. package/dist/lib/functions/functions.js +585 -0
  102. package/dist/lib/functions/functions.mjs +1 -0
  103. package/dist/lib/functions/index.cjs +1 -0
  104. package/dist/lib/functions/index.d.ts +3 -0
  105. package/dist/lib/functions/index.d.ts.map +1 -0
  106. package/dist/lib/functions/index.js +1 -0
  107. package/dist/lib/functions/index.mjs +1 -0
  108. package/dist/lib/functions/types.cjs +1 -0
  109. package/dist/lib/functions/types.d.ts +2 -0
  110. package/dist/lib/functions/types.d.ts.map +1 -0
  111. package/dist/lib/functions/types.js +1 -0
  112. package/dist/lib/functions/types.mjs +0 -0
  113. package/dist/lib/helpers.cjs +1 -0
  114. package/dist/lib/helpers.d.ts +13 -0
  115. package/dist/lib/helpers.d.ts.map +1 -0
  116. package/dist/lib/helpers.js +66 -0
  117. package/dist/lib/helpers.mjs +1 -0
  118. package/dist/lib/index.cjs +1 -0
  119. package/dist/lib/index.d.ts +6 -0
  120. package/dist/lib/index.d.ts.map +1 -0
  121. package/dist/lib/index.js +8 -0
  122. package/dist/lib/index.mjs +1 -0
  123. package/dist/lib/maths/constants.cjs +1 -0
  124. package/dist/lib/maths/constants.d.ts +84 -0
  125. package/dist/lib/maths/constants.d.ts.map +1 -0
  126. package/dist/lib/maths/constants.js +79 -0
  127. package/dist/lib/maths/constants.mjs +1 -0
  128. package/dist/lib/maths/fn/advanced.cjs +1 -0
  129. package/dist/lib/maths/fn/advanced.d.ts +149 -0
  130. package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
  131. package/dist/lib/maths/fn/advanced.js +264 -0
  132. package/dist/lib/maths/fn/advanced.mjs +1 -0
  133. package/dist/lib/maths/fn/basic.cjs +1 -0
  134. package/dist/lib/maths/fn/basic.d.ts +182 -0
  135. package/dist/lib/maths/fn/basic.d.ts.map +1 -0
  136. package/dist/lib/maths/fn/basic.js +343 -0
  137. package/dist/lib/maths/fn/basic.mjs +1 -0
  138. package/dist/lib/maths/fn/complex.cjs +1 -0
  139. package/dist/lib/maths/fn/complex.d.ts +153 -0
  140. package/dist/lib/maths/fn/complex.d.ts.map +1 -0
  141. package/dist/lib/maths/fn/complex.js +383 -0
  142. package/dist/lib/maths/fn/complex.mjs +1 -0
  143. package/dist/lib/maths/fn/index.cjs +1 -0
  144. package/dist/lib/maths/fn/index.d.ts +10 -0
  145. package/dist/lib/maths/fn/index.d.ts.map +1 -0
  146. package/dist/lib/maths/fn/index.js +9 -0
  147. package/dist/lib/maths/fn/index.mjs +1 -0
  148. package/dist/lib/maths/fn/trigo.cjs +1 -0
  149. package/dist/lib/maths/fn/trigo.d.ts +147 -0
  150. package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
  151. package/dist/lib/maths/fn/trigo.js +227 -0
  152. package/dist/lib/maths/fn/trigo.mjs +1 -0
  153. package/dist/lib/maths/fn/vector.cjs +1 -0
  154. package/dist/lib/maths/fn/vector.d.ts +512 -0
  155. package/dist/lib/maths/fn/vector.d.ts.map +1 -0
  156. package/dist/lib/maths/fn/vector.js +1136 -0
  157. package/dist/lib/maths/fn/vector.mjs +1 -0
  158. package/dist/lib/maths/formatter.cjs +1 -0
  159. package/dist/lib/maths/formatter.d.ts +35 -0
  160. package/dist/lib/maths/formatter.d.ts.map +1 -0
  161. package/dist/lib/maths/formatter.js +56 -0
  162. package/dist/lib/maths/formatter.mjs +1 -0
  163. package/dist/lib/maths/guards.cjs +1 -0
  164. package/dist/lib/maths/guards.d.ts +166 -0
  165. package/dist/lib/maths/guards.d.ts.map +1 -0
  166. package/dist/lib/maths/guards.js +315 -0
  167. package/dist/lib/maths/guards.mjs +1 -0
  168. package/dist/lib/maths/index.cjs +1 -0
  169. package/dist/lib/maths/index.d.ts +24 -0
  170. package/dist/lib/maths/index.d.ts.map +1 -0
  171. package/dist/lib/maths/index.js +29 -0
  172. package/dist/lib/maths/index.mjs +1 -0
  173. package/dist/lib/maths/parser.cjs +1 -0
  174. package/dist/lib/maths/parser.d.ts +20 -0
  175. package/dist/lib/maths/parser.d.ts.map +1 -0
  176. package/dist/lib/maths/parser.js +78 -0
  177. package/dist/lib/maths/parser.mjs +1 -0
  178. package/dist/lib/maths/random.cjs +1 -0
  179. package/dist/lib/maths/random.d.ts +27 -0
  180. package/dist/lib/maths/random.d.ts.map +1 -0
  181. package/dist/lib/maths/random.js +49 -0
  182. package/dist/lib/maths/random.mjs +1 -0
  183. package/dist/lib/maths/types/advanced.cjs +1 -0
  184. package/dist/lib/maths/types/advanced.d.ts +357 -0
  185. package/dist/lib/maths/types/advanced.d.ts.map +1 -0
  186. package/dist/lib/maths/types/advanced.js +5 -0
  187. package/dist/lib/maths/types/advanced.mjs +0 -0
  188. package/dist/lib/maths/types/basic.cjs +1 -0
  189. package/dist/lib/maths/types/basic.d.ts +95 -0
  190. package/dist/lib/maths/types/basic.d.ts.map +1 -0
  191. package/dist/lib/maths/types/basic.js +5 -0
  192. package/dist/lib/maths/types/basic.mjs +0 -0
  193. package/dist/lib/maths/types/index.cjs +1 -0
  194. package/dist/lib/maths/types/index.d.ts +4 -0
  195. package/dist/lib/maths/types/index.d.ts.map +1 -0
  196. package/dist/lib/maths/types/index.js +3 -0
  197. package/dist/lib/maths/types/index.mjs +0 -0
  198. package/dist/lib/maths/types/negative.cjs +1 -0
  199. package/dist/lib/maths/types/negative.d.ts +51 -0
  200. package/dist/lib/maths/types/negative.d.ts.map +1 -0
  201. package/dist/lib/maths/types/negative.js +5 -0
  202. package/dist/lib/maths/types/negative.mjs +0 -0
  203. package/dist/lib/maths/types/operations.cjs +1 -0
  204. package/dist/lib/maths/types/operations.d.ts +188 -0
  205. package/dist/lib/maths/types/operations.d.ts.map +1 -0
  206. package/dist/lib/maths/types/operations.js +6 -0
  207. package/dist/lib/maths/types/operations.mjs +0 -0
  208. package/dist/lib/object/clone.cjs +1 -0
  209. package/dist/lib/object/clone.d.ts +46 -0
  210. package/dist/lib/object/clone.d.ts.map +1 -0
  211. package/dist/lib/object/clone.js +104 -0
  212. package/dist/lib/object/clone.mjs +1 -0
  213. package/dist/lib/object/diff.cjs +1 -0
  214. package/dist/lib/object/diff.d.ts +203 -0
  215. package/dist/lib/object/diff.d.ts.map +1 -0
  216. package/dist/lib/object/diff.js +805 -0
  217. package/dist/lib/object/diff.mjs +1 -0
  218. package/dist/lib/object/getter-setter.cjs +1 -0
  219. package/dist/lib/object/getter-setter.d.ts +361 -0
  220. package/dist/lib/object/getter-setter.d.ts.map +1 -0
  221. package/dist/lib/object/getter-setter.js +1771 -0
  222. package/dist/lib/object/getter-setter.mjs +1 -0
  223. package/dist/lib/object/index.cjs +6 -0
  224. package/dist/lib/object/index.d.ts +13 -0
  225. package/dist/lib/object/index.d.ts.map +1 -0
  226. package/dist/lib/object/index.js +12 -0
  227. package/dist/lib/object/index.mjs +6 -0
  228. package/dist/lib/object/merge.cjs +1 -0
  229. package/dist/lib/object/merge.d.ts +93 -0
  230. package/dist/lib/object/merge.d.ts.map +1 -0
  231. package/dist/lib/object/merge.js +474 -0
  232. package/dist/lib/object/merge.mjs +1 -0
  233. package/dist/lib/object/object.cjs +1 -0
  234. package/dist/lib/object/object.d.ts +128 -0
  235. package/dist/lib/object/object.d.ts.map +1 -0
  236. package/dist/lib/object/object.js +432 -0
  237. package/dist/lib/object/object.mjs +1 -0
  238. package/dist/lib/object/omit.cjs +1 -0
  239. package/dist/lib/object/omit.d.ts +351 -0
  240. package/dist/lib/object/omit.d.ts.map +1 -0
  241. package/dist/lib/object/omit.js +656 -0
  242. package/dist/lib/object/omit.mjs +1 -0
  243. package/dist/lib/object/reconcile.cjs +1 -0
  244. package/dist/lib/object/reconcile.d.ts +7 -0
  245. package/dist/lib/object/reconcile.d.ts.map +1 -0
  246. package/dist/lib/object/reconcile.js +122 -0
  247. package/dist/lib/object/reconcile.mjs +1 -0
  248. package/dist/lib/object/selector.cjs +1 -0
  249. package/dist/lib/object/selector.d.ts +50 -0
  250. package/dist/lib/object/selector.d.ts.map +1 -0
  251. package/dist/lib/object/selector.js +371 -0
  252. package/dist/lib/object/selector.mjs +1 -0
  253. package/dist/lib/object/sort.cjs +1 -0
  254. package/dist/lib/object/sort.d.ts +149 -0
  255. package/dist/lib/object/sort.d.ts.map +1 -0
  256. package/dist/lib/object/sort.js +298 -0
  257. package/dist/lib/object/sort.mjs +1 -0
  258. package/dist/lib/object/transform.cjs +1 -0
  259. package/dist/lib/object/transform.d.ts +620 -0
  260. package/dist/lib/object/transform.d.ts.map +1 -0
  261. package/dist/lib/object/transform.js +1058 -0
  262. package/dist/lib/object/transform.mjs +1 -0
  263. package/dist/lib/object/types.cjs +1 -0
  264. package/dist/lib/object/types.d.ts +250 -0
  265. package/dist/lib/object/types.d.ts.map +1 -0
  266. package/dist/lib/object/types.js +1 -0
  267. package/dist/lib/object/types.mjs +0 -0
  268. package/dist/lib/object/utils.cjs +6 -0
  269. package/dist/lib/object/utils.d.ts +165 -0
  270. package/dist/lib/object/utils.d.ts.map +1 -0
  271. package/dist/lib/object/utils.js +444 -0
  272. package/dist/lib/object/utils.mjs +6 -0
  273. package/dist/lib/primitive/boolean/boolean.cjs +1 -0
  274. package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
  275. package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
  276. package/dist/lib/primitive/boolean/boolean.js +615 -0
  277. package/dist/lib/primitive/boolean/boolean.mjs +1 -0
  278. package/dist/lib/primitive/boolean/index.cjs +1 -0
  279. package/dist/lib/primitive/boolean/index.d.ts +3 -0
  280. package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
  281. package/dist/lib/primitive/boolean/index.js +2 -0
  282. package/dist/lib/primitive/boolean/index.mjs +1 -0
  283. package/dist/lib/primitive/boolean/types.cjs +1 -0
  284. package/dist/lib/primitive/boolean/types.d.ts +14 -0
  285. package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
  286. package/dist/lib/primitive/boolean/types.js +1 -0
  287. package/dist/lib/primitive/boolean/types.mjs +0 -0
  288. package/dist/lib/primitive/date/date.cjs +1 -0
  289. package/dist/lib/primitive/date/date.d.ts +641 -0
  290. package/dist/lib/primitive/date/date.d.ts.map +1 -0
  291. package/dist/lib/primitive/date/date.js +1998 -0
  292. package/dist/lib/primitive/date/date.mjs +1 -0
  293. package/dist/lib/primitive/date/index.cjs +1 -0
  294. package/dist/lib/primitive/date/index.d.ts +3 -0
  295. package/dist/lib/primitive/date/index.d.ts.map +1 -0
  296. package/dist/lib/primitive/date/index.js +2 -0
  297. package/dist/lib/primitive/date/index.mjs +1 -0
  298. package/dist/lib/primitive/date/types.cjs +1 -0
  299. package/dist/lib/primitive/date/types.d.ts +205 -0
  300. package/dist/lib/primitive/date/types.d.ts.map +1 -0
  301. package/dist/lib/primitive/date/types.js +7 -0
  302. package/dist/lib/primitive/date/types.mjs +0 -0
  303. package/dist/lib/primitive/index.cjs +5 -0
  304. package/dist/lib/primitive/index.d.ts +5 -0
  305. package/dist/lib/primitive/index.d.ts.map +1 -0
  306. package/dist/lib/primitive/index.js +4 -0
  307. package/dist/lib/primitive/index.mjs +5 -0
  308. package/dist/lib/primitive/string/index.cjs +5 -0
  309. package/dist/lib/primitive/string/index.d.ts +3 -0
  310. package/dist/lib/primitive/string/index.d.ts.map +1 -0
  311. package/dist/lib/primitive/string/index.js +2 -0
  312. package/dist/lib/primitive/string/index.mjs +5 -0
  313. package/dist/lib/primitive/string/parser-2.cjs +8 -0
  314. package/dist/lib/primitive/string/parser-2.d.ts +17 -0
  315. package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
  316. package/dist/lib/primitive/string/parser-2.js +656 -0
  317. package/dist/lib/primitive/string/parser-2.mjs +8 -0
  318. package/dist/lib/primitive/string/parser.cjs +3 -0
  319. package/dist/lib/primitive/string/parser.d.ts +9 -0
  320. package/dist/lib/primitive/string/parser.d.ts.map +1 -0
  321. package/dist/lib/primitive/string/parser.js +775 -0
  322. package/dist/lib/primitive/string/parser.mjs +3 -0
  323. package/dist/lib/primitive/string/string.cjs +5 -0
  324. package/dist/lib/primitive/string/string.d.ts +535 -0
  325. package/dist/lib/primitive/string/string.d.ts.map +1 -0
  326. package/dist/lib/primitive/string/string.js +1301 -0
  327. package/dist/lib/primitive/string/string.mjs +5 -0
  328. package/dist/lib/primitive/string/types.cjs +1 -0
  329. package/dist/lib/primitive/string/types.d.ts +318 -0
  330. package/dist/lib/primitive/string/types.d.ts.map +1 -0
  331. package/dist/lib/primitive/string/types.js +5 -0
  332. package/dist/lib/primitive/string/types.mjs +0 -0
  333. package/dist/lib/primitive/types.cjs +1 -0
  334. package/dist/lib/primitive/types.d.ts +27 -0
  335. package/dist/lib/primitive/types.d.ts.map +1 -0
  336. package/dist/lib/primitive/types.js +1 -0
  337. package/dist/lib/primitive/types.mjs +0 -0
  338. package/dist/lib/url/index.cjs +1 -0
  339. package/dist/lib/url/index.d.ts +3 -0
  340. package/dist/lib/url/index.d.ts.map +1 -0
  341. package/dist/lib/url/index.js +2 -0
  342. package/dist/lib/url/index.mjs +1 -0
  343. package/dist/lib/url/parser.cjs +1 -0
  344. package/dist/lib/url/parser.d.ts +216 -0
  345. package/dist/lib/url/parser.d.ts.map +1 -0
  346. package/dist/lib/url/parser.js +488 -0
  347. package/dist/lib/url/parser.mjs +1 -0
  348. package/dist/lib/url/types.cjs +1 -0
  349. package/dist/lib/url/types.d.ts +18 -0
  350. package/dist/lib/url/types.d.ts.map +1 -0
  351. package/dist/lib/url/types.js +1 -0
  352. package/dist/lib/url/types.mjs +0 -0
  353. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  354. package/package.json +150 -0
@@ -0,0 +1,1058 @@
1
+ /**
2
+ * Object transformation utilities for restructuring, mapping, and applying transformations.
3
+ * @module transform
4
+ */
5
+ import { isObject } from './utils.js';
6
+ import { get, set } from './getter-setter.js';
7
+ import { parseSelector } from './selector.js';
8
+ /**
9
+ * Maps object values with a transform function, keeping the same keys
10
+ *
11
+ * Creates a new object with the same keys as the input, but with transformed values
12
+ * using the provided function. The function receives the value, key, and original object.
13
+ *
14
+ * @template T - The input object type
15
+ * @template R - The transformed value type
16
+ * @param obj - The source object
17
+ * @param fn - The transform function to apply to each value
18
+ * @returns A new object with transformed values
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const prices = { apple: '1.25', banana: '0.75', orange: '1.00' };
23
+ * const numericPrices = mapValues(prices, price => parseFloat(price));
24
+ * // { apple: 1.25, banana: 0.75, orange: 1 }
25
+ * ```
26
+ */
27
+ export function mapValues(obj, fn) {
28
+ if (!isObject(obj)) {
29
+ return {};
30
+ }
31
+ const result = {};
32
+ for (const key in obj) {
33
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
34
+ result[key] = fn(obj[key], key, obj);
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ /**
40
+ * Maps object keys with a transform function, keeping the same values
41
+ *
42
+ * Creates a new object with transformed keys using the provided function,
43
+ * while maintaining the original values. The function receives the key, value, and original object.
44
+ *
45
+ * @template T - The input object type
46
+ * @param obj - The source object
47
+ * @param fn - The transform function to apply to each key
48
+ * @returns A new object with transformed keys
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * const data = { firstName: 'Alice', lastName: 'Smith' };
53
+ * const camelToSnake = mapKeys(data, key => key.replace(/[A-Z]/g, c => `_${c.toLowerCase()}`));
54
+ * // { first_name: 'Alice', last_name: 'Smith' }
55
+ * ```
56
+ */
57
+ export function mapKeys(obj, fn) {
58
+ if (!isObject(obj)) {
59
+ return {};
60
+ }
61
+ const result = {};
62
+ for (const key in obj) {
63
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
64
+ const newKey = fn(key, obj[key], obj);
65
+ result[newKey] = obj[key];
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Transforms keys to camelCase
72
+ *
73
+ * Creates a new object with all keys transformed to camelCase format.
74
+ * Handles keys in kebab-case, snake_case, and PascalCase.
75
+ *
76
+ * @template T - The input object type
77
+ * @param obj - The source object
78
+ * @param options - Transformation options
79
+ * @param options.deep - Whether to transform nested objects (default: true)
80
+ * @returns A new object with camelCase keys
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * const data = {
85
+ * first_name: 'Alice',
86
+ * 'last-name': 'Smith',
87
+ * ContactInfo: {
88
+ * email_address: 'alice@example.com',
89
+ * phone_number: '555-1234'
90
+ * }
91
+ * };
92
+ *
93
+ * const camelCased = camelCaseKeys(data);
94
+ * // {
95
+ * // firstName: 'Alice',
96
+ * // lastName: 'Smith',
97
+ * // contactInfo: {
98
+ * // emailAddress: 'alice@example.com',
99
+ * // phoneNumber: '555-1234'
100
+ * // }
101
+ * // }
102
+ * ```
103
+ */
104
+ export function camelCaseKeys(obj, options = { deep: true }) {
105
+ if (!isObject(obj)) {
106
+ return obj;
107
+ }
108
+ if (Array.isArray(obj)) {
109
+ return (options.deep ? obj.map((item) => camelCaseKeys(item, options)) : obj);
110
+ }
111
+ const result = {};
112
+ for (const key in obj) {
113
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
114
+ const value = obj[key];
115
+ const camelKey = key
116
+ .replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase())
117
+ .replace(/^([A-Z])/, (c) => c.toLowerCase());
118
+ result[camelKey] =
119
+ options.deep && isObject(value) ? camelCaseKeys(value, options) : value;
120
+ }
121
+ }
122
+ return result;
123
+ }
124
+ /**
125
+ * Transforms keys to snake_case
126
+ *
127
+ * Creates a new object with all keys transformed to snake_case format.
128
+ * Handles keys in camelCase, kebab-case, and PascalCase.
129
+ *
130
+ * @template T - The input object type
131
+ * @param obj - The source object
132
+ * @param options - Transformation options
133
+ * @param options.deep - Whether to transform nested objects (default: true)
134
+ * @returns A new object with snake_case keys
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * const data = {
139
+ * firstName: 'Alice',
140
+ * 'last-name': 'Smith',
141
+ * ContactInfo: {
142
+ * emailAddress: 'alice@example.com',
143
+ * phoneNumber: '555-1234'
144
+ * }
145
+ * };
146
+ *
147
+ * const snakeCased = snakeCaseKeys(data);
148
+ * // {
149
+ * // first_name: 'Alice',
150
+ * // last_name: 'Smith',
151
+ * // contact_info: {
152
+ * // email_address: 'alice@example.com',
153
+ * // phone_number: '555-1234'
154
+ * // }
155
+ * // }
156
+ * ```
157
+ */
158
+ export function snakeCaseKeys(obj, options = { deep: true }) {
159
+ if (!isObject(obj)) {
160
+ return obj;
161
+ }
162
+ if (Array.isArray(obj)) {
163
+ return (options.deep ? obj.map((item) => snakeCaseKeys(item, options)) : obj);
164
+ }
165
+ const result = {};
166
+ for (const key in obj) {
167
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
168
+ const value = obj[key];
169
+ const snakeKey = key
170
+ .replace(/[-]([a-z])/g, (_, c) => `_${c}`)
171
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
172
+ .toLowerCase();
173
+ result[snakeKey] =
174
+ options.deep && isObject(value) ? snakeCaseKeys(value, options) : value;
175
+ }
176
+ }
177
+ return result;
178
+ }
179
+ /**
180
+ * Transforms keys to kebab-case
181
+ *
182
+ * Creates a new object with all keys transformed to kebab-case format.
183
+ * Handles keys in camelCase, snake_case, and PascalCase.
184
+ *
185
+ * @template T - The input object type
186
+ * @param obj - The source object
187
+ * @param options - Transformation options
188
+ * @param options.deep - Whether to transform nested objects (default: true)
189
+ * @returns A new object with kebab-case keys
190
+ *
191
+ * @example
192
+ * ```ts
193
+ * const data = {
194
+ * firstName: 'Alice',
195
+ * last_name: 'Smith',
196
+ * ContactInfo: {
197
+ * emailAddress: 'alice@example.com',
198
+ * phone_number: '555-1234'
199
+ * }
200
+ * };
201
+ *
202
+ * const kebabCased = kebabCaseKeys(data);
203
+ * // {
204
+ * // 'first-name': 'Alice',
205
+ * // 'last-name': 'Smith',
206
+ * // 'contact-info': {
207
+ * // 'email-address': 'alice@example.com',
208
+ * // 'phone-number': '555-1234'
209
+ * // }
210
+ * // }
211
+ * ```
212
+ */
213
+ export function kebabCaseKeys(obj, options = { deep: true }) {
214
+ if (!isObject(obj)) {
215
+ return obj;
216
+ }
217
+ if (Array.isArray(obj)) {
218
+ return (options.deep ? obj.map((item) => kebabCaseKeys(item, options)) : obj);
219
+ }
220
+ const result = {};
221
+ for (const key in obj) {
222
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
223
+ const value = obj[key];
224
+ const kebabKey = key
225
+ .replace(/[_]([a-z])/g, (_, c) => `-${c}`)
226
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
227
+ .toLowerCase();
228
+ result[kebabKey] =
229
+ options.deep && isObject(value) ? kebabCaseKeys(value, options) : value;
230
+ }
231
+ }
232
+ return result;
233
+ }
234
+ /**
235
+ * Flattens a nested object structure into a single-level object with path-based keys
236
+ *
237
+ * Creates a new flat object where nested keys are represented as path strings.
238
+ * Supports custom path delimiters and array handling.
239
+ *
240
+ * @template T - The input object type
241
+ * @param obj - The source object to flatten
242
+ * @param options - Flattening options
243
+ * @param options.delimiter - Character(s) to separate path segments (default: '.')
244
+ * @param options.includeArrays - Whether to flatten arrays (default: true)
245
+ * @param options.maxDepth - Maximum depth to flatten (default: Infinity)
246
+ * @returns A flattened object with path keys
247
+ *
248
+ * @example
249
+ * ```ts
250
+ * const user = {
251
+ * name: {
252
+ * first: 'Alice',
253
+ * last: 'Smith'
254
+ * },
255
+ * contacts: [
256
+ * { type: 'email', value: 'alice@example.com' },
257
+ * { type: 'phone', value: '555-1234' }
258
+ * ]
259
+ * };
260
+ *
261
+ * const flattened = flatten(user);
262
+ * // {
263
+ * // 'name.first': 'Alice',
264
+ * // 'name.last': 'Smith',
265
+ * // 'contacts.0.type': 'email',
266
+ * // 'contacts.0.value': 'alice@example.com',
267
+ * // 'contacts.1.type': 'phone',
268
+ * // 'contacts.1.value': '555-1234'
269
+ * // }
270
+ * ```
271
+ */
272
+ // export function flatten<T extends AnyObject>(
273
+ // obj: T,
274
+ // options: {
275
+ // delimiter?: string;
276
+ // includeArrays?: boolean;
277
+ // maxDepth?: number;
278
+ // } = {}
279
+ // ): Record<string, any> {
280
+ // const {
281
+ // delimiter = '.',
282
+ // includeArrays = true,
283
+ // maxDepth = Infinity,
284
+ // } = options;
285
+ // const result: Record<string, any> = {};
286
+ // function _flatten(current: any, path: string = '', depth: number = 0) {
287
+ // if (depth > maxDepth) {
288
+ // result[path] = current;
289
+ // return;
290
+ // }
291
+ // if (!isObject(current)) {
292
+ // result[path] = current;
293
+ // return;
294
+ // }
295
+ // if (Array.isArray(current) && !includeArrays) {
296
+ // result[path] = current;
297
+ // return;
298
+ // }
299
+ // if (Object.keys(current).length === 0) {
300
+ // result[path] = current;
301
+ // return;
302
+ // }
303
+ // const prefix = path ? `${path}${delimiter}` : '';
304
+ // if (Array.isArray(current)) {
305
+ // for (let i = 0; i < current.length; i++) {
306
+ // _flatten(current[i], `${prefix}${i}`, depth + 1);
307
+ // }
308
+ // return;
309
+ // }
310
+ // for (const key in current) {
311
+ // if (Object.prototype.hasOwnProperty.call(current, key)) {
312
+ // _flatten(current[key], `${prefix}${key}`, depth + 1);
313
+ // }
314
+ // }
315
+ // }
316
+ // _flatten(obj);
317
+ // return result;
318
+ // }
319
+ export const flatten = (obj, options = {}) => {
320
+ const { delimiter = '.', includeArrays = false, maxDepth = 9 } = options;
321
+ if (Array.isArray(obj)) {
322
+ return obj.map((item) => flatten(item, options));
323
+ // return obj.reduce((acc, item) => {
324
+ // return {
325
+ // ...acc,
326
+ // ...flatten(item, separator),
327
+ // };
328
+ // }, {});
329
+ }
330
+ const result = {};
331
+ const flattenRecursive = (current, path = '', depth = 0) => {
332
+ if (depth > maxDepth) {
333
+ result[path] = current;
334
+ return;
335
+ }
336
+ if (!isObject(current) && !Array.isArray(current)) {
337
+ if (path) {
338
+ result[path] = current;
339
+ }
340
+ return;
341
+ }
342
+ if (Array.isArray(current)) {
343
+ for (let i = 0; i < current.length; i++) {
344
+ const newPath = path ? `${path}[${i}]` : `[${i}]`;
345
+ flattenRecursive(current[i], newPath, depth + 1);
346
+ }
347
+ }
348
+ else {
349
+ for (const key in current) {
350
+ const newPath = path ? `${path}${delimiter}${key}` : key;
351
+ flattenRecursive(current[key], newPath, depth + 1);
352
+ }
353
+ // Handle symbols
354
+ const symbols = Object.getOwnPropertySymbols(current);
355
+ for (const symbol of symbols) {
356
+ const newPath = path
357
+ ? `${path}${delimiter}${symbol.toString()}`
358
+ : symbol.toString();
359
+ flattenRecursive(current[symbol], newPath, depth + 1);
360
+ }
361
+ }
362
+ };
363
+ flattenRecursive(obj);
364
+ return result;
365
+ };
366
+ /**
367
+ * Unflatten a single-level object with path-based keys into a nested object structure
368
+ *
369
+ * Reverses the flattening process, converting an object with path keys back into a nested structure.
370
+ * Supports custom path delimiters and array detection.
371
+ *
372
+ * @param obj - The flattened object to unflatten
373
+ * @param options - Unflattening options
374
+ * @param options.delimiter - Character(s) that separate path segments (default: '.')
375
+ * @param options.detectArrays - Whether to convert numeric indices to arrays (default: true)
376
+ * @returns A nested object structure
377
+ *
378
+ * @example
379
+ * ```ts
380
+ * const flattened = {
381
+ * 'user.name.first': 'Alice',
382
+ * 'user.name.last': 'Smith',
383
+ * 'user.contacts.0.type': 'email',
384
+ * 'user.contacts.0.value': 'alice@example.com',
385
+ * 'user.contacts.1.type': 'phone',
386
+ * 'user.contacts.1.value': '555-1234'
387
+ * };
388
+ *
389
+ * const nested = unflatten(flattened);
390
+ * // {
391
+ * // user: {
392
+ * // name: {
393
+ * // first: 'Alice',
394
+ * // last: 'Smith'
395
+ * // },
396
+ * // contacts: [
397
+ * // { type: 'email', value: 'alice@example.com' },
398
+ * // { type: 'phone', value: '555-1234' }
399
+ * // ]
400
+ * // }
401
+ * // }
402
+ * ```
403
+ */
404
+ export function unflatten(obj, options = {}) {
405
+ const { delimiter = '.', detectArrays = true } = options;
406
+ const result = {};
407
+ for (const key in obj) {
408
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
409
+ const value = obj[key];
410
+ const parts = key.split(delimiter);
411
+ let current = result;
412
+ for (let i = 0; i < parts.length - 1; i++) {
413
+ const part = parts[i];
414
+ const nextPart = parts[i + 1];
415
+ const isNextPartArrayIndex = detectArrays && /^\d+$/.test(nextPart);
416
+ if (!(part in current)) {
417
+ current[part] = isNextPartArrayIndex ? [] : {};
418
+ }
419
+ else if (isNextPartArrayIndex && !Array.isArray(current[part])) {
420
+ // Convert to array if needed
421
+ const temp = { ...current[part] };
422
+ current[part] = [];
423
+ Object.assign(current[part], temp);
424
+ }
425
+ current = current[part];
426
+ }
427
+ const lastPart = parts[parts.length - 1];
428
+ current[lastPart] = value;
429
+ }
430
+ }
431
+ // Convert objects with sequential numeric keys to arrays
432
+ if (detectArrays) {
433
+ const convertToArrayIfNeeded = (obj) => {
434
+ if (!isObject(obj))
435
+ return obj;
436
+ if (Array.isArray(obj)) {
437
+ return obj.map(convertToArrayIfNeeded);
438
+ }
439
+ // Check if object has sequential numeric keys starting at 0
440
+ const keys = Object.keys(obj);
441
+ const isNumericSequence = keys.length > 0 &&
442
+ keys.every((key, index) => {
443
+ return String(index) === key;
444
+ });
445
+ if (isNumericSequence) {
446
+ return Object.values(obj).map(convertToArrayIfNeeded);
447
+ }
448
+ // Recursively process all values
449
+ const result = {};
450
+ for (const key in obj) {
451
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
452
+ result[key] = convertToArrayIfNeeded(obj[key]);
453
+ }
454
+ }
455
+ return result;
456
+ };
457
+ return convertToArrayIfNeeded(result);
458
+ }
459
+ return result;
460
+ }
461
+ /**
462
+ * Transforms object values by applying a series of transform functions
463
+ *
464
+ * Creates a new object with the same structure as the input,
465
+ * but with all values transformed by the provided sequence of functions.
466
+ * Each function is applied in order to the result of the previous function.
467
+ *
468
+ * @template T - The input object type
469
+ * @param obj - The source object
470
+ * @param transformers - One or more transform functions to apply
471
+ * @returns A new object with transformed values
472
+ *
473
+ * @example
474
+ * ```ts
475
+ * const data = {
476
+ * name: ' Alice Smith ',
477
+ * email: 'ALICE@EXAMPLE.COM',
478
+ * score: '85'
479
+ * };
480
+ *
481
+ * // Apply multiple transforms: trim strings, convert to lowercase, and parse numbers
482
+ * const transformed = transformValues(data,
483
+ * value => typeof value === 'string' ? value.trim() : value,
484
+ * value => typeof value === 'string' ? value.toLowerCase() : value,
485
+ * value => !isNaN(Number(value)) ? Number(value) : value
486
+ * );
487
+ *
488
+ * // {
489
+ * // name: 'alice smith',
490
+ * // email: 'alice@example.com',
491
+ * // score: 85
492
+ * // }
493
+ * ```
494
+ */
495
+ export function transformValues(obj, ...transformers) {
496
+ if (!isObject(obj) || transformers.length === 0) {
497
+ return obj;
498
+ }
499
+ const result = { ...obj };
500
+ for (const key in result) {
501
+ if (Object.prototype.hasOwnProperty.call(result, key)) {
502
+ let value = result[key];
503
+ for (const transformer of transformers) {
504
+ value = transformer(value);
505
+ }
506
+ result[key] = value;
507
+ }
508
+ }
509
+ return result;
510
+ }
511
+ /**
512
+ * Renames keys in an object according to a mapping
513
+ *
514
+ * Creates a new object with keys renamed according to the provided mapping.
515
+ * Keys not in the mapping are kept unchanged.
516
+ *
517
+ * @template T - The input object type
518
+ * @param obj - The source object
519
+ * @param mapping - Object mapping from original keys to new keys
520
+ * @param options - Renaming options
521
+ * @param options.removeUnmapped - Whether to remove keys not in the mapping (default: false)
522
+ * @returns A new object with renamed keys
523
+ *
524
+ * @example
525
+ * ```ts
526
+ * const user = {
527
+ * first_name: 'Alice',
528
+ * last_name: 'Smith',
529
+ * email: 'alice@example.com',
530
+ * age: 30
531
+ * };
532
+ *
533
+ * const renamed = renameKeys(user, {
534
+ * first_name: 'firstName',
535
+ * last_name: 'lastName'
536
+ * });
537
+ *
538
+ * // {
539
+ * // firstName: 'Alice',
540
+ * // lastName: 'Smith',
541
+ * // email: 'alice@example.com',
542
+ * // age: 30
543
+ * // }
544
+ * ```
545
+ */
546
+ export function renameKeys(obj, mapping, options = {}) {
547
+ if (!isObject(obj)) {
548
+ return obj;
549
+ }
550
+ const { removeUnmapped = false } = options;
551
+ const result = {};
552
+ for (const key in obj) {
553
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
554
+ if (key in mapping) {
555
+ result[mapping[key]] = obj[key];
556
+ }
557
+ else if (!removeUnmapped) {
558
+ result[key] = obj[key];
559
+ }
560
+ }
561
+ }
562
+ return result;
563
+ }
564
+ /**
565
+ * Deeply transforms objects, arrays, and values in a nested structure
566
+ *
567
+ * Recursively traverses an object or array and applies different transform functions
568
+ * based on the type of each node (object, array, or primitive value).
569
+ *
570
+ * @template T - The input type
571
+ * @param data - The data to transform
572
+ * @param options - Transform options
573
+ * @param options.transformObject - Function to transform objects (default: identity)
574
+ * @param options.transformArray - Function to transform arrays (default: identity)
575
+ * @param options.transformValue - Function to transform primitive values (default: identity)
576
+ * @param options.maxDepth - Maximum depth to transform (default: Infinity)
577
+ * @returns The transformed data
578
+ *
579
+ * @example
580
+ * ```ts
581
+ * const data = {
582
+ * user: {
583
+ * name: 'Alice Smith',
584
+ * scores: [85, 92, 78],
585
+ * active: true
586
+ * },
587
+ * metadata: {
588
+ * lastUpdated: '2023-01-15'
589
+ * }
590
+ * };
591
+ *
592
+ * // Apply different transformations to objects, arrays, and primitive values
593
+ * const result = deepTransform(data, {
594
+ * transformObject: obj => ({ ...obj, _transformed: true }),
595
+ * transformArray: arr => arr.map(x => typeof x === 'number' ? x + 10 : x),
596
+ * transformValue: (val, key) => key === 'name' ? val.toUpperCase() : val
597
+ * });
598
+ *
599
+ * // {
600
+ * // user: {
601
+ * // name: 'ALICE SMITH',
602
+ * // scores: [95, 102, 88],
603
+ * // active: true,
604
+ * // _transformed: true
605
+ * // },
606
+ * // metadata: {
607
+ * // lastUpdated: '2023-01-15',
608
+ * // _transformed: true
609
+ * // },
610
+ * // _transformed: true
611
+ * // }
612
+ * ```
613
+ */
614
+ export function deepTransform(data, options = {}) {
615
+ const { transformObject = (x) => x, transformArray = (x) => x, transformValue = (x) => x, maxDepth = Infinity, } = options;
616
+ function transform(value, path = [], depth = 0) {
617
+ if (depth > maxDepth) {
618
+ return value;
619
+ }
620
+ if (Array.isArray(value)) {
621
+ const transformedArray = value.map((item, index) => transform(item, [...path, index.toString()], depth + 1));
622
+ return transformArray(transformedArray, path);
623
+ }
624
+ if (isObject(value)) {
625
+ const transformedObj = {};
626
+ for (const key in value) {
627
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
628
+ const newPath = [...path, key];
629
+ transformedObj[key] = transform(value[key], newPath, depth + 1);
630
+ // Apply value transformation to object properties
631
+ if (!Array.isArray(value[key]) && !isObject(value[key])) {
632
+ transformedObj[key] = transformValue(transformedObj[key], key, newPath);
633
+ }
634
+ }
635
+ }
636
+ return transformObject(transformedObj, path);
637
+ }
638
+ // If this is a top-level primitive, it doesn't have a key
639
+ const key = path.length > 0 ? path[path.length - 1] : '';
640
+ return transformValue(value, key, path);
641
+ }
642
+ return transform(data);
643
+ }
644
+ /**
645
+ * Transforms a flat object with key-value pairs into a structured object by key pattern
646
+ *
647
+ * Useful for converting flat configuration objects or query parameters into structured objects.
648
+ * Groups keys by a shared prefix and nests them accordingly.
649
+ *
650
+ * @param obj - The flat object to structure
651
+ * @param options - Structuring options
652
+ * @param options.separator - The character(s) that separate key parts (default: '.')
653
+ * @param options.detectArrays - Whether to convert numeric indices to arrays (default: true)
654
+ * @returns A structured object
655
+ *
656
+ * @example
657
+ * ```ts
658
+ * const params = {
659
+ * 'user.name': 'Alice',
660
+ * 'user.email': 'alice@example.com',
661
+ * 'preferences.theme': 'dark',
662
+ * 'preferences.notifications': 'true',
663
+ * 'items.0.id': '1001',
664
+ * 'items.0.name': 'Widget',
665
+ * 'items.1.id': '1002',
666
+ * 'items.1.name': 'Gadget'
667
+ * };
668
+ *
669
+ * const structured = structureByKeys(params);
670
+ * // {
671
+ * // user: {
672
+ * // name: 'Alice',
673
+ * // email: 'alice@example.com'
674
+ * // },
675
+ * // preferences: {
676
+ * // theme: 'dark',
677
+ * // notifications: 'true'
678
+ * // },
679
+ * // items: [
680
+ * // { id: '1001', name: 'Widget' },
681
+ * // { id: '1002', name: 'Gadget' }
682
+ * // ]
683
+ * // }
684
+ * ```
685
+ */
686
+ export function structureByKeys(obj, options = {}) {
687
+ // This is essentially just an alias for unflatten with a more descriptive name
688
+ return unflatten(obj, options);
689
+ }
690
+ /**
691
+ * Transforms values at specific paths in an object
692
+ *
693
+ * Applies transform functions to values at the specified path(s),
694
+ * supporting advanced path selectors including property paths, array indices,
695
+ * wildcards, and filters. Supports transforming multiple paths in a single operation.
696
+ *
697
+ * @template T - The input object type
698
+ * @param obj - The source object
699
+ * @param paths - Path selector or array of path selectors to the values to transform
700
+ * @param transformer - Function to transform the value
701
+ * @param options - Additional options
702
+ * @param options.transformAll - Whether to apply transformer to all paths or individually (default: false)
703
+ * @returns A new object with the transformed value(s)
704
+ *
705
+ * @example
706
+ * ```ts
707
+ * const user = {
708
+ * id: 1,
709
+ * name: 'Alice Smith',
710
+ * profile: {
711
+ * email: 'alice@example.com',
712
+ * settings: {
713
+ * notifications: true
714
+ * }
715
+ * },
716
+ * posts: [
717
+ * { id: 101, title: 'Hello', likes: 5 },
718
+ * { id: 102, title: 'World', likes: 10 }
719
+ * ]
720
+ * };
721
+ *
722
+ * // Transform a simple property
723
+ * const result1 = transformPath(user, 'name', name => name.toUpperCase());
724
+ * // user.name becomes 'ALICE SMITH'
725
+ *
726
+ * // Transform a nested property
727
+ * const result2 = transformPath(user, 'profile.email', email => email.split('@')[0] + '@example.org');
728
+ * // user.profile.email becomes 'alice@example.org'
729
+ *
730
+ * // Transform using wildcards
731
+ * const result3 = transformPath(user, 'posts[*].likes', likes => likes * 2);
732
+ * // All post likes are doubled
733
+ *
734
+ * // Transform multiple paths
735
+ * const result4 = transformPath(
736
+ * user,
737
+ * ['name', 'profile.email', 'posts[*].likes'],
738
+ * value => {
739
+ * if (typeof value === 'string') return value.toUpperCase();
740
+ * if (typeof value === 'number') return value * 2;
741
+ * return value;
742
+ * }
743
+ * );
744
+ * // Transforms multiple properties at once
745
+ *
746
+ * // Use path-specific transformations
747
+ * const result5 = transformPath(
748
+ * user,
749
+ * ['name', 'profile.email', 'posts[*].likes'],
750
+ * (value, path) => {
751
+ * if (path === 'name') return value.toUpperCase();
752
+ * if (path === 'profile.email') return value.split('@')[0] + '@example.org';
753
+ * if (path.includes('likes')) return value + 10;
754
+ * return value;
755
+ * },
756
+ * { transformAll: true }
757
+ * );
758
+ * ```
759
+ */
760
+ export function transformPath(obj, paths, transformer, options = {}) {
761
+ if (!isObject(obj)) {
762
+ return obj;
763
+ }
764
+ // Deep clone to avoid modifying the original
765
+ const result = JSON.parse(JSON.stringify(obj));
766
+ const { transformAll = false } = options;
767
+ const pathsArray = Array.isArray(paths) ? paths : [paths];
768
+ // Process each path
769
+ for (const path of pathsArray) {
770
+ if (path.includes('*') || path.includes('[')) {
771
+ // Complex path with wildcards or array access
772
+ const matches = get(result, path, { preserveStructure: true });
773
+ if (matches === undefined) {
774
+ continue;
775
+ }
776
+ // Function to collect and transform all matching paths
777
+ function applyTransform(obj, basePath = '') {
778
+ if (!isObject(obj))
779
+ return;
780
+ if (Array.isArray(obj)) {
781
+ obj.forEach((item, index) => {
782
+ const currentPath = basePath
783
+ ? `${basePath}[${index}]`
784
+ : `[${index}]`;
785
+ applyTransform(item, currentPath);
786
+ });
787
+ return;
788
+ }
789
+ for (const key in obj) {
790
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
791
+ const value = obj[key];
792
+ const currentPath = basePath ? `${basePath}.${key}` : key;
793
+ // If we've reached a leaf node or primitive value, transform it
794
+ if (value === null || typeof value !== 'object') {
795
+ const parent = basePath ? get(result, basePath) : result;
796
+ const transformed = transformAll
797
+ ? transformer(value, currentPath, parent)
798
+ : transformer(value, key, parent);
799
+ obj[key] = transformed;
800
+ }
801
+ else {
802
+ // Continue traversing
803
+ applyTransform(value, currentPath);
804
+ }
805
+ }
806
+ }
807
+ }
808
+ applyTransform(matches);
809
+ // Update the original object with our changes
810
+ const segments = parseSelector(path);
811
+ if (segments.length > 0) {
812
+ const firstSegment = segments[0];
813
+ if (firstSegment.type === 'property') {
814
+ result[String(firstSegment.value)] =
815
+ matches[String(firstSegment.value)];
816
+ }
817
+ }
818
+ }
819
+ else {
820
+ // Simple path - direct property access
821
+ const segments = path.split('.');
822
+ function setValueAtPath(obj, segments, index, fullPath) {
823
+ if (index >= segments.length)
824
+ return obj;
825
+ const key = segments[index];
826
+ if (index === segments.length - 1) {
827
+ // We've reached the target property
828
+ const parent = index > 0
829
+ ? setValueAtPath(obj, segments, index - 1, segments.slice(0, index).join('.'))
830
+ : obj;
831
+ const transformed = transformAll
832
+ ? transformer(parent[key], fullPath, parent)
833
+ : transformer(parent[key], key, parent);
834
+ parent[key] = transformed;
835
+ return parent;
836
+ }
837
+ // Continue traversing
838
+ const nextObj = obj[key] || {};
839
+ obj[key] = setValueAtPath(nextObj, segments, index + 1, fullPath);
840
+ return obj;
841
+ }
842
+ setValueAtPath(result, segments, 0, path);
843
+ }
844
+ }
845
+ return result;
846
+ }
847
+ /**
848
+ * Maps values at specific paths in an object
849
+ *
850
+ * Similar to transformPath, but returns an array of transformed values
851
+ * rather than modifying the object structure. Supports mapping multiple paths
852
+ * in a single operation.
853
+ *
854
+ * @template T - The input object type
855
+ * @template R - The output value type
856
+ * @param obj - The source object
857
+ * @param paths - Path selector or array of path selectors to the values to map
858
+ * @param mapper - Function to transform each value
859
+ * @param options - Additional options
860
+ * @param options.flatten - Whether to flatten the results into a single array (default: true)
861
+ * @returns An array of transformed values
862
+ *
863
+ * @example
864
+ * ```ts
865
+ * const data = {
866
+ * users: [
867
+ * { id: 1, name: 'Alice', age: 30 },
868
+ * { id: 2, name: 'Bob', age: 25 },
869
+ * { id: 3, name: 'Charlie', age: 35 }
870
+ * ]
871
+ * };
872
+ *
873
+ * // Extract and transform user names
874
+ * const names = mapPath(data, 'users[*].name', name => name.toUpperCase());
875
+ * // ['ALICE', 'BOB', 'CHARLIE']
876
+ *
877
+ * // Filter and transform
878
+ * const youngUsers = mapPath(data, 'users[?(@.age < 30)].name', (name, user) => ({
879
+ * name,
880
+ * greeting: `Hello, ${name}!`
881
+ * }));
882
+ * // [{ name: 'Bob', greeting: 'Hello, Bob!' }]
883
+ *
884
+ * // Map multiple paths
885
+ * const info = mapPath(
886
+ * data,
887
+ * ['users[*].name', 'users[*].age'],
888
+ * value => typeof value === 'string' ? value.toUpperCase() : value
889
+ * );
890
+ * // ['ALICE', 'BOB', 'CHARLIE', 30, 25, 35]
891
+ *
892
+ * // Map multiple paths with path context
893
+ * const info = mapPath(
894
+ * data,
895
+ * ['users[*].name', 'users[*].age'],
896
+ * (value, path) => {
897
+ * if (path.includes('name')) return `Name: ${value}`;
898
+ * if (path.includes('age')) return `Age: ${value}`;
899
+ * return value;
900
+ * }
901
+ * );
902
+ * // ['Name: Alice', 'Name: Bob', 'Name: Charlie', 'Age: 30', 'Age: 25', 'Age: 35']
903
+ * ```
904
+ */
905
+ export function mapPath(obj, paths, mapper, options = {}) {
906
+ if (!isObject(obj)) {
907
+ return [];
908
+ }
909
+ const { flatten = true } = options;
910
+ const pathsArray = Array.isArray(paths) ? paths : [paths];
911
+ // Process each path
912
+ const results = pathsArray.map((path) => {
913
+ const values = get(obj, path);
914
+ if (values === undefined) {
915
+ return [];
916
+ }
917
+ if (Array.isArray(values)) {
918
+ return values.map((value, index) => {
919
+ const fullPath = `${path}[${index}]`;
920
+ return mapper(value, fullPath, obj);
921
+ });
922
+ }
923
+ return [mapper(values, path, obj)];
924
+ });
925
+ // Return flattened or grouped results
926
+ return flatten ? results.flat() : results;
927
+ }
928
+ /**
929
+ * Transforms keys at specific paths in an object
930
+ *
931
+ * Applies a transform function to keys at the specified path(s),
932
+ * supporting advanced path selectors. Supports transforming keys at multiple paths
933
+ * in a single operation.
934
+ *
935
+ * @template T - The input object type
936
+ * @param obj - The source object
937
+ * @param paths - Path selector or array of path selectors to the objects whose keys should be transformed
938
+ * @param transformer - Function to transform each key
939
+ * @param options - Additional options
940
+ * @param options.recursive - Whether to recursively transform nested object keys (default: false)
941
+ * @returns A new object with transformed keys at the specified path(s)
942
+ *
943
+ * @example
944
+ * ```ts
945
+ * const data = {
946
+ * user: {
947
+ * personal_info: {
948
+ * first_name: 'Alice',
949
+ * last_name: 'Smith',
950
+ * email_address: 'alice@example.com'
951
+ * },
952
+ * work_info: {
953
+ * job_title: 'Developer',
954
+ * department_name: 'Engineering'
955
+ * }
956
+ * }
957
+ * };
958
+ *
959
+ * // Transform snake_case keys to camelCase at a single path
960
+ * const result1 = transformKeysAtPath(data, 'user.personal_info', key =>
961
+ * key.replace(/_([a-z])/g, (_, c) => c.toUpperCase())
962
+ * );
963
+ *
964
+ * // Transform keys at multiple paths
965
+ * const result2 = transformKeysAtPath(
966
+ * data,
967
+ * ['user.personal_info', 'user.work_info'],
968
+ * key => key.replace(/_([a-z])/g, (_, c) => c.toUpperCase())
969
+ * );
970
+ *
971
+ * // Recursively transform all nested keys
972
+ * const result3 = transformKeysAtPath(
973
+ * data,
974
+ * 'user',
975
+ * key => key.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),
976
+ * { recursive: true }
977
+ * );
978
+ *
979
+ * // Result3:
980
+ * // {
981
+ * // user: {
982
+ * // personalInfo: {
983
+ * // firstName: 'Alice',
984
+ * // lastName: 'Smith',
985
+ * // emailAddress: 'alice@example.com'
986
+ * // },
987
+ * // workInfo: {
988
+ * // jobTitle: 'Developer',
989
+ * // departmentName: 'Engineering'
990
+ * // }
991
+ * // }
992
+ * // }
993
+ * ```
994
+ */
995
+ export function transformKeysAtPath(obj, paths, transformer, options = {}) {
996
+ if (!isObject(obj)) {
997
+ return obj;
998
+ }
999
+ // Deep clone to avoid modifying the original
1000
+ const result = JSON.parse(JSON.stringify(obj));
1001
+ const { recursive = false } = options;
1002
+ const pathsArray = Array.isArray(paths) ? paths : [paths];
1003
+ for (const path of pathsArray) {
1004
+ // Get the object at the specified path
1005
+ const target = path ? get(result, path) : result;
1006
+ if (!isObject(target)) {
1007
+ continue;
1008
+ }
1009
+ // Function to recursively transform object keys
1010
+ const transformObjectKeys = (obj, currentPath = path) => {
1011
+ if (!isObject(obj)) {
1012
+ return obj;
1013
+ }
1014
+ if (Array.isArray(obj)) {
1015
+ return obj.map((item, index) => isObject(item) && !Array.isArray(item)
1016
+ ? transformObjectKeys(item, `${currentPath}[${index}]`)
1017
+ : item);
1018
+ }
1019
+ const transformed = {};
1020
+ for (const key in obj) {
1021
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
1022
+ const newKey = transformer(key, obj[key], currentPath);
1023
+ // Recursively transform nested objects if option is enabled
1024
+ if (recursive && isObject(obj[key])) {
1025
+ transformed[newKey] = transformObjectKeys(obj[key], `${currentPath}.${newKey}`);
1026
+ }
1027
+ else {
1028
+ transformed[newKey] = obj[key];
1029
+ }
1030
+ }
1031
+ }
1032
+ return transformed;
1033
+ };
1034
+ if (Array.isArray(target)) {
1035
+ // Handle arrays of objects
1036
+ for (let i = 0; i < target.length; i++) {
1037
+ if (isObject(target[i]) && !Array.isArray(target[i])) {
1038
+ target[i] = transformObjectKeys(target[i], `${path}[${i}]`);
1039
+ }
1040
+ }
1041
+ }
1042
+ else {
1043
+ // Handle plain objects
1044
+ const transformed = transformObjectKeys(target);
1045
+ // Update the object at path
1046
+ if (path) {
1047
+ set(result, path, transformed);
1048
+ }
1049
+ else {
1050
+ Object.keys(result).forEach((key) => {
1051
+ delete result[key];
1052
+ });
1053
+ Object.assign(result, transformed);
1054
+ }
1055
+ }
1056
+ }
1057
+ return result;
1058
+ }