fp-pack 0.1.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.
Files changed (927) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +664 -0
  3. package/dist/fp-pack-stream.umd.js +2 -0
  4. package/dist/fp-pack-stream.umd.js.map +1 -0
  5. package/dist/fp-pack.umd.js +2 -0
  6. package/dist/fp-pack.umd.js.map +1 -0
  7. package/dist/implement/array/append.d.ts +7 -0
  8. package/dist/implement/array/append.d.ts.map +1 -0
  9. package/dist/implement/array/append.mjs +9 -0
  10. package/dist/implement/array/append.mjs.map +1 -0
  11. package/dist/implement/array/chunk.d.ts +7 -0
  12. package/dist/implement/array/chunk.d.ts.map +1 -0
  13. package/dist/implement/array/chunk.mjs +15 -0
  14. package/dist/implement/array/chunk.mjs.map +1 -0
  15. package/dist/implement/array/concat.d.ts +7 -0
  16. package/dist/implement/array/concat.d.ts.map +1 -0
  17. package/dist/implement/array/concat.mjs +9 -0
  18. package/dist/implement/array/concat.mjs.map +1 -0
  19. package/dist/implement/array/drop.d.ts +7 -0
  20. package/dist/implement/array/drop.d.ts.map +1 -0
  21. package/dist/implement/array/drop.mjs +10 -0
  22. package/dist/implement/array/drop.mjs.map +1 -0
  23. package/dist/implement/array/dropWhile.d.ts +7 -0
  24. package/dist/implement/array/dropWhile.d.ts.map +1 -0
  25. package/dist/implement/array/dropWhile.mjs +13 -0
  26. package/dist/implement/array/dropWhile.mjs.map +1 -0
  27. package/dist/implement/array/every.d.ts +7 -0
  28. package/dist/implement/array/every.d.ts.map +1 -0
  29. package/dist/implement/array/every.mjs +9 -0
  30. package/dist/implement/array/every.mjs.map +1 -0
  31. package/dist/implement/array/filter.d.ts +7 -0
  32. package/dist/implement/array/filter.d.ts.map +1 -0
  33. package/dist/implement/array/filter.mjs +9 -0
  34. package/dist/implement/array/filter.mjs.map +1 -0
  35. package/dist/implement/array/find.d.ts +7 -0
  36. package/dist/implement/array/find.d.ts.map +1 -0
  37. package/dist/implement/array/find.mjs +9 -0
  38. package/dist/implement/array/find.mjs.map +1 -0
  39. package/dist/implement/array/flatMap.d.ts +7 -0
  40. package/dist/implement/array/flatMap.d.ts.map +1 -0
  41. package/dist/implement/array/flatMap.mjs +9 -0
  42. package/dist/implement/array/flatMap.mjs.map +1 -0
  43. package/dist/implement/array/flatten.d.ts +6 -0
  44. package/dist/implement/array/flatten.d.ts.map +1 -0
  45. package/dist/implement/array/flatten.mjs +7 -0
  46. package/dist/implement/array/flatten.mjs.map +1 -0
  47. package/dist/implement/array/flattenDeep.d.ts +6 -0
  48. package/dist/implement/array/flattenDeep.d.ts.map +1 -0
  49. package/dist/implement/array/flattenDeep.mjs +11 -0
  50. package/dist/implement/array/flattenDeep.mjs.map +1 -0
  51. package/dist/implement/array/groupBy.d.ts +7 -0
  52. package/dist/implement/array/groupBy.d.ts.map +1 -0
  53. package/dist/implement/array/groupBy.mjs +12 -0
  54. package/dist/implement/array/groupBy.mjs.map +1 -0
  55. package/dist/implement/array/head.d.ts +6 -0
  56. package/dist/implement/array/head.d.ts.map +1 -0
  57. package/dist/implement/array/head.mjs +7 -0
  58. package/dist/implement/array/head.mjs.map +1 -0
  59. package/dist/implement/array/index.d.ts +34 -0
  60. package/dist/implement/array/index.d.ts.map +1 -0
  61. package/dist/implement/array/init.d.ts +6 -0
  62. package/dist/implement/array/init.d.ts.map +1 -0
  63. package/dist/implement/array/init.mjs +7 -0
  64. package/dist/implement/array/init.mjs.map +1 -0
  65. package/dist/implement/array/last.d.ts +6 -0
  66. package/dist/implement/array/last.d.ts.map +1 -0
  67. package/dist/implement/array/last.mjs +8 -0
  68. package/dist/implement/array/last.mjs.map +1 -0
  69. package/dist/implement/array/map.d.ts +7 -0
  70. package/dist/implement/array/map.d.ts.map +1 -0
  71. package/dist/implement/array/map.mjs +9 -0
  72. package/dist/implement/array/map.mjs.map +1 -0
  73. package/dist/implement/array/partition.d.ts +7 -0
  74. package/dist/implement/array/partition.d.ts.map +1 -0
  75. package/dist/implement/array/partition.mjs +12 -0
  76. package/dist/implement/array/partition.mjs.map +1 -0
  77. package/dist/implement/array/prepend.d.ts +7 -0
  78. package/dist/implement/array/prepend.d.ts.map +1 -0
  79. package/dist/implement/array/prepend.mjs +9 -0
  80. package/dist/implement/array/prepend.mjs.map +1 -0
  81. package/dist/implement/array/range.d.ts +6 -0
  82. package/dist/implement/array/range.d.ts.map +1 -0
  83. package/dist/implement/array/range.mjs +12 -0
  84. package/dist/implement/array/range.mjs.map +1 -0
  85. package/dist/implement/array/reduce.d.ts +8 -0
  86. package/dist/implement/array/reduce.d.ts.map +1 -0
  87. package/dist/implement/array/reduce.mjs +9 -0
  88. package/dist/implement/array/reduce.mjs.map +1 -0
  89. package/dist/implement/array/scan.d.ts +8 -0
  90. package/dist/implement/array/scan.d.ts.map +1 -0
  91. package/dist/implement/array/scan.mjs +13 -0
  92. package/dist/implement/array/scan.mjs.map +1 -0
  93. package/dist/implement/array/some.d.ts +7 -0
  94. package/dist/implement/array/some.d.ts.map +1 -0
  95. package/dist/implement/array/some.mjs +11 -0
  96. package/dist/implement/array/some.mjs.map +1 -0
  97. package/dist/implement/array/sort.d.ts +7 -0
  98. package/dist/implement/array/sort.d.ts.map +1 -0
  99. package/dist/implement/array/sort.mjs +9 -0
  100. package/dist/implement/array/sort.mjs.map +1 -0
  101. package/dist/implement/array/sortBy.d.ts +7 -0
  102. package/dist/implement/array/sortBy.d.ts.map +1 -0
  103. package/dist/implement/array/sortBy.mjs +12 -0
  104. package/dist/implement/array/sortBy.mjs.map +1 -0
  105. package/dist/implement/array/tail.d.ts +6 -0
  106. package/dist/implement/array/tail.d.ts.map +1 -0
  107. package/dist/implement/array/tail.mjs +7 -0
  108. package/dist/implement/array/tail.mjs.map +1 -0
  109. package/dist/implement/array/take.d.ts +7 -0
  110. package/dist/implement/array/take.d.ts.map +1 -0
  111. package/dist/implement/array/take.mjs +9 -0
  112. package/dist/implement/array/take.mjs.map +1 -0
  113. package/dist/implement/array/takeWhile.d.ts +7 -0
  114. package/dist/implement/array/takeWhile.d.ts.map +1 -0
  115. package/dist/implement/array/takeWhile.mjs +15 -0
  116. package/dist/implement/array/takeWhile.mjs.map +1 -0
  117. package/dist/implement/array/uniq.d.ts +6 -0
  118. package/dist/implement/array/uniq.d.ts.map +1 -0
  119. package/dist/implement/array/uniq.mjs +10 -0
  120. package/dist/implement/array/uniq.mjs.map +1 -0
  121. package/dist/implement/array/uniqBy.d.ts +7 -0
  122. package/dist/implement/array/uniqBy.d.ts.map +1 -0
  123. package/dist/implement/array/uniqBy.mjs +14 -0
  124. package/dist/implement/array/uniqBy.mjs.map +1 -0
  125. package/dist/implement/array/unzip.d.ts +6 -0
  126. package/dist/implement/array/unzip.d.ts.map +1 -0
  127. package/dist/implement/array/unzip.mjs +10 -0
  128. package/dist/implement/array/unzip.mjs.map +1 -0
  129. package/dist/implement/array/zip.d.ts +7 -0
  130. package/dist/implement/array/zip.d.ts.map +1 -0
  131. package/dist/implement/array/zip.mjs +12 -0
  132. package/dist/implement/array/zip.mjs.map +1 -0
  133. package/dist/implement/array/zipIndex.d.ts +6 -0
  134. package/dist/implement/array/zipIndex.d.ts.map +1 -0
  135. package/dist/implement/array/zipIndex.mjs +7 -0
  136. package/dist/implement/array/zipIndex.mjs.map +1 -0
  137. package/dist/implement/array/zipWith.d.ts +8 -0
  138. package/dist/implement/array/zipWith.d.ts.map +1 -0
  139. package/dist/implement/array/zipWith.mjs +12 -0
  140. package/dist/implement/array/zipWith.mjs.map +1 -0
  141. package/dist/implement/async/debounce.d.ts +7 -0
  142. package/dist/implement/async/debounce.d.ts.map +1 -0
  143. package/dist/implement/async/debounce.mjs +16 -0
  144. package/dist/implement/async/debounce.mjs.map +1 -0
  145. package/dist/implement/async/debounceLeading.d.ts +7 -0
  146. package/dist/implement/async/debounceLeading.d.ts.map +1 -0
  147. package/dist/implement/async/debounceLeading.mjs +14 -0
  148. package/dist/implement/async/debounceLeading.mjs.map +1 -0
  149. package/dist/implement/async/debounceLeadingTrailing.d.ts +7 -0
  150. package/dist/implement/async/debounceLeadingTrailing.d.ts.map +1 -0
  151. package/dist/implement/async/debounceLeadingTrailing.mjs +19 -0
  152. package/dist/implement/async/debounceLeadingTrailing.mjs.map +1 -0
  153. package/dist/implement/async/delay.d.ts +4 -0
  154. package/dist/implement/async/delay.d.ts.map +1 -0
  155. package/dist/implement/async/delay.mjs +9 -0
  156. package/dist/implement/async/delay.mjs.map +1 -0
  157. package/dist/implement/async/index.d.ts +10 -0
  158. package/dist/implement/async/index.d.ts.map +1 -0
  159. package/dist/implement/async/pipeAsync.d.ts +14 -0
  160. package/dist/implement/async/pipeAsync.d.ts.map +1 -0
  161. package/dist/implement/async/pipeAsync.mjs +12 -0
  162. package/dist/implement/async/pipeAsync.mjs.map +1 -0
  163. package/dist/implement/async/pipeAsyncSideEffect.d.ts +21 -0
  164. package/dist/implement/async/pipeAsyncSideEffect.d.ts.map +1 -0
  165. package/dist/implement/async/pipeAsyncSideEffect.mjs +16 -0
  166. package/dist/implement/async/pipeAsyncSideEffect.mjs.map +1 -0
  167. package/dist/implement/async/retry.d.ts +7 -0
  168. package/dist/implement/async/retry.d.ts.map +1 -0
  169. package/dist/implement/async/retry.mjs +19 -0
  170. package/dist/implement/async/retry.mjs.map +1 -0
  171. package/dist/implement/async/throttle.d.ts +7 -0
  172. package/dist/implement/async/throttle.d.ts.map +1 -0
  173. package/dist/implement/async/throttle.mjs +18 -0
  174. package/dist/implement/async/throttle.mjs.map +1 -0
  175. package/dist/implement/async/timeout.d.ts +7 -0
  176. package/dist/implement/async/timeout.d.ts.map +1 -0
  177. package/dist/implement/async/timeout.mjs +16 -0
  178. package/dist/implement/async/timeout.mjs.map +1 -0
  179. package/dist/implement/composition/complement.d.ts +7 -0
  180. package/dist/implement/composition/complement.d.ts.map +1 -0
  181. package/dist/implement/composition/complement.mjs +7 -0
  182. package/dist/implement/composition/complement.mjs.map +1 -0
  183. package/dist/implement/composition/compose.d.ts +16 -0
  184. package/dist/implement/composition/compose.d.ts.map +1 -0
  185. package/dist/implement/composition/compose.mjs +7 -0
  186. package/dist/implement/composition/compose.mjs.map +1 -0
  187. package/dist/implement/composition/constant.d.ts +6 -0
  188. package/dist/implement/composition/constant.d.ts.map +1 -0
  189. package/dist/implement/composition/constant.mjs +7 -0
  190. package/dist/implement/composition/constant.mjs.map +1 -0
  191. package/dist/implement/composition/curry.d.ts +32 -0
  192. package/dist/implement/composition/curry.d.ts.map +1 -0
  193. package/dist/implement/composition/curry.mjs +8 -0
  194. package/dist/implement/composition/curry.mjs.map +1 -0
  195. package/dist/implement/composition/flip.d.ts +10 -0
  196. package/dist/implement/composition/flip.d.ts.map +1 -0
  197. package/dist/implement/composition/flip.mjs +10 -0
  198. package/dist/implement/composition/flip.mjs.map +1 -0
  199. package/dist/implement/composition/identity.d.ts +6 -0
  200. package/dist/implement/composition/identity.d.ts.map +1 -0
  201. package/dist/implement/composition/identity.mjs +7 -0
  202. package/dist/implement/composition/identity.mjs.map +1 -0
  203. package/dist/implement/composition/index.d.ts +16 -0
  204. package/dist/implement/composition/index.d.ts.map +1 -0
  205. package/dist/implement/composition/memoize.d.ts +7 -0
  206. package/dist/implement/composition/memoize.d.ts.map +1 -0
  207. package/dist/implement/composition/memoize.mjs +16 -0
  208. package/dist/implement/composition/memoize.mjs.map +1 -0
  209. package/dist/implement/composition/once.d.ts +7 -0
  210. package/dist/implement/composition/once.d.ts.map +1 -0
  211. package/dist/implement/composition/once.mjs +10 -0
  212. package/dist/implement/composition/once.mjs.map +1 -0
  213. package/dist/implement/composition/partial.d.ts +6 -0
  214. package/dist/implement/composition/partial.d.ts.map +1 -0
  215. package/dist/implement/composition/partial.mjs +10 -0
  216. package/dist/implement/composition/partial.mjs.map +1 -0
  217. package/dist/implement/composition/pipe.d.ts +13 -0
  218. package/dist/implement/composition/pipe.d.ts.map +1 -0
  219. package/dist/implement/composition/pipe.mjs +7 -0
  220. package/dist/implement/composition/pipe.mjs.map +1 -0
  221. package/dist/implement/composition/pipe.type-test.d.ts +20 -0
  222. package/dist/implement/composition/pipe.type-test.d.ts.map +1 -0
  223. package/dist/implement/composition/pipeSideEffect.d.ts +17 -0
  224. package/dist/implement/composition/pipeSideEffect.d.ts.map +1 -0
  225. package/dist/implement/composition/pipeSideEffect.mjs +16 -0
  226. package/dist/implement/composition/pipeSideEffect.mjs.map +1 -0
  227. package/dist/implement/composition/sideEffect.d.ts +18 -0
  228. package/dist/implement/composition/sideEffect.d.ts.map +1 -0
  229. package/dist/implement/composition/sideEffect.mjs +27 -0
  230. package/dist/implement/composition/sideEffect.mjs.map +1 -0
  231. package/dist/implement/composition/tap.d.ts +6 -0
  232. package/dist/implement/composition/tap.d.ts.map +1 -0
  233. package/dist/implement/composition/tap.mjs +7 -0
  234. package/dist/implement/composition/tap.mjs.map +1 -0
  235. package/dist/implement/control/cond.d.ts +6 -0
  236. package/dist/implement/control/cond.d.ts.map +1 -0
  237. package/dist/implement/control/cond.mjs +11 -0
  238. package/dist/implement/control/cond.mjs.map +1 -0
  239. package/dist/implement/control/guard.d.ts +8 -0
  240. package/dist/implement/control/guard.d.ts.map +1 -0
  241. package/dist/implement/control/guard.mjs +9 -0
  242. package/dist/implement/control/guard.mjs.map +1 -0
  243. package/dist/implement/control/ifElse.d.ts +18 -0
  244. package/dist/implement/control/ifElse.d.ts.map +1 -0
  245. package/dist/implement/control/ifElse.mjs +9 -0
  246. package/dist/implement/control/ifElse.mjs.map +1 -0
  247. package/dist/implement/control/index.d.ts +7 -0
  248. package/dist/implement/control/index.d.ts.map +1 -0
  249. package/dist/implement/control/tryCatch.d.ts +12 -0
  250. package/dist/implement/control/tryCatch.d.ts.map +1 -0
  251. package/dist/implement/control/tryCatch.mjs +14 -0
  252. package/dist/implement/control/tryCatch.mjs.map +1 -0
  253. package/dist/implement/control/unless.d.ts +9 -0
  254. package/dist/implement/control/unless.d.ts.map +1 -0
  255. package/dist/implement/control/unless.mjs +9 -0
  256. package/dist/implement/control/unless.mjs.map +1 -0
  257. package/dist/implement/control/when.d.ts +9 -0
  258. package/dist/implement/control/when.d.ts.map +1 -0
  259. package/dist/implement/control/when.mjs +9 -0
  260. package/dist/implement/control/when.mjs.map +1 -0
  261. package/dist/implement/debug/assert.d.ts +6 -0
  262. package/dist/implement/debug/assert.d.ts.map +1 -0
  263. package/dist/implement/debug/assert.mjs +10 -0
  264. package/dist/implement/debug/assert.mjs.map +1 -0
  265. package/dist/implement/debug/index.d.ts +4 -0
  266. package/dist/implement/debug/index.d.ts.map +1 -0
  267. package/dist/implement/debug/invariant.d.ts +6 -0
  268. package/dist/implement/debug/invariant.d.ts.map +1 -0
  269. package/dist/implement/debug/invariant.mjs +10 -0
  270. package/dist/implement/debug/invariant.mjs.map +1 -0
  271. package/dist/implement/debug/log.d.ts +4 -0
  272. package/dist/implement/debug/log.d.ts.map +1 -0
  273. package/dist/implement/debug/log.mjs +7 -0
  274. package/dist/implement/debug/log.mjs.map +1 -0
  275. package/dist/implement/equality/clamp.d.ts +7 -0
  276. package/dist/implement/equality/clamp.d.ts.map +1 -0
  277. package/dist/implement/equality/clamp.mjs +9 -0
  278. package/dist/implement/equality/clamp.mjs.map +1 -0
  279. package/dist/implement/equality/equals.d.ts +6 -0
  280. package/dist/implement/equality/equals.d.ts.map +1 -0
  281. package/dist/implement/equality/equals.mjs +59 -0
  282. package/dist/implement/equality/equals.mjs.map +1 -0
  283. package/dist/implement/equality/gt.d.ts +4 -0
  284. package/dist/implement/equality/gt.d.ts.map +1 -0
  285. package/dist/implement/equality/gt.mjs +7 -0
  286. package/dist/implement/equality/gt.mjs.map +1 -0
  287. package/dist/implement/equality/gte.d.ts +4 -0
  288. package/dist/implement/equality/gte.d.ts.map +1 -0
  289. package/dist/implement/equality/gte.mjs +7 -0
  290. package/dist/implement/equality/gte.mjs.map +1 -0
  291. package/dist/implement/equality/includes.d.ts +4 -0
  292. package/dist/implement/equality/includes.d.ts.map +1 -0
  293. package/dist/implement/equality/includes.mjs +15 -0
  294. package/dist/implement/equality/includes.mjs.map +1 -0
  295. package/dist/implement/equality/index.d.ts +11 -0
  296. package/dist/implement/equality/index.d.ts.map +1 -0
  297. package/dist/implement/equality/isEmpty.d.ts +4 -0
  298. package/dist/implement/equality/isEmpty.d.ts.map +1 -0
  299. package/dist/implement/equality/isEmpty.mjs +7 -0
  300. package/dist/implement/equality/isEmpty.mjs.map +1 -0
  301. package/dist/implement/equality/isNil.d.ts +4 -0
  302. package/dist/implement/equality/isNil.d.ts.map +1 -0
  303. package/dist/implement/equality/isNil.mjs +7 -0
  304. package/dist/implement/equality/isNil.mjs.map +1 -0
  305. package/dist/implement/equality/isType.d.ts +4 -0
  306. package/dist/implement/equality/isType.d.ts.map +1 -0
  307. package/dist/implement/equality/isType.mjs +13 -0
  308. package/dist/implement/equality/isType.mjs.map +1 -0
  309. package/dist/implement/equality/lt.d.ts +4 -0
  310. package/dist/implement/equality/lt.d.ts.map +1 -0
  311. package/dist/implement/equality/lt.mjs +7 -0
  312. package/dist/implement/equality/lt.mjs.map +1 -0
  313. package/dist/implement/equality/lte.d.ts +4 -0
  314. package/dist/implement/equality/lte.d.ts.map +1 -0
  315. package/dist/implement/equality/lte.mjs +7 -0
  316. package/dist/implement/equality/lte.mjs.map +1 -0
  317. package/dist/implement/math/add.d.ts +6 -0
  318. package/dist/implement/math/add.d.ts.map +1 -0
  319. package/dist/implement/math/add.mjs +9 -0
  320. package/dist/implement/math/add.mjs.map +1 -0
  321. package/dist/implement/math/ceil.d.ts +4 -0
  322. package/dist/implement/math/ceil.d.ts.map +1 -0
  323. package/dist/implement/math/ceil.mjs +7 -0
  324. package/dist/implement/math/ceil.mjs.map +1 -0
  325. package/dist/implement/math/div.d.ts +6 -0
  326. package/dist/implement/math/div.d.ts.map +1 -0
  327. package/dist/implement/math/div.mjs +9 -0
  328. package/dist/implement/math/div.mjs.map +1 -0
  329. package/dist/implement/math/floor.d.ts +4 -0
  330. package/dist/implement/math/floor.d.ts.map +1 -0
  331. package/dist/implement/math/floor.mjs +7 -0
  332. package/dist/implement/math/floor.mjs.map +1 -0
  333. package/dist/implement/math/index.d.ts +13 -0
  334. package/dist/implement/math/index.d.ts.map +1 -0
  335. package/dist/implement/math/max.d.ts +4 -0
  336. package/dist/implement/math/max.d.ts.map +1 -0
  337. package/dist/implement/math/max.mjs +7 -0
  338. package/dist/implement/math/max.mjs.map +1 -0
  339. package/dist/implement/math/mean.d.ts +4 -0
  340. package/dist/implement/math/mean.d.ts.map +1 -0
  341. package/dist/implement/math/mean.mjs +7 -0
  342. package/dist/implement/math/mean.mjs.map +1 -0
  343. package/dist/implement/math/min.d.ts +4 -0
  344. package/dist/implement/math/min.d.ts.map +1 -0
  345. package/dist/implement/math/min.mjs +7 -0
  346. package/dist/implement/math/min.mjs.map +1 -0
  347. package/dist/implement/math/mul.d.ts +6 -0
  348. package/dist/implement/math/mul.d.ts.map +1 -0
  349. package/dist/implement/math/mul.mjs +9 -0
  350. package/dist/implement/math/mul.mjs.map +1 -0
  351. package/dist/implement/math/randomInt.d.ts +6 -0
  352. package/dist/implement/math/randomInt.d.ts.map +1 -0
  353. package/dist/implement/math/randomInt.mjs +10 -0
  354. package/dist/implement/math/randomInt.mjs.map +1 -0
  355. package/dist/implement/math/round.d.ts +4 -0
  356. package/dist/implement/math/round.d.ts.map +1 -0
  357. package/dist/implement/math/round.mjs +7 -0
  358. package/dist/implement/math/round.mjs.map +1 -0
  359. package/dist/implement/math/sub.d.ts +6 -0
  360. package/dist/implement/math/sub.d.ts.map +1 -0
  361. package/dist/implement/math/sub.mjs +9 -0
  362. package/dist/implement/math/sub.mjs.map +1 -0
  363. package/dist/implement/math/sum.d.ts +4 -0
  364. package/dist/implement/math/sum.d.ts.map +1 -0
  365. package/dist/implement/math/sum.mjs +7 -0
  366. package/dist/implement/math/sum.mjs.map +1 -0
  367. package/dist/implement/nullable/fold.d.ts +8 -0
  368. package/dist/implement/nullable/fold.d.ts.map +1 -0
  369. package/dist/implement/nullable/fold.mjs +9 -0
  370. package/dist/implement/nullable/fold.mjs.map +1 -0
  371. package/dist/implement/nullable/getOrElse.d.ts +4 -0
  372. package/dist/implement/nullable/getOrElse.d.ts.map +1 -0
  373. package/dist/implement/nullable/getOrElse.mjs +7 -0
  374. package/dist/implement/nullable/getOrElse.mjs.map +1 -0
  375. package/dist/implement/nullable/index.d.ts +6 -0
  376. package/dist/implement/nullable/index.d.ts.map +1 -0
  377. package/dist/implement/nullable/mapMaybe.d.ts +4 -0
  378. package/dist/implement/nullable/mapMaybe.d.ts.map +1 -0
  379. package/dist/implement/nullable/mapMaybe.mjs +14 -0
  380. package/dist/implement/nullable/mapMaybe.mjs.map +1 -0
  381. package/dist/implement/nullable/maybe.d.ts +4 -0
  382. package/dist/implement/nullable/maybe.d.ts.map +1 -0
  383. package/dist/implement/nullable/maybe.mjs +7 -0
  384. package/dist/implement/nullable/maybe.mjs.map +1 -0
  385. package/dist/implement/nullable/result.d.ts +10 -0
  386. package/dist/implement/nullable/result.d.ts.map +1 -0
  387. package/dist/implement/nullable/result.mjs +11 -0
  388. package/dist/implement/nullable/result.mjs.map +1 -0
  389. package/dist/implement/object/assoc.d.ts +11 -0
  390. package/dist/implement/object/assoc.d.ts.map +1 -0
  391. package/dist/implement/object/assoc.mjs +16 -0
  392. package/dist/implement/object/assoc.mjs.map +1 -0
  393. package/dist/implement/object/assoc.type-test.d.ts +30 -0
  394. package/dist/implement/object/assoc.type-test.d.ts.map +1 -0
  395. package/dist/implement/object/assocPath.d.ts +9 -0
  396. package/dist/implement/object/assocPath.d.ts.map +1 -0
  397. package/dist/implement/object/assocPath.mjs +21 -0
  398. package/dist/implement/object/assocPath.mjs.map +1 -0
  399. package/dist/implement/object/dissoc.d.ts +7 -0
  400. package/dist/implement/object/dissoc.d.ts.map +1 -0
  401. package/dist/implement/object/dissoc.mjs +15 -0
  402. package/dist/implement/object/dissoc.mjs.map +1 -0
  403. package/dist/implement/object/dissocPath.d.ts +8 -0
  404. package/dist/implement/object/dissocPath.d.ts.map +1 -0
  405. package/dist/implement/object/dissocPath.mjs +33 -0
  406. package/dist/implement/object/dissocPath.mjs.map +1 -0
  407. package/dist/implement/object/entries.d.ts +6 -0
  408. package/dist/implement/object/entries.d.ts.map +1 -0
  409. package/dist/implement/object/entries.mjs +7 -0
  410. package/dist/implement/object/entries.mjs.map +1 -0
  411. package/dist/implement/object/evolve.d.ts +9 -0
  412. package/dist/implement/object/evolve.d.ts.map +1 -0
  413. package/dist/implement/object/evolve.mjs +14 -0
  414. package/dist/implement/object/evolve.mjs.map +1 -0
  415. package/dist/implement/object/has.d.ts +7 -0
  416. package/dist/implement/object/has.d.ts.map +1 -0
  417. package/dist/implement/object/has.mjs +9 -0
  418. package/dist/implement/object/has.mjs.map +1 -0
  419. package/dist/implement/object/hasPath.d.ts +6 -0
  420. package/dist/implement/object/hasPath.d.ts.map +1 -0
  421. package/dist/implement/object/hasPath.mjs +15 -0
  422. package/dist/implement/object/hasPath.mjs.map +1 -0
  423. package/dist/implement/object/index.d.ts +21 -0
  424. package/dist/implement/object/index.d.ts.map +1 -0
  425. package/dist/implement/object/keys.d.ts +6 -0
  426. package/dist/implement/object/keys.d.ts.map +1 -0
  427. package/dist/implement/object/keys.mjs +7 -0
  428. package/dist/implement/object/keys.mjs.map +1 -0
  429. package/dist/implement/object/mapValues.d.ts +6 -0
  430. package/dist/implement/object/mapValues.d.ts.map +1 -0
  431. package/dist/implement/object/mapValues.mjs +12 -0
  432. package/dist/implement/object/mapValues.mjs.map +1 -0
  433. package/dist/implement/object/merge.d.ts +7 -0
  434. package/dist/implement/object/merge.d.ts.map +1 -0
  435. package/dist/implement/object/merge.mjs +12 -0
  436. package/dist/implement/object/merge.mjs.map +1 -0
  437. package/dist/implement/object/mergeAll.d.ts +6 -0
  438. package/dist/implement/object/mergeAll.d.ts.map +1 -0
  439. package/dist/implement/object/mergeAll.mjs +10 -0
  440. package/dist/implement/object/mergeAll.mjs.map +1 -0
  441. package/dist/implement/object/mergeDeep.d.ts +7 -0
  442. package/dist/implement/object/mergeDeep.d.ts.map +1 -0
  443. package/dist/implement/object/mergeDeep.mjs +20 -0
  444. package/dist/implement/object/mergeDeep.mjs.map +1 -0
  445. package/dist/implement/object/omit.d.ts +7 -0
  446. package/dist/implement/object/omit.d.ts.map +1 -0
  447. package/dist/implement/object/omit.mjs +12 -0
  448. package/dist/implement/object/omit.mjs.map +1 -0
  449. package/dist/implement/object/path.d.ts +7 -0
  450. package/dist/implement/object/path.d.ts.map +1 -0
  451. package/dist/implement/object/path.mjs +9 -0
  452. package/dist/implement/object/path.mjs.map +1 -0
  453. package/dist/implement/object/pathOr.d.ts +8 -0
  454. package/dist/implement/object/pathOr.d.ts.map +1 -0
  455. package/dist/implement/object/pathOr.mjs +10 -0
  456. package/dist/implement/object/pathOr.mjs.map +1 -0
  457. package/dist/implement/object/pick.d.ts +7 -0
  458. package/dist/implement/object/pick.d.ts.map +1 -0
  459. package/dist/implement/object/pick.mjs +12 -0
  460. package/dist/implement/object/pick.mjs.map +1 -0
  461. package/dist/implement/object/prop.d.ts +7 -0
  462. package/dist/implement/object/prop.d.ts.map +1 -0
  463. package/dist/implement/object/prop.mjs +9 -0
  464. package/dist/implement/object/prop.mjs.map +1 -0
  465. package/dist/implement/object/propOr.d.ts +8 -0
  466. package/dist/implement/object/propOr.d.ts.map +1 -0
  467. package/dist/implement/object/propOr.mjs +10 -0
  468. package/dist/implement/object/propOr.mjs.map +1 -0
  469. package/dist/implement/object/values.d.ts +6 -0
  470. package/dist/implement/object/values.d.ts.map +1 -0
  471. package/dist/implement/object/values.mjs +7 -0
  472. package/dist/implement/object/values.mjs.map +1 -0
  473. package/dist/implement/string/endsWith.d.ts +5 -0
  474. package/dist/implement/string/endsWith.d.ts.map +1 -0
  475. package/dist/implement/string/endsWith.mjs +18 -0
  476. package/dist/implement/string/endsWith.mjs.map +1 -0
  477. package/dist/implement/string/index.d.ts +10 -0
  478. package/dist/implement/string/index.d.ts.map +1 -0
  479. package/dist/implement/string/join.d.ts +6 -0
  480. package/dist/implement/string/join.d.ts.map +1 -0
  481. package/dist/implement/string/join.mjs +9 -0
  482. package/dist/implement/string/join.mjs.map +1 -0
  483. package/dist/implement/string/match.d.ts +6 -0
  484. package/dist/implement/string/match.d.ts.map +1 -0
  485. package/dist/implement/string/match.mjs +9 -0
  486. package/dist/implement/string/match.mjs.map +1 -0
  487. package/dist/implement/string/replace.d.ts +7 -0
  488. package/dist/implement/string/replace.d.ts.map +1 -0
  489. package/dist/implement/string/replace.mjs +9 -0
  490. package/dist/implement/string/replace.mjs.map +1 -0
  491. package/dist/implement/string/split.d.ts +6 -0
  492. package/dist/implement/string/split.d.ts.map +1 -0
  493. package/dist/implement/string/split.mjs +9 -0
  494. package/dist/implement/string/split.mjs.map +1 -0
  495. package/dist/implement/string/startsWith.d.ts +5 -0
  496. package/dist/implement/string/startsWith.d.ts.map +1 -0
  497. package/dist/implement/string/startsWith.mjs +16 -0
  498. package/dist/implement/string/startsWith.mjs.map +1 -0
  499. package/dist/implement/string/toLower.d.ts +4 -0
  500. package/dist/implement/string/toLower.d.ts.map +1 -0
  501. package/dist/implement/string/toLower.mjs +7 -0
  502. package/dist/implement/string/toLower.mjs.map +1 -0
  503. package/dist/implement/string/toUpper.d.ts +4 -0
  504. package/dist/implement/string/toUpper.d.ts.map +1 -0
  505. package/dist/implement/string/toUpper.mjs +7 -0
  506. package/dist/implement/string/toUpper.mjs.map +1 -0
  507. package/dist/implement/string/trim.d.ts +4 -0
  508. package/dist/implement/string/trim.d.ts.map +1 -0
  509. package/dist/implement/string/trim.mjs +7 -0
  510. package/dist/implement/string/trim.mjs.map +1 -0
  511. package/dist/index.d.ts +11 -0
  512. package/dist/index.d.ts.map +1 -0
  513. package/dist/index.mjs +247 -0
  514. package/dist/index.mjs.map +1 -0
  515. package/dist/skills/fp-pack.md +1644 -0
  516. package/dist/stream/append.d.ts +8 -0
  517. package/dist/stream/append.d.ts.map +1 -0
  518. package/dist/stream/append.mjs +23 -0
  519. package/dist/stream/append.mjs.map +1 -0
  520. package/dist/stream/chunk.d.ts +7 -0
  521. package/dist/stream/chunk.d.ts.map +1 -0
  522. package/dist/stream/chunk.mjs +37 -0
  523. package/dist/stream/chunk.mjs.map +1 -0
  524. package/dist/stream/concat.d.ts +8 -0
  525. package/dist/stream/concat.d.ts.map +1 -0
  526. package/dist/stream/concat.mjs +25 -0
  527. package/dist/stream/concat.mjs.map +1 -0
  528. package/dist/stream/drop.d.ts +8 -0
  529. package/dist/stream/drop.d.ts.map +1 -0
  530. package/dist/stream/drop.mjs +42 -0
  531. package/dist/stream/drop.mjs.map +1 -0
  532. package/dist/stream/dropWhile.d.ts +8 -0
  533. package/dist/stream/dropWhile.d.ts.map +1 -0
  534. package/dist/stream/dropWhile.mjs +23 -0
  535. package/dist/stream/dropWhile.mjs.map +1 -0
  536. package/dist/stream/every.d.ts +8 -0
  537. package/dist/stream/every.d.ts.map +1 -0
  538. package/dist/stream/every.mjs +24 -0
  539. package/dist/stream/every.mjs.map +1 -0
  540. package/dist/stream/filter.d.ts +8 -0
  541. package/dist/stream/filter.d.ts.map +1 -0
  542. package/dist/stream/filter.mjs +21 -0
  543. package/dist/stream/filter.mjs.map +1 -0
  544. package/dist/stream/find.d.ts +8 -0
  545. package/dist/stream/find.d.ts.map +1 -0
  546. package/dist/stream/find.mjs +23 -0
  547. package/dist/stream/find.mjs.map +1 -0
  548. package/dist/stream/flatMap.d.ts +8 -0
  549. package/dist/stream/flatMap.d.ts.map +1 -0
  550. package/dist/stream/flatMap.mjs +27 -0
  551. package/dist/stream/flatMap.mjs.map +1 -0
  552. package/dist/stream/flatten.d.ts +6 -0
  553. package/dist/stream/flatten.d.ts.map +1 -0
  554. package/dist/stream/flatten.mjs +23 -0
  555. package/dist/stream/flatten.mjs.map +1 -0
  556. package/dist/stream/flattenDeep.d.ts +6 -0
  557. package/dist/stream/flattenDeep.d.ts.map +1 -0
  558. package/dist/stream/flattenDeep.mjs +30 -0
  559. package/dist/stream/flattenDeep.mjs.map +1 -0
  560. package/dist/stream/index.d.ts +24 -0
  561. package/dist/stream/index.d.ts.map +1 -0
  562. package/dist/stream/index.mjs +49 -0
  563. package/dist/stream/index.mjs.map +1 -0
  564. package/dist/stream/map.d.ts +8 -0
  565. package/dist/stream/map.d.ts.map +1 -0
  566. package/dist/stream/map.mjs +21 -0
  567. package/dist/stream/map.mjs.map +1 -0
  568. package/dist/stream/prepend.d.ts +8 -0
  569. package/dist/stream/prepend.d.ts.map +1 -0
  570. package/dist/stream/prepend.mjs +23 -0
  571. package/dist/stream/prepend.mjs.map +1 -0
  572. package/dist/stream/range.d.ts +4 -0
  573. package/dist/stream/range.d.ts.map +1 -0
  574. package/dist/stream/range.mjs +17 -0
  575. package/dist/stream/range.mjs.map +1 -0
  576. package/dist/stream/reduce.d.ts +8 -0
  577. package/dist/stream/reduce.d.ts.map +1 -0
  578. package/dist/stream/reduce.mjs +24 -0
  579. package/dist/stream/reduce.mjs.map +1 -0
  580. package/dist/stream/scan.d.ts +8 -0
  581. package/dist/stream/scan.d.ts.map +1 -0
  582. package/dist/stream/scan.mjs +23 -0
  583. package/dist/stream/scan.mjs.map +1 -0
  584. package/dist/stream/some.d.ts +8 -0
  585. package/dist/stream/some.d.ts.map +1 -0
  586. package/dist/stream/some.mjs +24 -0
  587. package/dist/stream/some.mjs.map +1 -0
  588. package/dist/stream/take.d.ts +8 -0
  589. package/dist/stream/take.d.ts.map +1 -0
  590. package/dist/stream/take.mjs +30 -0
  591. package/dist/stream/take.mjs.map +1 -0
  592. package/dist/stream/takeWhile.d.ts +8 -0
  593. package/dist/stream/takeWhile.d.ts.map +1 -0
  594. package/dist/stream/takeWhile.mjs +27 -0
  595. package/dist/stream/takeWhile.mjs.map +1 -0
  596. package/dist/stream/toArray.d.ts +5 -0
  597. package/dist/stream/toArray.d.ts.map +1 -0
  598. package/dist/stream/toArray.mjs +19 -0
  599. package/dist/stream/toArray.mjs.map +1 -0
  600. package/dist/stream/toAsync.d.ts +6 -0
  601. package/dist/stream/toAsync.d.ts.map +1 -0
  602. package/dist/stream/toAsync.mjs +21 -0
  603. package/dist/stream/toAsync.mjs.map +1 -0
  604. package/dist/stream/utils.d.ts +12 -0
  605. package/dist/stream/utils.d.ts.map +1 -0
  606. package/dist/stream/utils.mjs +10 -0
  607. package/dist/stream/utils.mjs.map +1 -0
  608. package/dist/stream/zip.d.ts +8 -0
  609. package/dist/stream/zip.d.ts.map +1 -0
  610. package/dist/stream/zip.mjs +31 -0
  611. package/dist/stream/zip.mjs.map +1 -0
  612. package/dist/stream/zipWith.d.ts +8 -0
  613. package/dist/stream/zipWith.d.ts.map +1 -0
  614. package/dist/stream/zipWith.mjs +31 -0
  615. package/dist/stream/zipWith.mjs.map +1 -0
  616. package/package.json +87 -0
  617. package/src/implement/array/append.test.ts +13 -0
  618. package/src/implement/array/append.ts +16 -0
  619. package/src/implement/array/chunk.test.ts +23 -0
  620. package/src/implement/array/chunk.ts +25 -0
  621. package/src/implement/array/concat.test.ts +13 -0
  622. package/src/implement/array/concat.ts +16 -0
  623. package/src/implement/array/curried.test.ts +91 -0
  624. package/src/implement/array/drop.test.ts +22 -0
  625. package/src/implement/array/drop.ts +20 -0
  626. package/src/implement/array/dropWhile.test.ts +13 -0
  627. package/src/implement/array/dropWhile.ts +26 -0
  628. package/src/implement/array/every.test.ts +16 -0
  629. package/src/implement/array/every.ts +16 -0
  630. package/src/implement/array/filter.test.ts +16 -0
  631. package/src/implement/array/filter.ts +16 -0
  632. package/src/implement/array/find.test.ts +16 -0
  633. package/src/implement/array/find.ts +16 -0
  634. package/src/implement/array/flatMap.test.ts +13 -0
  635. package/src/implement/array/flatMap.ts +16 -0
  636. package/src/implement/array/flatten.test.ts +8 -0
  637. package/src/implement/array/flatten.ts +8 -0
  638. package/src/implement/array/flattenDeep.test.ts +13 -0
  639. package/src/implement/array/flattenDeep.ts +21 -0
  640. package/src/implement/array/groupBy.test.ts +13 -0
  641. package/src/implement/array/groupBy.ts +23 -0
  642. package/src/implement/array/head.test.ts +12 -0
  643. package/src/implement/array/head.ts +8 -0
  644. package/src/implement/array/index.ts +33 -0
  645. package/src/implement/array/init.test.ts +13 -0
  646. package/src/implement/array/init.ts +9 -0
  647. package/src/implement/array/last.test.ts +12 -0
  648. package/src/implement/array/last.ts +9 -0
  649. package/src/implement/array/map.test.ts +12 -0
  650. package/src/implement/array/map.ts +16 -0
  651. package/src/implement/array/partition.test.ts +14 -0
  652. package/src/implement/array/partition.ts +27 -0
  653. package/src/implement/array/prepend.test.ts +13 -0
  654. package/src/implement/array/prepend.ts +16 -0
  655. package/src/implement/array/range.test.ts +16 -0
  656. package/src/implement/array/range.ts +18 -0
  657. package/src/implement/array/reduce.test.ts +25 -0
  658. package/src/implement/array/reduce.ts +21 -0
  659. package/src/implement/array/scan.test.ts +13 -0
  660. package/src/implement/array/scan.ts +23 -0
  661. package/src/implement/array/some.test.ts +25 -0
  662. package/src/implement/array/some.ts +19 -0
  663. package/src/implement/array/sort.test.ts +25 -0
  664. package/src/implement/array/sort.ts +16 -0
  665. package/src/implement/array/sortBy.test.ts +28 -0
  666. package/src/implement/array/sortBy.ts +22 -0
  667. package/src/implement/array/tail.test.ts +13 -0
  668. package/src/implement/array/tail.ts +8 -0
  669. package/src/implement/array/take.test.ts +20 -0
  670. package/src/implement/array/take.ts +18 -0
  671. package/src/implement/array/takeWhile.test.ts +13 -0
  672. package/src/implement/array/takeWhile.ts +23 -0
  673. package/src/implement/array/uniq.test.ts +18 -0
  674. package/src/implement/array/uniq.ts +16 -0
  675. package/src/implement/array/uniqBy.test.ts +22 -0
  676. package/src/implement/array/uniqBy.ts +25 -0
  677. package/src/implement/array/unzip.test.ts +22 -0
  678. package/src/implement/array/unzip.ts +16 -0
  679. package/src/implement/array/zip.test.ts +16 -0
  680. package/src/implement/array/zip.ts +23 -0
  681. package/src/implement/array/zipIndex.test.ts +16 -0
  682. package/src/implement/array/zipIndex.ts +8 -0
  683. package/src/implement/array/zipWith.test.ts +13 -0
  684. package/src/implement/array/zipWith.ts +22 -0
  685. package/src/implement/async/curried.test.ts +133 -0
  686. package/src/implement/async/debounce.test.ts +21 -0
  687. package/src/implement/async/debounce.ts +26 -0
  688. package/src/implement/async/debounceLeading.test.ts +24 -0
  689. package/src/implement/async/debounceLeading.ts +25 -0
  690. package/src/implement/async/debounceLeadingTrailing.test.ts +37 -0
  691. package/src/implement/async/debounceLeadingTrailing.ts +41 -0
  692. package/src/implement/async/delay.test.ts +22 -0
  693. package/src/implement/async/delay.ts +7 -0
  694. package/src/implement/async/index.ts +9 -0
  695. package/src/implement/async/pipeAsync.test.ts +23 -0
  696. package/src/implement/async/pipeAsync.ts +48 -0
  697. package/src/implement/async/pipeAsyncSideEffect.test.ts +23 -0
  698. package/src/implement/async/pipeAsyncSideEffect.ts +68 -0
  699. package/src/implement/async/retry.test.ts +49 -0
  700. package/src/implement/async/retry.ts +27 -0
  701. package/src/implement/async/throttle.test.ts +38 -0
  702. package/src/implement/async/throttle.ts +47 -0
  703. package/src/implement/async/timeout.test.ts +20 -0
  704. package/src/implement/async/timeout.ts +24 -0
  705. package/src/implement/composition/complement.test.ts +17 -0
  706. package/src/implement/composition/complement.ts +13 -0
  707. package/src/implement/composition/compose.test.ts +30 -0
  708. package/src/implement/composition/compose.ts +48 -0
  709. package/src/implement/composition/constant.test.ts +16 -0
  710. package/src/implement/composition/constant.ts +8 -0
  711. package/src/implement/composition/curry.test.ts +31 -0
  712. package/src/implement/composition/curry.ts +71 -0
  713. package/src/implement/composition/flip.test.ts +41 -0
  714. package/src/implement/composition/flip.ts +16 -0
  715. package/src/implement/composition/identity.test.ts +14 -0
  716. package/src/implement/composition/identity.ts +8 -0
  717. package/src/implement/composition/index.ts +15 -0
  718. package/src/implement/composition/memoize.test.ts +46 -0
  719. package/src/implement/composition/memoize.ts +31 -0
  720. package/src/implement/composition/once.test.ts +30 -0
  721. package/src/implement/composition/once.ts +21 -0
  722. package/src/implement/composition/partial.test.ts +25 -0
  723. package/src/implement/composition/partial.ts +14 -0
  724. package/src/implement/composition/pipe.test.ts +31 -0
  725. package/src/implement/composition/pipe.ts +39 -0
  726. package/src/implement/composition/pipe.type-test.ts +54 -0
  727. package/src/implement/composition/pipeSideEffect.test.ts +23 -0
  728. package/src/implement/composition/pipeSideEffect.ts +60 -0
  729. package/src/implement/composition/sideEffect.test.ts +43 -0
  730. package/src/implement/composition/sideEffect.ts +44 -0
  731. package/src/implement/composition/tap.test.ts +17 -0
  732. package/src/implement/composition/tap.ts +11 -0
  733. package/src/implement/control/cond.test.ts +33 -0
  734. package/src/implement/control/cond.ts +17 -0
  735. package/src/implement/control/curried.test.ts +71 -0
  736. package/src/implement/control/guard.test.ts +17 -0
  737. package/src/implement/control/guard.ts +27 -0
  738. package/src/implement/control/ifElse.test.ts +68 -0
  739. package/src/implement/control/ifElse.ts +40 -0
  740. package/src/implement/control/index.ts +6 -0
  741. package/src/implement/control/tryCatch.test.ts +35 -0
  742. package/src/implement/control/tryCatch.ts +36 -0
  743. package/src/implement/control/unless.test.ts +22 -0
  744. package/src/implement/control/unless.ts +29 -0
  745. package/src/implement/control/when.test.ts +22 -0
  746. package/src/implement/control/when.ts +29 -0
  747. package/src/implement/debug/assert.test.ts +16 -0
  748. package/src/implement/debug/assert.ts +9 -0
  749. package/src/implement/debug/curried.test.ts +17 -0
  750. package/src/implement/debug/index.ts +3 -0
  751. package/src/implement/debug/invariant.test.ts +16 -0
  752. package/src/implement/debug/invariant.ts +9 -0
  753. package/src/implement/debug/log.test.ts +20 -0
  754. package/src/implement/debug/log.ts +12 -0
  755. package/src/implement/equality/clamp.test.ts +16 -0
  756. package/src/implement/equality/clamp.ts +9 -0
  757. package/src/implement/equality/curried.test.ts +13 -0
  758. package/src/implement/equality/equals.test.ts +63 -0
  759. package/src/implement/equality/equals.ts +87 -0
  760. package/src/implement/equality/gt.test.ts +13 -0
  761. package/src/implement/equality/gt.ts +5 -0
  762. package/src/implement/equality/gte.test.ts +13 -0
  763. package/src/implement/equality/gte.ts +5 -0
  764. package/src/implement/equality/includes.test.ts +16 -0
  765. package/src/implement/equality/includes.ts +21 -0
  766. package/src/implement/equality/index.ts +10 -0
  767. package/src/implement/equality/isEmpty.test.ts +24 -0
  768. package/src/implement/equality/isEmpty.ts +19 -0
  769. package/src/implement/equality/isNil.test.ts +16 -0
  770. package/src/implement/equality/isNil.ts +5 -0
  771. package/src/implement/equality/isType.test.ts +28 -0
  772. package/src/implement/equality/isType.ts +13 -0
  773. package/src/implement/equality/lt.test.ts +13 -0
  774. package/src/implement/equality/lt.ts +5 -0
  775. package/src/implement/equality/lte.test.ts +13 -0
  776. package/src/implement/equality/lte.ts +5 -0
  777. package/src/implement/math/add.test.ts +9 -0
  778. package/src/implement/math/add.ts +7 -0
  779. package/src/implement/math/ceil.test.ts +10 -0
  780. package/src/implement/math/ceil.ts +5 -0
  781. package/src/implement/math/curried.test.ts +30 -0
  782. package/src/implement/math/div.test.ts +9 -0
  783. package/src/implement/math/div.ts +7 -0
  784. package/src/implement/math/floor.test.ts +10 -0
  785. package/src/implement/math/floor.ts +5 -0
  786. package/src/implement/math/index.ts +12 -0
  787. package/src/implement/math/max.test.ts +13 -0
  788. package/src/implement/math/max.ts +6 -0
  789. package/src/implement/math/mean.test.ts +12 -0
  790. package/src/implement/math/mean.ts +7 -0
  791. package/src/implement/math/min.test.ts +12 -0
  792. package/src/implement/math/min.ts +6 -0
  793. package/src/implement/math/mul.test.ts +9 -0
  794. package/src/implement/math/mul.ts +7 -0
  795. package/src/implement/math/randomInt.test.ts +22 -0
  796. package/src/implement/math/randomInt.ts +14 -0
  797. package/src/implement/math/round.test.ts +10 -0
  798. package/src/implement/math/round.ts +5 -0
  799. package/src/implement/math/sub.test.ts +9 -0
  800. package/src/implement/math/sub.ts +7 -0
  801. package/src/implement/math/sum.test.ts +10 -0
  802. package/src/implement/math/sum.ts +5 -0
  803. package/src/implement/nullable/curried.test.ts +13 -0
  804. package/src/implement/nullable/fold.test.ts +31 -0
  805. package/src/implement/nullable/fold.ts +27 -0
  806. package/src/implement/nullable/getOrElse.test.ts +17 -0
  807. package/src/implement/nullable/getOrElse.ts +10 -0
  808. package/src/implement/nullable/index.ts +5 -0
  809. package/src/implement/nullable/mapMaybe.test.ts +23 -0
  810. package/src/implement/nullable/mapMaybe.ts +18 -0
  811. package/src/implement/nullable/maybe.test.ts +15 -0
  812. package/src/implement/nullable/maybe.ts +10 -0
  813. package/src/implement/nullable/result.test.ts +17 -0
  814. package/src/implement/nullable/result.ts +10 -0
  815. package/src/implement/object/assoc.test.ts +27 -0
  816. package/src/implement/object/assoc.ts +43 -0
  817. package/src/implement/object/assoc.type-test.ts +38 -0
  818. package/src/implement/object/assocPath.test.ts +33 -0
  819. package/src/implement/object/assocPath.ts +64 -0
  820. package/src/implement/object/curried.test.ts +49 -0
  821. package/src/implement/object/dissoc.test.ts +20 -0
  822. package/src/implement/object/dissoc.ts +26 -0
  823. package/src/implement/object/dissocPath.test.ts +18 -0
  824. package/src/implement/object/dissocPath.ts +80 -0
  825. package/src/implement/object/entries.test.ts +12 -0
  826. package/src/implement/object/entries.ts +8 -0
  827. package/src/implement/object/evolve.test.ts +14 -0
  828. package/src/implement/object/evolve.ts +31 -0
  829. package/src/implement/object/has.test.ts +10 -0
  830. package/src/implement/object/has.ts +16 -0
  831. package/src/implement/object/hasPath.test.ts +13 -0
  832. package/src/implement/object/hasPath.ts +17 -0
  833. package/src/implement/object/index.ts +20 -0
  834. package/src/implement/object/keys.test.ts +9 -0
  835. package/src/implement/object/keys.ts +8 -0
  836. package/src/implement/object/mapValues.test.ts +11 -0
  837. package/src/implement/object/mapValues.ts +16 -0
  838. package/src/implement/object/merge.test.ts +22 -0
  839. package/src/implement/object/merge.ts +19 -0
  840. package/src/implement/object/mergeAll.test.ts +13 -0
  841. package/src/implement/object/mergeAll.ts +12 -0
  842. package/src/implement/object/mergeDeep.test.ts +11 -0
  843. package/src/implement/object/mergeDeep.ts +41 -0
  844. package/src/implement/object/omit.test.ts +12 -0
  845. package/src/implement/object/omit.ts +20 -0
  846. package/src/implement/object/path.test.ts +9 -0
  847. package/src/implement/object/path.ts +16 -0
  848. package/src/implement/object/pathOr.test.ts +15 -0
  849. package/src/implement/object/pathOr.ts +18 -0
  850. package/src/implement/object/pick.test.ts +12 -0
  851. package/src/implement/object/pick.ts +22 -0
  852. package/src/implement/object/prop.test.ts +9 -0
  853. package/src/implement/object/prop.ts +16 -0
  854. package/src/implement/object/propOr.test.ts +15 -0
  855. package/src/implement/object/propOr.ts +22 -0
  856. package/src/implement/object/values.test.ts +9 -0
  857. package/src/implement/object/values.ts +8 -0
  858. package/src/implement/string/curried.test.ts +21 -0
  859. package/src/implement/string/endsWith.test.ts +23 -0
  860. package/src/implement/string/endsWith.ts +21 -0
  861. package/src/implement/string/index.ts +9 -0
  862. package/src/implement/string/join.test.ts +16 -0
  863. package/src/implement/string/join.ts +7 -0
  864. package/src/implement/string/match.test.ts +13 -0
  865. package/src/implement/string/match.ts +7 -0
  866. package/src/implement/string/replace.test.ts +12 -0
  867. package/src/implement/string/replace.ts +7 -0
  868. package/src/implement/string/split.test.ts +12 -0
  869. package/src/implement/string/split.ts +7 -0
  870. package/src/implement/string/startsWith.test.ts +21 -0
  871. package/src/implement/string/startsWith.ts +20 -0
  872. package/src/implement/string/toLower.test.ts +13 -0
  873. package/src/implement/string/toLower.ts +5 -0
  874. package/src/implement/string/toUpper.test.ts +13 -0
  875. package/src/implement/string/toUpper.ts +5 -0
  876. package/src/implement/string/trim.test.ts +12 -0
  877. package/src/implement/string/trim.ts +5 -0
  878. package/src/index.ts +32 -0
  879. package/src/stream/append.test.ts +20 -0
  880. package/src/stream/append.ts +39 -0
  881. package/src/stream/chunk.test.ts +62 -0
  882. package/src/stream/chunk.ts +74 -0
  883. package/src/stream/concat.test.ts +20 -0
  884. package/src/stream/concat.ts +43 -0
  885. package/src/stream/drop.test.ts +19 -0
  886. package/src/stream/drop.ts +59 -0
  887. package/src/stream/dropWhile.test.ts +20 -0
  888. package/src/stream/dropWhile.ts +51 -0
  889. package/src/stream/every.test.ts +19 -0
  890. package/src/stream/every.ts +46 -0
  891. package/src/stream/filter.test.ts +19 -0
  892. package/src/stream/filter.ts +46 -0
  893. package/src/stream/find.test.ts +18 -0
  894. package/src/stream/find.ts +43 -0
  895. package/src/stream/flatMap.test.ts +20 -0
  896. package/src/stream/flatMap.ts +48 -0
  897. package/src/stream/flatten.test.ts +17 -0
  898. package/src/stream/flatten.ts +33 -0
  899. package/src/stream/flattenDeep.test.ts +17 -0
  900. package/src/stream/flattenDeep.ts +41 -0
  901. package/src/stream/index.ts +23 -0
  902. package/src/stream/map.test.ts +19 -0
  903. package/src/stream/map.ts +39 -0
  904. package/src/stream/pipeAsync.test.ts +27 -0
  905. package/src/stream/prepend.test.ts +20 -0
  906. package/src/stream/prepend.ts +39 -0
  907. package/src/stream/range.test.ts +16 -0
  908. package/src/stream/range.ts +20 -0
  909. package/src/stream/reduce.test.ts +18 -0
  910. package/src/stream/reduce.ts +47 -0
  911. package/src/stream/scan.test.ts +22 -0
  912. package/src/stream/scan.ts +49 -0
  913. package/src/stream/some.test.ts +19 -0
  914. package/src/stream/some.ts +46 -0
  915. package/src/stream/take.test.ts +19 -0
  916. package/src/stream/take.ts +51 -0
  917. package/src/stream/takeWhile.test.ts +20 -0
  918. package/src/stream/takeWhile.ts +45 -0
  919. package/src/stream/toArray.test.ts +19 -0
  920. package/src/stream/toArray.ts +26 -0
  921. package/src/stream/toAsync.test.ts +11 -0
  922. package/src/stream/toAsync.ts +30 -0
  923. package/src/stream/utils.ts +30 -0
  924. package/src/stream/zip.test.ts +29 -0
  925. package/src/stream/zip.ts +56 -0
  926. package/src/stream/zipWith.test.ts +22 -0
  927. package/src/stream/zipWith.ts +62 -0
@@ -0,0 +1,1644 @@
1
+ # fp-pack AI Agent Skills
2
+
3
+ Document Version: 0.1.0
4
+
5
+ This document provides guidelines for AI coding assistants when working in projects that use fp-pack. Follow these instructions to write clean, declarative, functional code using fp-pack's utilities.
6
+
7
+ ## Project Philosophy
8
+
9
+ fp-pack is a TypeScript functional programming library focused on:
10
+
11
+ 1. **Function Composition**: Use `pipe` and `pipeAsync` as the primary tools for combining operations
12
+ 2. **Declarative Code**: Prefer function composition over imperative loops and mutations
13
+ 3. **No Monad Pattern**: Traditional FP monads (Option, Either, etc.) are NOT used - they don't compose well with `pipe`
14
+ 4. **SideEffect Pattern**: Handle errors and side effects using `SideEffect` with `pipeSideEffect` / `pipeAsyncSideEffect` pipelines
15
+ 5. **Lazy Evaluation**: Use `stream/*` functions for efficient iterable processing
16
+
17
+ ## Core Composition Functions
18
+
19
+ ### pipe - Synchronous Function Composition
20
+
21
+ **Always prefer `pipe` for synchronous operations** instead of manual imperative code.
22
+
23
+ ```typescript
24
+ import { pipe, map, filter, take } from 'fp-pack';
25
+
26
+ // GOOD: Declarative pipe composition
27
+ const processUsers = pipe(
28
+ filter((user: User) => user.age >= 18),
29
+ map(user => user.name.toUpperCase()),
30
+ take(10)
31
+ );
32
+
33
+ // BAD: Imperative approach
34
+ const processUsers = (users: User[]) => {
35
+ const result = [];
36
+ for (const user of users) {
37
+ if (user.age >= 18) {
38
+ result.push(user.name.toUpperCase());
39
+ if (result.length >= 10) break;
40
+ }
41
+ }
42
+ return result;
43
+ };
44
+ ```
45
+
46
+ > For SideEffect-based early exits, use `pipeSideEffect`.
47
+
48
+ ### pipeAsync - Asynchronous Function Composition
49
+
50
+ **Use `pipeAsync` for any async operations** including API calls, database queries, or async transformations.
51
+
52
+ ```typescript
53
+ import { pipeAsync } from 'fp-pack';
54
+
55
+ // GOOD: Async pipe composition
56
+ const fetchUserData = pipeAsync(
57
+ async (userId: string) => fetch(`/api/users/${userId}`),
58
+ async (response) => response.json(),
59
+ (data) => data.user
60
+ );
61
+
62
+ // BAD: Manual async handling
63
+ const fetchUserData = async (userId: string) => {
64
+ const response = await fetch(`/api/users/${userId}`);
65
+ const data = await response.json();
66
+ return data.user;
67
+ };
68
+ ```
69
+
70
+ > For SideEffect-aware async pipelines, use `pipeAsyncSideEffect`.
71
+
72
+ ## SideEffect Pattern - For Special Cases Only
73
+
74
+ **Most cases: Use `pipe` / `pipeAsync` - they're simpler and sufficient for 99% of use cases.**
75
+
76
+ `pipe` and `pipeAsync` are for **pure** functions and don't handle `SideEffect`. **Only use `pipeSideEffect`/`pipeAsyncSideEffect` when you specifically need**:
77
+ - Early termination based on validation
78
+ - Error handling with side effects (logging, toasts, etc.)
79
+ - Optional chaining patterns
80
+
81
+ For regular error handling, standard try-catch or error propagation is perfectly fine.
82
+
83
+ ```typescript
84
+ // MOST CASES: Just use pipe with regular error handling
85
+ import { pipe, map, filter } from 'fp-pack';
86
+
87
+ const processData = pipe(
88
+ validateInput,
89
+ transformData,
90
+ saveData
91
+ );
92
+
93
+ try {
94
+ const result = processData(input);
95
+ } catch (error) {
96
+ console.error('Processing failed:', error);
97
+ }
98
+
99
+ // SPECIAL CASES: Use pipeSideEffect when you need early termination with side effects
100
+ import { pipeSideEffect, SideEffect, runPipeResult } from 'fp-pack';
101
+
102
+ const processDataPipeline = pipeSideEffect(
103
+ validateInput,
104
+ (data) => {
105
+ if (!data.isValid) {
106
+ return SideEffect.of(() => {
107
+ showToast('Invalid data'); // Side effect
108
+ logError('validation_failed'); // Side effect
109
+ return null;
110
+ });
111
+ }
112
+ return data;
113
+ },
114
+ transformData
115
+ );
116
+
117
+ // runPipeResult must be called OUTSIDE the pipeline
118
+ const finalValue = runPipeResult(processDataPipeline(input));
119
+ ```
120
+
121
+ **Key SideEffect functions:**
122
+ - `SideEffect.of(fn, label?)` - Create a side effect container
123
+ - `isSideEffect(value)` - Type guard for **runtime checking** whether a value is a SideEffect
124
+ - `runPipeResult<T, R>(result)` - Execute SideEffect or return value (call **OUTSIDE** pipelines, provide generics for type safety). **⚠️ CRITICAL:** `runPipeResult<T, R=any>` has default `R=any`, so using it without generics returns `any` type. Always provide generics for type safety
125
+ - `matchSideEffect(result, { value, effect })` - Pattern match on result
126
+
127
+ **Type-safe result handling:**
128
+
129
+ ```typescript
130
+ import { pipeSideEffect, SideEffect, isSideEffect, runPipeResult } from 'fp-pack';
131
+
132
+ const processNumbers = pipeSideEffect(
133
+ (nums: number[]) => nums.filter(n => n % 2 === 1),
134
+ (odds) => odds.length > 0
135
+ ? odds
136
+ : SideEffect.of(() => 'No odd numbers'),
137
+ (odds) => odds.map(n => n * 2)
138
+ );
139
+
140
+ const result = processNumbers([1, 2, 3, 4, 5]);
141
+
142
+ // ✅ CORRECT: Use isSideEffect for runtime checking + provide generics to runPipeResult
143
+ if (!isSideEffect(result)) {
144
+ // TypeScript knows: result is number[]
145
+ const sum: number = result.reduce((a, b) => a + b, 0);
146
+ } else {
147
+ // TypeScript knows: result is SideEffect<string>
148
+ // But runPipeResult still returns number[] | string (not fully narrowed)
149
+ const error = runPipeResult<number[], string>(result); // error: number[] | string
150
+ }
151
+
152
+ // ❌ WRONG: runPipeResult without generics
153
+ const value = runPipeResult(result); // result: any (no type information!)
154
+
155
+ // ✅ CORRECT: Provide generics to runPipeResult
156
+ const value = runPipeResult<number[], string>(result); // result: number[] | string (union type - safe but not narrowed)
157
+ ```
158
+
159
+ **⚠️ CRITICAL: runPipeResult Type Safety**
160
+
161
+ `runPipeResult<T, R=any>` has a default type parameter `R=any`. This means:
162
+
163
+ - ❌ **Without generics**: `const result = runPipeResult(pipeline(data));` returns `any` type (unsafe!)
164
+ - ✅ **With generics**: `runPipeResult<SuccessType, ErrorType>(result)` returns union type `SuccessType | ErrorType` (type-safe)
165
+ - ✅ **With isSideEffect**: Use for runtime checking whether a value is SideEffect
166
+
167
+ **Always provide generics to `runPipeResult`** for type safety. Use `isSideEffect` for runtime type checking.
168
+
169
+ ## Stream Functions - Lazy Iterable Processing
170
+
171
+ **Use `stream/*` functions for lazy, memory-efficient data processing** instead of array methods.
172
+
173
+ ```typescript
174
+ import { pipe } from 'fp-pack';
175
+ import { map, filter, take, toArray, range } from 'fp-pack/stream';
176
+
177
+ // GOOD: Lazy stream processing
178
+ const processLargeDataset = pipe(
179
+ filter((n: number) => n % 2 === 0),
180
+ map(n => n * n),
181
+ take(100),
182
+ toArray
183
+ );
184
+
185
+ // Processes only what's needed - memory efficient
186
+ const result = processLargeDataset(range(1, 1000000));
187
+
188
+ // BAD: Eager array processing
189
+ const result = Array.from({ length: 1000000 }, (_, i) => i + 1)
190
+ .filter(n => n % 2 === 0)
191
+ .map(n => n * n)
192
+ .slice(0, 100); // Processed entire dataset!
193
+ ```
194
+
195
+ **Stream functions support both sync and async iterables:**
196
+ - Sync: `Iterable<T>` → `IterableIterator<R>`
197
+ - Async: `AsyncIterable<T>` → `AsyncIterableIterator<R>`
198
+
199
+ ## Available Functions by Category
200
+
201
+ ### Composition
202
+ - `pipe` - Left-to-right function composition (sync)
203
+ - `pipeSideEffect` - Left-to-right composition with SideEffect short-circuiting
204
+ - `compose` - Right-to-left function composition
205
+ - `curry` - Curry a function
206
+ - `partial` - Partial application
207
+ - `flip` - Flip function argument order
208
+ - `complement` - Logical negation
209
+ - `identity` - Return input unchanged
210
+ - `constant` - Always return the same value
211
+ - `tap` - Execute side effect and return original value
212
+ - `once` - Execute function only once
213
+ - `memoize` - Cache function results
214
+ - `SideEffect` - Side effect container
215
+ - `isSideEffect` - Type guard for SideEffect
216
+ - `matchSideEffect` - Pattern match on value/SideEffect
217
+ - `runPipeResult` - Execute SideEffect or return value
218
+
219
+ ### Async
220
+ - `pipeAsync` - Async function composition
221
+ - `pipeAsyncSideEffect` - Async composition with SideEffect short-circuiting
222
+ - `delay` - Delay execution
223
+ - `timeout` - Add timeout to promise
224
+ - `retry` - Retry failed operations
225
+ - `debounce` - Debounce function calls
226
+ - `debounceLeading` - Debounce with leading edge
227
+ - `debounceLeadingTrailing` - Debounce with both edges
228
+ - `throttle` - Throttle function calls
229
+
230
+ ### Array
231
+ - `map`, `filter`, `reduce`, `flatMap`
232
+ - `find`, `some`, `every`
233
+ - `take`, `drop`, `takeWhile`, `dropWhile`
234
+ - `chunk`, `zip`, `zipWith`, `unzip`, `zipIndex`
235
+ - `uniq`, `uniqBy`, `sort`, `sortBy`, `groupBy`
236
+ - `concat`, `append`, `prepend`, `flatten`, `flattenDeep`
237
+ - `head`, `tail`, `last`, `init`
238
+ - `range`, `partition`, `scan`
239
+
240
+ ### Object
241
+ - `prop`, `propOr`, `path`, `pathOr`
242
+ - `pick`, `omit`
243
+ - `assoc`, `assocPath`, `dissoc`, `dissocPath`
244
+ - `merge`, `mergeDeep`, `mergeAll`
245
+ - `keys`, `values`, `entries`
246
+ - `mapValues`, `evolve`
247
+ - `has`, `hasPath`
248
+
249
+ ### Control Flow
250
+ - `ifElse` - Conditional branching
251
+ - `when`, `unless` - Conditional execution
252
+ - `cond` - Multi-branch conditional
253
+ - `tryCatch` - Safe function execution
254
+ - `guard` - Validation guard
255
+
256
+ ### Stream (Lazy Iterables)
257
+ - `append`, `concat`, `prepend`
258
+ - `map`, `filter`, `flatMap`, `flatten`, `flattenDeep`
259
+ - `take`, `takeWhile`, `drop`, `dropWhile`, `chunk`
260
+ - `zip`, `zipWith`, `find`, `some`, `every`
261
+ - `reduce`, `scan`
262
+ - `range`
263
+ - `toArray` - Materialize stream to array
264
+ - `toAsync` - Convert to async iterable
265
+
266
+ ### Math
267
+ - `add`, `sub`, `mul`, `div`
268
+ - `sum`, `mean`, `min`, `max`
269
+ - `round`, `floor`, `ceil`, `randomInt`
270
+
271
+ ### String
272
+ - `trim`, `split`, `join`, `replace`
273
+ - `toUpper`, `toLower`
274
+ - `startsWith`, `endsWith`, `match`
275
+
276
+ ### Equality
277
+ - `equals`, `includes`
278
+ - `isNil`, `isEmpty`, `isType`
279
+ - `gt`, `gte`, `lt`, `lte`
280
+ - `clamp`
281
+
282
+ ### Nullable
283
+ - `maybe`, `mapMaybe`, `getOrElse`, `fold`, `result`
284
+
285
+ ### Debug
286
+ - `assert`, `invariant`, `log`
287
+
288
+ ## Coding Guidelines for AI Agents
289
+
290
+ ### 0. Detect Project Language (JS vs TS)
291
+
292
+ Before writing code, check whether the project is JavaScript or TypeScript:
293
+
294
+ - **TypeScript projects**: use explicit types, leverage generics, and keep type-safe signatures in examples.
295
+ - **JavaScript projects**: avoid TypeScript-only syntax and prefer JSDoc only when it adds clarity or is already used.
296
+
297
+ ### 0.1 Quick Signature Lookup
298
+
299
+ If a function signature or argument order is unclear, check the local declaration or source files:
300
+
301
+ - Main exports: `dist/index.d.ts`
302
+ - Stream exports: `dist/stream/index.d.ts`
303
+ - Main utilities (fallback): `src/implement/**`
304
+ - Stream utilities (fallback): `src/stream/**`
305
+ - Installed package:
306
+ - `node_modules/fp-pack/dist/index.d.ts`
307
+ - `node_modules/fp-pack/dist/stream/index.d.ts`
308
+ - `node_modules/fp-pack/src/implement/**`
309
+ - `node_modules/fp-pack/src/stream/**`
310
+
311
+ ### 1. Always Prefer pipe/pipeAsync
312
+
313
+ ```typescript
314
+ // GOOD
315
+ const result = pipe(
316
+ trim,
317
+ split(','),
318
+ map(toNumber),
319
+ filter(isPositive)
320
+ )(input);
321
+
322
+ // BAD
323
+ const trimmed = trim(input);
324
+ const parts = split(',')(trimmed);
325
+ const numbers = map(toNumber)(parts);
326
+ const result = filter(isPositive)(numbers);
327
+ ```
328
+
329
+ ### 2. Use Curried Functions (Where Available)
330
+
331
+ Most multi-arg functions are curried. Many single-arg utilities are not (e.g. `uniq`, `flatten`, `flattenDeep`, `head`, `tail`, `last`, `init`, `range`, `sum`, `mean`, `min`, `max`, `round`, `floor`, `ceil`, `trim`, `toLower`, `toUpper`, `isNil`, `isEmpty`, `isType`). Call those directly (there's no curried variant).
332
+
333
+ ```typescript
334
+ import { pipe, map, filter } from 'fp-pack';
335
+
336
+ // GOOD: Curried usage in pipe
337
+ const processUsers = pipe(
338
+ filter(user => user.active),
339
+ map(user => user.name)
340
+ );
341
+
342
+ // GOOD: Partial application
343
+ const filterActive = filter((user: User) => user.active);
344
+ const getNames = map((user: User) => user.name);
345
+ const processUsers = pipe(filterActive, getNames);
346
+ ```
347
+
348
+ ### 2.1 Custom Utility Authoring (Curry Typing)
349
+
350
+ When you add your own helpers for `pipe`, follow these rules:
351
+
352
+ - Keep **data-last** argument order.
353
+ - **Curry multi-arg functions** so they compose well.
354
+ - **Fixed signatures** can use `curry(fn)` directly.
355
+ - **Generic or overloaded signatures** should use an explicit type alias + cast to preserve inference.
356
+
357
+ ```typescript
358
+ // Fixed signature: curry is enough
359
+ function split(separator: string, str: string): string[] {
360
+ return str.split(separator);
361
+ }
362
+ export default curry(split);
363
+
364
+ // Generic signature: add a type alias for the curried form
365
+ type Chunk = {
366
+ (size: number): <T>(arr: T[]) => T[][];
367
+ <T>(size: number, arr: T[]): T[][];
368
+ };
369
+
370
+ function chunk<T>(size: number, arr: T[]): T[][] {
371
+ // ...
372
+ return [];
373
+ }
374
+
375
+ const curriedChunk = curry(chunk) as Chunk;
376
+ export default curriedChunk;
377
+ ```
378
+
379
+ ### 3. Choose pipe vs pipeSideEffect
380
+
381
+ **Default choice: Start with `pipe` / `pipeAsync`**
382
+
383
+ Most data transformations are pure and don't need SideEffect handling. Use `pipe` for sync operations and `pipeAsync` for async operations. **Only switch to SideEffect-aware pipes when you actually need** early termination or error handling with side effects.
384
+
385
+ - **`pipe`** - Synchronous, **pure** transformations (99% of cases)
386
+ - **`pipeAsync`** - Async, **pure** transformations (99% of cases)
387
+ - **`pipeSideEffect`** - **Only when you need** SideEffect short-circuiting (sync)
388
+ - **`pipeAsyncSideEffect`** - **Only when you need** SideEffect short-circuiting (async)
389
+
390
+ **Important:** `pipe` and `pipeAsync` are for **pure** functions only—they don't handle `SideEffect`. If your pipeline can return `SideEffect`, use `pipeSideEffect` or `pipeAsyncSideEffect` instead.
391
+
392
+ ```typescript
393
+ // Sync: use pipe
394
+ const processNumbers = pipe(
395
+ map((n: number) => n * 2),
396
+ filter(n => n > 10)
397
+ );
398
+
399
+ // Async: use pipeAsync
400
+ const processUsers = pipeAsync(
401
+ async (ids: string[]) => db.users.findMany(ids),
402
+ map(user => user.profile),
403
+ filter(profile => profile.verified)
404
+ );
405
+ ```
406
+
407
+ ### 3.1. SideEffect Composition Rule
408
+
409
+ **🔄 Critical Rule: SideEffect Contagion**
410
+
411
+ Once you use `pipeSideEffect` or `pipeAsyncSideEffect`, the result is **always `T | SideEffect`** (or `Promise<T | SideEffect>` for async).
412
+
413
+ If you want to continue composing this result, you **MUST** keep using SideEffect-aware pipes. You **CANNOT** switch back to `pipe` or `pipeAsync` because they don't handle `SideEffect`.
414
+
415
+ ```typescript
416
+ import { pipe, pipeSideEffect, SideEffect } from 'fp-pack';
417
+
418
+ const validateUserPipeline = pipeSideEffect(
419
+ findUser,
420
+ validateAge
421
+ );
422
+ // Result type: User | SideEffect
423
+
424
+ // ❌ WRONG - pipe cannot handle SideEffect
425
+ const wrongPipeline = pipe(
426
+ validateUserPipeline, // Returns User | SideEffect
427
+ (user) => user.email // Type error! SideEffect has no 'email' property
428
+ );
429
+
430
+ // ✅ CORRECT - Keep using pipeSideEffect
431
+ const correctPipeline = pipeSideEffect(
432
+ validateUserPipeline, // User | SideEffect - handled correctly
433
+ (user) => user.email, // Automatically skipped if SideEffect
434
+ sendEmail
435
+ );
436
+
437
+ // The same rule applies to async pipes
438
+ const asyncPipeline = pipeAsyncSideEffect(
439
+ fetchUser,
440
+ validateUser
441
+ );
442
+ // Result type: Promise<User | SideEffect>
443
+
444
+ // You must continue with pipeAsyncSideEffect, not pipeAsync
445
+ const extendedAsyncPipeline = pipeAsyncSideEffect(
446
+ asyncPipeline,
447
+ processUser,
448
+ saveToDatabase
449
+ );
450
+ ```
451
+
452
+ ### 4. Use stream/* for Large Datasets
453
+
454
+ ```typescript
455
+ import { pipe } from 'fp-pack';
456
+ import { filter, map, take, toArray, range } from 'fp-pack/stream';
457
+
458
+ // GOOD: Lazy processing
459
+ const getFirst100Even = pipe(
460
+ filter((n: number) => n % 2 === 0),
461
+ take(100),
462
+ toArray
463
+ );
464
+
465
+ // Stops after finding 100 items (only processes 100, not 1 million)
466
+ const result = getFirst100Even(range(1, 1000000));
467
+ ```
468
+
469
+ ### 5. Handle Errors with SideEffect
470
+
471
+ ```typescript
472
+ import { pipeSideEffect, SideEffect, runPipeResult } from 'fp-pack';
473
+
474
+ const safeDividePipeline = pipeSideEffect(
475
+ (input: { a: number; b: number }) => {
476
+ if (input.b === 0) {
477
+ return SideEffect.of(() => {
478
+ throw new Error('Division by zero');
479
+ }, 'DIVISION_ERROR');
480
+ }
481
+ return input;
482
+ },
483
+ ({ a, b }) => a / b
484
+ );
485
+
486
+ // runPipeResult must be called OUTSIDE the pipeline
487
+ const result = runPipeResult(safeDividePipeline({ a: 10, b: 2 })); // 5
488
+ ```
489
+
490
+ ### 6. Use Control Flow Functions
491
+
492
+ ```typescript
493
+ import { pipe, ifElse, when, cond } from 'fp-pack';
494
+
495
+ // GOOD: Declarative conditionals
496
+ const processAge = pipe(
497
+ ifElse(
498
+ (age: number) => age >= 18,
499
+ age => ({ age, status: 'adult' }),
500
+ age => ({ age, status: 'minor' })
501
+ )
502
+ );
503
+
504
+ // GOOD: Multi-branch with cond
505
+ const gradeToLetter = cond([
506
+ [(n: number) => n >= 90, () => 'A'],
507
+ [(n: number) => n >= 80, () => 'B'],
508
+ [(n: number) => n >= 70, () => 'C'],
509
+ [() => true, () => 'F']
510
+ ]);
511
+ ```
512
+
513
+ ### 7. Object Transformations
514
+
515
+ ```typescript
516
+ import { pipe, pick, mapValues, merge } from 'fp-pack';
517
+
518
+ // GOOD: Declarative object operations
519
+ const processUser = pipe(
520
+ pick(['name', 'email', 'age']),
521
+ mapValues((value) => typeof value === 'string' ? value.trim() : value),
522
+ merge({ verified: false })
523
+ );
524
+ ```
525
+
526
+ ## Anti-Patterns to Avoid
527
+
528
+ ### ❌ Don't use imperative loops
529
+
530
+ ```typescript
531
+ // BAD
532
+ const result = [];
533
+ for (const item of items) {
534
+ if (item.active) {
535
+ result.push(item.name);
536
+ }
537
+ }
538
+
539
+ // GOOD
540
+ const result = pipe(
541
+ filter((item: Item) => item.active),
542
+ map(item => item.name)
543
+ )(items);
544
+ ```
545
+
546
+ ### ❌ Don't chain array methods
547
+
548
+ ```typescript
549
+ // BAD
550
+ const result = users
551
+ .filter(u => u.active)
552
+ .map(u => u.name)
553
+ .slice(0, 10);
554
+
555
+ // GOOD
556
+ const result = pipe(
557
+ filter((u: User) => u.active),
558
+ map(u => u.name),
559
+ take(10)
560
+ )(users);
561
+ ```
562
+
563
+ ### ❌ Don't use traditional monads (Option, Either, Maybe)
564
+
565
+ ```typescript
566
+ // BAD - Don't implement this pattern
567
+ const maybeUser = Option.of(user)
568
+ .map(u => u.profile)
569
+ .flatMap(p => p.email);
570
+
571
+ // GOOD - Use SideEffect with pipeSideEffect
572
+ const getUserEmail = pipeSideEffect(
573
+ (user: User) => {
574
+ if (!user.profile) {
575
+ return SideEffect.of(() => null, 'NO_PROFILE');
576
+ }
577
+ return user.profile;
578
+ },
579
+ (profile) => {
580
+ if (!profile.email) {
581
+ return SideEffect.of(() => null, 'NO_EMAIL');
582
+ }
583
+ return profile.email;
584
+ }
585
+ );
586
+ ```
587
+
588
+ ### ❌ Don't mutate data
589
+
590
+ ```typescript
591
+ // BAD
592
+ const updateUser = (user: User) => {
593
+ user.lastLogin = new Date();
594
+ return user;
595
+ };
596
+
597
+ // GOOD
598
+ const updateUser = (user: User) => ({
599
+ ...user,
600
+ lastLogin: new Date()
601
+ });
602
+
603
+ // EVEN BETTER with fp-pack
604
+ import { assoc } from 'fp-pack';
605
+ const updateUser = assoc('lastLogin', new Date());
606
+ ```
607
+
608
+ ## Quick Reference
609
+
610
+ ### Import Paths
611
+ - Main functions: `import { pipe, map, filter } from 'fp-pack'`
612
+ - Async: `import { pipeAsync, delay, retry } from 'fp-pack'`
613
+ - SideEffect: `import { pipeSideEffect, pipeAsyncSideEffect, SideEffect } from 'fp-pack'`
614
+ - Stream: `import { map, filter, toArray } from 'fp-pack/stream'`
615
+
616
+ ### When to Use What
617
+ - **Pure sync transformations**: `pipe` + array/object functions
618
+ - **Pure async operations**: `pipeAsync`
619
+ - **Error handling with SideEffect**: `pipeSideEffect` (sync) / `pipeAsyncSideEffect` (async)
620
+ - **Type-safe result handling**: `isSideEffect` for precise type narrowing (⚠️ **ALWAYS prefer this over bare `runPipeResult`** to avoid `any` types)
621
+ - **Execute SideEffect**: `runPipeResult` (call OUTSIDE pipelines). **⚠️ CRITICAL:** Returns `any` type without type narrowing due to default `R=any` parameter
622
+ - **Large datasets**: `stream/*` functions
623
+ - **Conditionals**: `ifElse`, `when`, `unless`, `cond`
624
+ - **Object access**: `prop`, `path`, `pick`, `omit`
625
+ - **Object updates**: `assoc`, `merge`, `evolve`
626
+
627
+ ## UI Framework Integration Patterns
628
+
629
+ fp-pack works seamlessly with UI frameworks. Here are common patterns organized by **use case**, not framework.
630
+
631
+ ### Pattern 1: Handling User Input
632
+
633
+ **When**: Form inputs, button clicks, drag & drop, any user interaction
634
+ **Where to use**: Event handlers (onChange, @input, on:click, etc.)
635
+
636
+ ```typescript
637
+ import { pipe, pipeAsyncSideEffect, trim, prop, assoc, tap, SideEffect, runPipeResult } from 'fp-pack';
638
+
639
+ // GOOD: Process form input declaratively
640
+ const handleNameChange = pipe(
641
+ prop('currentTarget'), // Safer than target in most UI libs
642
+ (el) => (el as HTMLInputElement).value,
643
+ trim,
644
+ tap((value) => {
645
+ // Prefer updater form to avoid stale state in React-like frameworks
646
+ setFormState(prev => assoc('name', value, prev));
647
+ })
648
+ );
649
+
650
+ // Use in any framework:
651
+ // React: <input onChange={handleNameChange} />
652
+ // Vue: <input @input="handleNameChange" />
653
+ // Svelte: <input on:input={handleNameChange} />
654
+
655
+ // GOOD: Complex form validation
656
+ const validateFieldsOrStop = (data: any) => {
657
+ const errors = validateFields(data);
658
+ if (!errors) return data;
659
+ return SideEffect.of(() => {
660
+ setErrors(errors);
661
+ return null;
662
+ }, 'VALIDATION_ERROR');
663
+ };
664
+
665
+ const handleSubmitPipeline = pipeAsyncSideEffect(
666
+ tap((e: Event) => e.preventDefault()),
667
+ prop('currentTarget'),
668
+ (form) => getFormData(form as HTMLFormElement),
669
+ validateFieldsOrStop, // Returns data or SideEffect
670
+ sanitizeInput,
671
+ submitToAPI
672
+ );
673
+
674
+ const handleSubmit = (e: Event) => runPipeResult(handleSubmitPipeline(e));
675
+ ```
676
+
677
+ ### Pattern 2: Computing Derived/Reactive Values
678
+
679
+ **When**: Displaying filtered/sorted/transformed data from state
680
+ **Where to use**: Computed properties, memoized values, derived state
681
+
682
+ ```typescript
683
+ import { pipe, filter, sortBy, map, take } from 'fp-pack';
684
+
685
+ // GOOD: Create reusable data transformation
686
+ const processUsers = pipe(
687
+ filter((u: User) => u.status === 'active'),
688
+ sortBy(u => u.lastLogin),
689
+ map(u => ({ ...u, displayName: `${u.firstName} ${u.lastName}` })),
690
+ take(50)
691
+ );
692
+
693
+ // Use in any framework:
694
+ // React: const processed = useMemo(() => processUsers(users), [users]);
695
+ // Vue: const processed = computed(() => processUsers(users.value));
696
+ // Svelte: $: processed = processUsers($users);
697
+ // Solid: const processed = createMemo(() => processUsers(users()));
698
+
699
+ // GOOD: Search + filter + pagination
700
+ const searchUsers = (query: string, page: number) =>
701
+ pipe(
702
+ filter((u: User) =>
703
+ u.name.toLowerCase().includes(query.toLowerCase())
704
+ ),
705
+ sortBy(u => u.name),
706
+ chunk(20), // Paginate
707
+ (pages) => pages[page] || []
708
+ );
709
+
710
+ // React example:
711
+ // const results = useMemo(
712
+ // () => searchUsers(searchQuery, currentPage)(allUsers),
713
+ // [searchQuery, currentPage, allUsers]
714
+ // );
715
+ ```
716
+
717
+ ### Pattern 3: Async Data Fetching and Processing
718
+
719
+ **When**: API calls, database queries, file operations
720
+ **Where to use**: Lifecycle hooks, effects, async event handlers
721
+
722
+ ```typescript
723
+ import { pipeAsync, pipeAsyncSideEffect, tap, SideEffect, runPipeResult } from 'fp-pack';
724
+ import { filter, map } from 'fp-pack';
725
+
726
+ // GOOD: Fetch + transform + update state
727
+ const fetchAndProcessUsers = pipeAsync(
728
+ async (userId: string) => fetch(`/api/users/${userId}/friends`),
729
+ async (res) => res.json(),
730
+ filter((u: User) => u.isActive),
731
+ map(u => ({ id: u.id, name: u.name, avatar: u.avatar })),
732
+ (processed) => {
733
+ setUsers(processed); // Framework-specific state update
734
+ return processed;
735
+ }
736
+ );
737
+
738
+ // Use in any framework:
739
+ // React: useEffect(() => { fetchAndProcessUsers(id); }, [id]);
740
+ // Vue: watchEffect(() => fetchAndProcessUsers(userId.value));
741
+ // Svelte: $: fetchAndProcessUsers($userId);
742
+
743
+ // GOOD: Error handling with SideEffect
744
+ const validateResponseOrStop = (users: unknown) => {
745
+ if (!Array.isArray(users)) {
746
+ return SideEffect.of(() => {
747
+ setError('Invalid response');
748
+ return [];
749
+ }, 'INVALID_RESPONSE');
750
+ }
751
+ return users as User[];
752
+ };
753
+
754
+ const safeFetchUsersPipeline = pipeAsyncSideEffect(
755
+ fetchUsers,
756
+ validateResponseOrStop,
757
+ filter((u: User) => u.verified),
758
+ tap((users) => setUsers(users))
759
+ );
760
+
761
+ const safeFetchUsers = () => runPipeResult(safeFetchUsersPipeline());
762
+ ```
763
+
764
+ ### Pattern 4: List/Table Data Processing
765
+
766
+ **When**: Displaying lists, tables, grids with search/filter/sort
767
+ **Where to use**: Component render logic, computed values
768
+
769
+ ```typescript
770
+ import { pipe, filter, sortBy, groupBy, map } from 'fp-pack';
771
+
772
+ // GOOD: Complete table data pipeline
773
+ const processTableData = (
774
+ data: Product[],
775
+ filters: Filters,
776
+ sortConfig: SortConfig
777
+ ) => pipe(
778
+ // Apply filters
779
+ filter((p: Product) => {
780
+ if (filters.category && p.category !== filters.category) return false;
781
+ if (filters.minPrice && p.price < filters.minPrice) return false;
782
+ if (filters.maxPrice && p.price > filters.maxPrice) return false;
783
+ return true;
784
+ }),
785
+ // Apply sorting
786
+ sortBy(sortConfig.direction === 'asc'
787
+ ? (p) => p[sortConfig.key]
788
+ : (p) => -p[sortConfig.key]
789
+ ),
790
+ // Add row metadata
791
+ map((product, index) => ({
792
+ ...product,
793
+ rowId: `row-${index}`,
794
+ isEven: index % 2 === 0
795
+ }))
796
+ )(data);
797
+
798
+ // GOOD: Group for categorized display
799
+ const groupProductsByCategory = pipe(
800
+ groupBy((p: Product) => p.category),
801
+ (grouped) => Object.entries(grouped).map(([category, products]) => ({
802
+ category,
803
+ products,
804
+ count: products.length,
805
+ totalValue: products.reduce((sum, p) => sum + p.price, 0)
806
+ }))
807
+ );
808
+ ```
809
+
810
+ ### Pattern 5: Form State Management
811
+
812
+ **When**: Complex forms with validation and state
813
+ **Where to use**: Form submission, field updates, validation
814
+
815
+ ```typescript
816
+ import { pipe, pipeSideEffect, assoc, pick, mapValues, SideEffect, runPipeResult } from 'fp-pack';
817
+
818
+ // GOOD: Update nested form state immutably
819
+ const updateField = (fieldName: string, value: any) =>
820
+ pipe(
821
+ assoc(fieldName, value),
822
+ (state) => assoc('touched', { ...state.touched, [fieldName]: true }, state)
823
+ );
824
+
825
+ // GOOD: Form submission with validation
826
+ const validateFormOrStop = (data: any) => {
827
+ const errors = validateFormData(data);
828
+ return Object.keys(errors).length > 0
829
+ ? SideEffect.of(() => {
830
+ setFormErrors(errors);
831
+ return null;
832
+ }, 'VALIDATION_ERROR')
833
+ : data;
834
+ };
835
+
836
+ const submitFormPipeline = pipeSideEffect(
837
+ pick(['email', 'password', 'name']), // Only include relevant fields
838
+ mapValues((v) => typeof v === 'string' ? v.trim() : v), // Sanitize
839
+ validateFormOrStop,
840
+ submitToAPI
841
+ );
842
+
843
+ const submitForm = (data: any) => runPipeResult(submitFormPipeline(data));
844
+
845
+ // GOOD: Multi-step form state
846
+ const validateCurrentStepOrStop = (state: any) => {
847
+ const errors = validateCurrentStep(state);
848
+ if (!errors) return state;
849
+ return SideEffect.of(() => {
850
+ setStepErrors(errors);
851
+ return state;
852
+ }, 'STEP_VALIDATION_ERROR');
853
+ };
854
+
855
+ const goToNextStepPipeline = pipeSideEffect(
856
+ validateCurrentStepOrStop,
857
+ (state) => assoc('currentStep', state.currentStep + 1, state)
858
+ );
859
+
860
+ const goToNextStep = (state: any) => runPipeResult(goToNextStepPipeline(state));
861
+ ```
862
+
863
+ ### Pattern 6: Real-time Data Streams
864
+
865
+ **When**: WebSocket updates, SSE, real-time data
866
+ **Where to use**: WebSocket handlers, event listeners
867
+
868
+ ```typescript
869
+ import { pipe, filter, map, take } from 'fp-pack';
870
+
871
+ // GOOD: Process incoming WebSocket messages
872
+ const handleWebSocketMessage = pipe(
873
+ (event: MessageEvent) => JSON.parse(event.data),
874
+ filter((msg: Message) => msg.type === 'USER_UPDATE'),
875
+ map(msg => msg.payload),
876
+ (update) => {
877
+ // Update state with new data
878
+ setUsers(prevUsers =>
879
+ prevUsers.map(u => u.id === update.id ? { ...u, ...update } : u)
880
+ );
881
+ }
882
+ );
883
+
884
+ // websocket.onmessage = handleWebSocketMessage;
885
+
886
+ // GOOD: Batch updates with stream
887
+ import { pipe as streamPipe, filter as streamFilter, take as streamTake, toArray } from 'fp-pack/stream';
888
+ import { pipeAsync, runPipeResult } from 'fp-pack';
889
+
890
+ const processBatchUpdates = async (updates: AsyncIterable<Update>) => {
891
+ const processed = await streamPipe(
892
+ streamFilter((u: Update) => u.priority === 'high'),
893
+ streamTake(100),
894
+ toArray
895
+ )(updates);
896
+
897
+ batchUpdateUI(processed);
898
+ };
899
+ ```
900
+
901
+ ### Pattern 7: Component Props Transformation
902
+
903
+ **When**: Passing data to child components
904
+ **Where to use**: Component composition, prop drilling
905
+
906
+ ```typescript
907
+ import { pipe, pick, map, merge } from 'fp-pack';
908
+
909
+ // GOOD: Transform data for child component
910
+ const prepareUserCardProps = pipe(
911
+ pick(['id', 'name', 'avatar', 'status']),
912
+ merge({
913
+ onClick: handleUserClick,
914
+ className: 'user-card'
915
+ })
916
+ );
917
+
918
+ // Usage:
919
+ // const userProps = prepareUserCardProps(user);
920
+ // <UserCard {...userProps} />
921
+
922
+ // GOOD: Prepare list of component props
923
+ const prepareListItems = pipe(
924
+ filter((item: Item) => item.visible),
925
+ map(item => ({
926
+ key: item.id,
927
+ ...pick(['title', 'description', 'icon'], item),
928
+ onClick: () => handleClick(item.id),
929
+ isActive: item.id === activeId
930
+ }))
931
+ );
932
+
933
+ // Usage:
934
+ // {prepareListItems(items).map(props => <ListItem {...props} />)}
935
+ ```
936
+
937
+ ### Pattern 8: State Update Reducers
938
+
939
+ **When**: Complex state updates, global state management
940
+ **Where to use**: Redux/Zustand/Pinia reducers, state update functions
941
+
942
+ ```typescript
943
+ import { pipe, assoc, dissoc, merge, evolve } from 'fp-pack';
944
+
945
+ // GOOD: Redux-style reducer with fp-pack
946
+ const userReducer = (state: State, action: Action) => {
947
+ switch (action.type) {
948
+ case 'ADD_USER':
949
+ return pipe(
950
+ prop('users'),
951
+ append(action.payload),
952
+ (users) => assoc('users', users, state)
953
+ )(state);
954
+
955
+ case 'UPDATE_USER':
956
+ return pipe(
957
+ prop('users'),
958
+ map((u: User) => u.id === action.payload.id
959
+ ? merge(u, action.payload.updates)
960
+ : u
961
+ ),
962
+ (users) => assoc('users', users, state)
963
+ )(state);
964
+
965
+ case 'DELETE_USER':
966
+ return pipe(
967
+ prop('users'),
968
+ filter((u: User) => u.id !== action.payload),
969
+ (users) => assoc('users', users, state)
970
+ )(state);
971
+
972
+ default:
973
+ return state;
974
+ }
975
+ };
976
+
977
+ // GOOD: Using evolve for nested updates
978
+ const updateNestedState = evolve({
979
+ user: evolve({
980
+ profile: merge({ verified: true }),
981
+ settings: assoc('notifications', false)
982
+ }),
983
+ lastUpdated: () => new Date()
984
+ });
985
+ ```
986
+
987
+ ### Pattern 9: Optimistic Updates
988
+
989
+ **When**: UI updates before server confirmation
990
+ **Where to use**: Create/update/delete operations
991
+
992
+ ```typescript
993
+ import { pipe, pipeAsync, append, filter } from 'fp-pack';
994
+
995
+ // GOOD: Optimistic create with rollback
996
+ const createItemOptimistically = (newItem: Item) => {
997
+ const tempId = `temp-${Date.now()}`;
998
+ const optimisticItem = { ...newItem, id: tempId, pending: true };
999
+
1000
+ // Immediately update UI
1001
+ setItems(pipe(append(optimisticItem)));
1002
+
1003
+ // Then persist
1004
+ return pipeAsync(
1005
+ async () => api.createItem(newItem),
1006
+ (savedItem) => {
1007
+ // Replace temp with real item
1008
+ setItems(
1009
+ pipe(
1010
+ filter((item: Item) => item.id !== tempId),
1011
+ append(savedItem)
1012
+ )
1013
+ );
1014
+ return savedItem;
1015
+ }
1016
+ )().catch((error) => {
1017
+ // Rollback on error
1018
+ setItems(pipe(filter((item: Item) => item.id !== tempId)));
1019
+ throw error;
1020
+ });
1021
+ };
1022
+ ```
1023
+
1024
+ ### Pattern 10: URL/Query Parameter Handling
1025
+
1026
+ **When**: Syncing UI state with URL
1027
+ **Where to use**: Routing, search parameters, filters
1028
+
1029
+ ```typescript
1030
+ import { pipe, pick, mapValues, merge } from 'fp-pack';
1031
+
1032
+ // GOOD: Parse query params to state
1033
+ const parseQueryParams = pipe(
1034
+ (search: string) => new URLSearchParams(search),
1035
+ (params) => ({
1036
+ page: Number(params.get('page')) || 1,
1037
+ query: params.get('q') || '',
1038
+ category: params.get('category') || 'all',
1039
+ sort: params.get('sort') || 'date'
1040
+ })
1041
+ );
1042
+
1043
+ // GOOD: Convert state to query params
1044
+ const stateToQueryParams = pipe(
1045
+ pick(['page', 'query', 'category', 'sort']),
1046
+ (state) => {
1047
+ const params = new URLSearchParams();
1048
+ Object.entries(state).forEach(([key, value]) => {
1049
+ if (value) params.set(key, String(value));
1050
+ });
1051
+ return params.toString();
1052
+ }
1053
+ );
1054
+
1055
+ // Usage in framework router:
1056
+ // const filters = parseQueryParams(location.search);
1057
+ // navigate(`/products?${stateToQueryParams(currentState)}`);
1058
+ ```
1059
+
1060
+ ### Pattern 11: Infinite Scroll / Virtual Lists
1061
+
1062
+ **When**: Large lists with lazy loading, infinite scroll, virtual rendering
1063
+ **Where to use**: Scroll handlers, pagination, large dataset rendering
1064
+
1065
+ ```typescript
1066
+ import { pipe, pipeAsyncSideEffect, when, tap, runPipeResult } from 'fp-pack';
1067
+ import { pipe as streamPipe, filter as streamFilter, take as streamTake, toArray } from 'fp-pack/stream';
1068
+
1069
+ // GOOD: Infinite scroll with pipe - all logic inside
1070
+ const handleScroll = pipe(
1071
+ (e: Event) => e.target as HTMLElement,
1072
+ (el) => ({
1073
+ scrollTop: el.scrollTop,
1074
+ scrollHeight: el.scrollHeight,
1075
+ clientHeight: el.clientHeight,
1076
+ hasMore,
1077
+ isLoading
1078
+ }),
1079
+ // Only load if near bottom, has more data, and not currently loading
1080
+ when(
1081
+ ({ scrollTop, scrollHeight, clientHeight, hasMore, isLoading }) =>
1082
+ scrollTop + clientHeight >= scrollHeight - 100 && hasMore && !isLoading,
1083
+ tap(() => loadNextPage())
1084
+ )
1085
+ );
1086
+
1087
+ // GOOD: Load next page with stream processing
1088
+ const loadNextPagePipeline = pipeAsyncSideEffect(
1089
+ async () => {
1090
+ setIsLoading(true);
1091
+ return fetchItemsFromAPI(currentPage);
1092
+ },
1093
+ // Use stream for lazy processing
1094
+ (items) => streamPipe(
1095
+ streamFilter((item: Item) => item.visible),
1096
+ streamTake(pageSize),
1097
+ toArray
1098
+ )(items),
1099
+ tap((newItems) => setItems(prev => [...prev, ...newItems])),
1100
+ tap(() => setCurrentPage(prev => prev + 1)),
1101
+ tap(() => setIsLoading(false))
1102
+ );
1103
+
1104
+ const loadNextPage = () => runPipeResult(loadNextPagePipeline());
1105
+
1106
+ // GOOD: Virtual scroll - calculate visible range in pipe
1107
+ const getVisibleItems = pipe(
1108
+ (scrollTop: number) => ({
1109
+ itemHeight: 50,
1110
+ viewportHeight: 600,
1111
+ bufferSize: 5,
1112
+ scrollTop
1113
+ }),
1114
+ ({ itemHeight, viewportHeight, bufferSize, scrollTop }) => ({
1115
+ startIndex: Math.floor(scrollTop / itemHeight),
1116
+ endIndex: Math.ceil((scrollTop + viewportHeight) / itemHeight),
1117
+ bufferSize
1118
+ }),
1119
+ ({ startIndex, endIndex, bufferSize }) => ({
1120
+ start: Math.max(0, startIndex - bufferSize),
1121
+ end: endIndex + bufferSize
1122
+ }),
1123
+ ({ start, end }) => allItems.slice(start, end)
1124
+ );
1125
+
1126
+ // GOOD: Lazy load with intersection observer
1127
+ const handleIntersection = pipe(
1128
+ (entries: IntersectionObserverEntry[]) => entries[0],
1129
+ when(
1130
+ (entry) => entry.isIntersecting && hasMoreItems && !isLoadingMore,
1131
+ tap(() => loadMoreItems())
1132
+ )
1133
+ );
1134
+ ```
1135
+
1136
+ ### Pattern 12: Conditional State Updates
1137
+
1138
+ **When**: State updates that depend on conditions
1139
+ **Where to use**: Any state update with business logic
1140
+
1141
+ ```typescript
1142
+ import { pipe, ifElse, when, cond, tap, assoc, prop, append, map, filter, merge } from 'fp-pack';
1143
+
1144
+ // GOOD: Use ifElse instead of if/else
1145
+ const toggleUserStatus = pipe(
1146
+ ifElse(
1147
+ (user: User) => user.status === 'active',
1148
+ assoc('status', 'inactive'),
1149
+ assoc('status', 'active')
1150
+ ),
1151
+ tap((updatedUser) => setUser(updatedUser))
1152
+ );
1153
+
1154
+ // GOOD: Use ifElse for conditional side effects
1155
+ const saveIfValid = pipe(
1156
+ validateForm,
1157
+ ifElse(
1158
+ (result) => result.isValid,
1159
+ pipe(
1160
+ tap((data) => saveToAPI(data)),
1161
+ tap(() => showSuccessMessage())
1162
+ ),
1163
+ tap((result) => setErrors(result.errors))
1164
+ )
1165
+ );
1166
+
1167
+ // GOOD: Use cond for multi-branch logic (instead of switch/if-else chain)
1168
+ const processUserAction = pipe(
1169
+ prop('action'),
1170
+ cond([
1171
+ [
1172
+ (action) => action.type === 'CREATE',
1173
+ pipe(
1174
+ prop('payload'),
1175
+ (user) => append(user),
1176
+ tap((users) => setUsers(users))
1177
+ )
1178
+ ],
1179
+ [
1180
+ (action) => action.type === 'UPDATE',
1181
+ pipe(
1182
+ prop('payload'),
1183
+ ({ id, updates }) => map((u: User) => u.id === id ? merge(u, updates) : u),
1184
+ tap((users) => setUsers(users))
1185
+ )
1186
+ ],
1187
+ [
1188
+ (action) => action.type === 'DELETE',
1189
+ pipe(
1190
+ prop('payload'),
1191
+ (id) => filter((u: User) => u.id !== id),
1192
+ tap((users) => setUsers(users))
1193
+ )
1194
+ ],
1195
+ [
1196
+ () => true, // default case
1197
+ tap(() => console.warn('Unknown action'))
1198
+ ]
1199
+ ])
1200
+ );
1201
+
1202
+ // GOOD: Complex state update with all logic in pipe
1203
+ const updateCartItem = (itemId: string, quantity: number) => pipe(
1204
+ // Get current cart
1205
+ (cart) => cart.items,
1206
+ // Find and update item
1207
+ map((item: CartItem) =>
1208
+ ifElse(
1209
+ () => item.id === itemId,
1210
+ pipe(
1211
+ assoc('quantity', quantity),
1212
+ when(
1213
+ (updated) => updated.quantity <= 0,
1214
+ () => null // Mark for removal
1215
+ )
1216
+ ),
1217
+ () => item
1218
+ )(item)
1219
+ ),
1220
+ // Remove null items (quantity <= 0)
1221
+ filter((item) => item !== null),
1222
+ // Update state
1223
+ tap((items) => setCart({ items })),
1224
+ // Show notification
1225
+ tap((items) => {
1226
+ const item = items.find(i => i.id === itemId);
1227
+ if (item) showNotification(`Updated ${item.name}`);
1228
+ else showNotification('Item removed from cart');
1229
+ })
1230
+ );
1231
+ ```
1232
+
1233
+ ## Library Integration Quick Reference
1234
+
1235
+ This section shows how to integrate fp-pack with popular UI libraries. All examples keep logic **inside pipe chains** using fp-pack control flow functions.
1236
+
1237
+ ### React Ecosystem
1238
+
1239
+ #### React Query / TanStack Query
1240
+
1241
+ ```typescript
1242
+ import { useQuery, useMutation } from '@tanstack/react-query';
1243
+ import { pipe, filter, sortBy, map, tap, when } from 'fp-pack';
1244
+
1245
+ // GOOD: Transform data in select using pipe
1246
+ const { data: activeUsers } = useQuery({
1247
+ queryKey: ['users'],
1248
+ queryFn: fetchUsers,
1249
+ select: pipe(
1250
+ filter((u: User) => u.status === 'active'),
1251
+ sortBy((u) => u.name),
1252
+ map((u) => ({ id: u.id, name: u.name, email: u.email }))
1253
+ )
1254
+ });
1255
+
1256
+ // GOOD: Handle mutations with pipe
1257
+ const mutation = useMutation({
1258
+ mutationFn: createUser,
1259
+ onSuccess: pipe(
1260
+ tap((newUser) => queryClient.invalidateQueries(['users'])),
1261
+ when(
1262
+ (user) => user.isPremium,
1263
+ tap(() => showPremiumWelcome())
1264
+ ),
1265
+ tap(() => navigate('/dashboard'))
1266
+ )
1267
+ });
1268
+
1269
+ // GOOD: Optimistic updates in pipe
1270
+ const updateMutation = useMutation({
1271
+ mutationFn: updateUser,
1272
+ onMutate: pipe(
1273
+ tap(async (newUser) => {
1274
+ await queryClient.cancelQueries(['users']);
1275
+ const previous = queryClient.getQueryData(['users']);
1276
+ queryClient.setQueryData(['users'], pipe(
1277
+ map((u: User) => u.id === newUser.id ? merge(u, newUser) : u)
1278
+ ));
1279
+ return { previous };
1280
+ })
1281
+ ),
1282
+ onError: pipe(
1283
+ tap((err, variables, context) => {
1284
+ if (context?.previous) {
1285
+ queryClient.setQueryData(['users'], context.previous);
1286
+ }
1287
+ })
1288
+ )
1289
+ });
1290
+ ```
1291
+
1292
+ #### Zustand
1293
+
1294
+ ```typescript
1295
+ import create from 'zustand';
1296
+ import { pipe, append, filter, map, merge, ifElse, when, tap, prop, sortBy, assoc } from 'fp-pack';
1297
+
1298
+ // GOOD: All actions use pipe
1299
+ const useStore = create((set, get) => ({
1300
+ users: [],
1301
+
1302
+ addUser: pipe(
1303
+ (user: User) => user,
1304
+ when(
1305
+ (user) => !get().users.some(u => u.id === user.id),
1306
+ tap((user) => set(pipe(
1307
+ prop('users'),
1308
+ append(user),
1309
+ sortBy((u: User) => u.name),
1310
+ (users) => ({ users })
1311
+ )(get())))
1312
+ )
1313
+ ),
1314
+
1315
+ updateUser: (id: string, updates: Partial<User>) => set(pipe(
1316
+ prop('users'),
1317
+ map((u: User) =>
1318
+ ifElse(
1319
+ () => u.id === id,
1320
+ merge(u, updates),
1321
+ () => u
1322
+ )(u)
1323
+ ),
1324
+ (users) => ({ users })
1325
+ )(get())),
1326
+
1327
+ deleteUser: (id: string) => set(pipe(
1328
+ prop('users'),
1329
+ filter((u: User) => u.id !== id),
1330
+ (users) => ({ users })
1331
+ )(get())),
1332
+
1333
+ toggleUserStatus: (id: string) => set(pipe(
1334
+ prop('users'),
1335
+ map((u: User) => u.id === id
1336
+ ? pipe(
1337
+ ifElse(
1338
+ (user) => user.status === 'active',
1339
+ assoc('status', 'inactive'),
1340
+ assoc('status', 'active')
1341
+ )
1342
+ )(u)
1343
+ : u
1344
+ ),
1345
+ (users) => ({ users })
1346
+ )(get()))
1347
+ }));
1348
+ ```
1349
+
1350
+ #### Redux Toolkit
1351
+
1352
+ ```typescript
1353
+ import { createSlice } from '@reduxjs/toolkit';
1354
+ import { pipe, append, filter, map, merge, sortBy, cond, assoc } from 'fp-pack';
1355
+
1356
+ // GOOD: Reducers with pipe - no manual mutations
1357
+ const userSlice = createSlice({
1358
+ name: 'users',
1359
+ initialState: { list: [], loading: false },
1360
+ reducers: {
1361
+ addUser: (state, action) => {
1362
+ state.list = pipe(
1363
+ append(action.payload),
1364
+ sortBy((u: User) => u.createdAt)
1365
+ )(state.list);
1366
+ },
1367
+
1368
+ updateUser: (state, action) => {
1369
+ state.list = pipe(
1370
+ map((u: User) =>
1371
+ u.id === action.payload.id
1372
+ ? merge(u, action.payload.updates)
1373
+ : u
1374
+ )
1375
+ )(state.list);
1376
+ },
1377
+
1378
+ deleteUser: (state, action) => {
1379
+ state.list = pipe(
1380
+ filter((u: User) => u.id !== action.payload)
1381
+ )(state.list);
1382
+ },
1383
+
1384
+ // Complex update with cond
1385
+ processAction: (state, action) => {
1386
+ state.list = pipe(
1387
+ cond([
1388
+ [
1389
+ () => action.type === 'BULK_ACTIVATE',
1390
+ map((u: User) => assoc('status', 'active', u))
1391
+ ],
1392
+ [
1393
+ () => action.type === 'BULK_DELETE',
1394
+ filter((u: User) => !action.payload.ids.includes(u.id))
1395
+ ],
1396
+ [
1397
+ () => true,
1398
+ (users) => users // no change
1399
+ ]
1400
+ ])
1401
+ )(state.list);
1402
+ }
1403
+ }
1404
+ });
1405
+ ```
1406
+
1407
+ #### React Hook Form
1408
+
1409
+ ```typescript
1410
+ import { useForm } from 'react-hook-form';
1411
+ import { pipe, pipeSideEffect, pipeAsyncSideEffect, pick, mapValues, trim, when, tap, SideEffect, runPipeResult } from 'fp-pack';
1412
+
1413
+ // GOOD: Validation with pipeSideEffect
1414
+ const validateFormDataPipeline = pipeSideEffect(
1415
+ pick(['email', 'password', 'name']),
1416
+ mapValues((v) => typeof v === 'string' ? trim(v) : v),
1417
+ (data) => {
1418
+ const errors: any = {};
1419
+ if (!data.email?.includes('@')) errors.email = 'Invalid email';
1420
+ if ((data.password?.length || 0) < 8) errors.password = 'Too short';
1421
+ return Object.keys(errors).length > 0
1422
+ ? SideEffect.of(() => ({ values: {}, errors }), 'VALIDATION_ERROR')
1423
+ : { values: data, errors: {} };
1424
+ }
1425
+ );
1426
+
1427
+ const validateFormData = (values: any) => runPipeResult(validateFormDataPipeline(values));
1428
+
1429
+ const { register, handleSubmit } = useForm({
1430
+ resolver: (values) => validateFormData(values)
1431
+ });
1432
+
1433
+ // GOOD: Submit handler with pipeAsyncSideEffect
1434
+ const onSubmitPipeline = pipeAsyncSideEffect(
1435
+ validateFormData,
1436
+ when(
1437
+ (result) => Object.keys(result.errors).length === 0,
1438
+ pipe(
1439
+ prop('values'),
1440
+ submitToAPI,
1441
+ tap(() => navigate('/success'))
1442
+ )
1443
+ )
1444
+ );
1445
+
1446
+ const onSubmit = (data: any) => runPipeResult(onSubmitPipeline(data));
1447
+ ```
1448
+
1449
+ ### Vue Ecosystem
1450
+
1451
+ #### Pinia
1452
+
1453
+ ```typescript
1454
+ import { defineStore } from 'pinia';
1455
+ import { pipe, append, filter, map, merge, sortBy, when, tap } from 'fp-pack';
1456
+
1457
+ // GOOD: All actions use pipe
1458
+ export const useUserStore = defineStore('user', {
1459
+ state: () => ({ users: [], loading: false }),
1460
+
1461
+ actions: {
1462
+ addUser(user: User) {
1463
+ this.users = pipe(
1464
+ append(user),
1465
+ sortBy((u: User) => u.name),
1466
+ when(
1467
+ (users) => users.length > 100,
1468
+ tap(() => this.showWarning('Many users'))
1469
+ )
1470
+ )(this.users);
1471
+ },
1472
+
1473
+ updateUser(id: string, updates: Partial<User>) {
1474
+ this.users = pipe(
1475
+ map((u: User) => u.id === id ? merge(u, updates) : u)
1476
+ )(this.users);
1477
+ },
1478
+
1479
+ deleteUser(id: string) {
1480
+ this.users = pipe(
1481
+ filter((u: User) => u.id !== id),
1482
+ tap((users) => {
1483
+ if (users.length === 0) this.showEmptyState = true;
1484
+ })
1485
+ )(this.users);
1486
+ }
1487
+ }
1488
+ });
1489
+ ```
1490
+
1491
+ #### VueUse
1492
+
1493
+ ```typescript
1494
+ import { useFetch } from '@vueuse/core';
1495
+ import { pipe, filter, map, sortBy, tap } from 'fp-pack';
1496
+
1497
+ // GOOD: Transform response with pipe
1498
+ const { data } = useFetch('/api/users', {
1499
+ afterFetch: pipe(
1500
+ prop('data'),
1501
+ filter((u: User) => u.verified),
1502
+ sortBy((u) => u.name),
1503
+ map((u) => ({ id: u.id, name: u.name })),
1504
+ tap((users) => console.log(`Loaded ${users.length} users`))
1505
+ )
1506
+ }).json();
1507
+
1508
+ // GOOD: Refetch with condition in pipe
1509
+ const { execute } = useFetch('/api/users');
1510
+
1511
+ const refreshIfNeeded = pipe(
1512
+ (lastUpdate: Date) => Date.now() - lastUpdate.getTime(),
1513
+ when(
1514
+ (diff) => diff > 5 * 60 * 1000, // 5 minutes
1515
+ tap(() => execute())
1516
+ )
1517
+ );
1518
+ ```
1519
+
1520
+ ### State Management Patterns
1521
+
1522
+ All state management libraries benefit from fp-pack's immutable update patterns:
1523
+
1524
+ ```typescript
1525
+ // GOOD: Generic state update pattern (works with any library)
1526
+ const updateState = <T>(
1527
+ state: T,
1528
+ path: string[],
1529
+ updater: (value: any) => any
1530
+ ) => pipe(
1531
+ pathOr(null, path),
1532
+ updater,
1533
+ (newValue) => assocPath(path, newValue, state)
1534
+ )(state);
1535
+
1536
+ // Usage in any framework:
1537
+ // Redux: return updateState(state, ['users', 0, 'name'], toUpper);
1538
+ // Zustand: set(updateState(get(), ['users', 0, 'name'], toUpper));
1539
+ // Pinia: this.$state = updateState(this.$state, ['users', 0, 'name'], toUpper);
1540
+ ```
1541
+
1542
+ ## Framework-Specific Notes
1543
+
1544
+ While the patterns above are framework-agnostic, here's where to apply them:
1545
+
1546
+ ### Reactive/Computed Values
1547
+ - **React**: `useMemo(() => pipe(...)(data), [data])`
1548
+ - **Vue**: `computed(() => pipe(...)(data.value))`
1549
+ - **Svelte**: `$: result = pipe(...)(data)`
1550
+ - **Solid**: `createMemo(() => pipe(...)(data()))`
1551
+
1552
+ ### Event Handlers
1553
+ - **React**: `<button onClick={pipe(...)}>` or `const handler = pipe(...)`
1554
+ - **Vue**: `<button @click="pipe(...)">` or `const handler = pipe(...)`
1555
+ - **Svelte**: `<button on:click={pipe(...)}>` or `const handler = pipe(...)`
1556
+
1557
+ ### Side Effects (API calls, subscriptions)
1558
+ - **React**: `useEffect(() => { pipeAsync(...)() }, [deps])`
1559
+ - **Vue**: `watchEffect(() => pipeAsync(...)())`
1560
+ - **Svelte**: `onMount(() => pipeAsync(...)())`
1561
+
1562
+ ### State Updates
1563
+ - **React**: `setState(pipe(...)(currentState))`
1564
+ - **Vue**: `state.value = pipe(...)(state.value)`
1565
+ - **Svelte**: `$state = pipe(...)($state)`
1566
+
1567
+ All patterns use the same fp-pack functions - only the framework's state/reactive wrapper changes.
1568
+
1569
+ ## Summary
1570
+
1571
+ As an AI coding assistant working with fp-pack:
1572
+
1573
+ 1. **Default to `pipe`** for all data transformations
1574
+ 2. **Switch to `pipeAsync`** when async operations are involved (use `pipeAsyncSideEffect` if SideEffect is in the flow)
1575
+ 3. **Use `stream/*`** for lazy, memory-efficient processing
1576
+ 4. **Handle errors with `SideEffect`** in `pipeSideEffect`/`pipeAsyncSideEffect`, not try-catch
1577
+ 5. **Avoid imperative loops** - use fp-pack's declarative functions
1578
+ 6. **Never suggest monads** - use SideEffect pattern instead
1579
+ 7. **Keep code declarative** - describe what, not how
1580
+ 8. **All logic inside pipe** - never break out of pipe chains for conditionals or loops
1581
+ 9. **Use control flow functions** - `when`, `unless`, `ifElse`, `cond` instead of if/else/switch
1582
+ 10. **Call `runPipeResult` OUTSIDE pipelines** - `runPipeResult` / `matchSideEffect` must be called outside `pipeSideEffect`/`pipeAsyncSideEffect` for proper type safety
1583
+ 11. **Use `isSideEffect` for type narrowing** - get precise types in both success and error branches
1584
+ 12. **Apply use-case patterns** - recognize scenarios (form handling, list processing, etc.) and apply appropriate fp-pack patterns
1585
+ 13. **Framework-agnostic core** - write fp-pack logic independent of UI framework, only wrap at the boundaries
1586
+ 14. **Library integration** - use pipe in select/resolver/action functions of popular libraries (React Query, Zustand, Pinia, etc.)
1587
+
1588
+ ### Key Principles
1589
+
1590
+ **✅ DO: Keep everything in pipe**
1591
+ ```typescript
1592
+ // GOOD: All logic inside pipe
1593
+ const handleSubmitPipeline = pipeAsyncSideEffect(
1594
+ getFormData,
1595
+ validateFields,
1596
+ when(isValid, submitToAPI),
1597
+ unless(isValid, showErrors)
1598
+ );
1599
+
1600
+ const handleSubmit = (form: HTMLFormElement) => runPipeResult(handleSubmitPipeline(form));
1601
+ ```
1602
+
1603
+ **❌ DON'T: Break out of pipe for conditionals**
1604
+ ```typescript
1605
+ // BAD: Breaking pipe for if/else
1606
+ const handleSubmit = pipe(
1607
+ getFormData,
1608
+ validateFields
1609
+ );
1610
+ const result = handleSubmit(form);
1611
+ if (result.isValid) { // ❌ Outside pipe
1612
+ submitToAPI(result);
1613
+ } else {
1614
+ showErrors(result.errors);
1615
+ }
1616
+ ```
1617
+
1618
+ **✅ DO: Use when/cond/ifElse for branching**
1619
+ ```typescript
1620
+ // GOOD: Branching inside pipe
1621
+ const processAction = pipe(
1622
+ cond([
1623
+ [(action) => action.type === 'CREATE', handleCreate],
1624
+ [(action) => action.type === 'UPDATE', handleUpdate],
1625
+ [(action) => action.type === 'DELETE', handleDelete],
1626
+ [() => true, handleDefault]
1627
+ ])
1628
+ );
1629
+ ```
1630
+
1631
+ **❌ DON'T: Use switch/if-else chains**
1632
+ ```typescript
1633
+ // BAD: Imperative branching
1634
+ const processAction = (action) => {
1635
+ switch (action.type) { // ❌ Imperative
1636
+ case 'CREATE': return handleCreate(action);
1637
+ case 'UPDATE': return handleUpdate(action);
1638
+ case 'DELETE': return handleDelete(action);
1639
+ default: return handleDefault(action);
1640
+ }
1641
+ };
1642
+ ```
1643
+
1644
+ Your goal is to write clean, readable, functional code that leverages fp-pack's full potential in real-world UI applications.