@ezez/utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (391) hide show
  1. package/.prettierignore +3 -0
  2. package/.prettierrc.json +1 -0
  3. package/CHANGELOG.md +244 -0
  4. package/LICENSE +21 -0
  5. package/README.md +79 -0
  6. package/babel.config.cjs +6 -0
  7. package/dist/cap.d.ts +3 -0
  8. package/dist/cap.d.ts.map +1 -0
  9. package/dist/cap.js +14 -0
  10. package/dist/cap.js.map +1 -0
  11. package/dist/capitalize.d.ts +3 -0
  12. package/dist/capitalize.d.ts.map +1 -0
  13. package/dist/capitalize.js +9 -0
  14. package/dist/capitalize.js.map +1 -0
  15. package/dist/coalesce.d.ts +3 -0
  16. package/dist/coalesce.d.ts.map +1 -0
  17. package/dist/coalesce.js +14 -0
  18. package/dist/coalesce.js.map +1 -0
  19. package/dist/ensureArray.d.ts +3 -0
  20. package/dist/ensureArray.d.ts.map +1 -0
  21. package/dist/ensureArray.js +11 -0
  22. package/dist/ensureArray.js.map +1 -0
  23. package/dist/ensureError.d.ts +3 -0
  24. package/dist/ensureError.d.ts.map +1 -0
  25. package/dist/ensureError.js +11 -0
  26. package/dist/ensureError.js.map +1 -0
  27. package/dist/get.d.ts +7 -0
  28. package/dist/get.d.ts.map +1 -0
  29. package/dist/get.js +19 -0
  30. package/dist/get.js.map +1 -0
  31. package/dist/getMultiple.d.ts +7 -0
  32. package/dist/getMultiple.d.ts.map +1 -0
  33. package/dist/getMultiple.js +18 -0
  34. package/dist/getMultiple.js.map +1 -0
  35. package/dist/index.d.ts +33 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +49 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/insertSeparator.d.ts +3 -0
  40. package/dist/insertSeparator.d.ts.map +1 -0
  41. package/dist/insertSeparator.js +18 -0
  42. package/dist/insertSeparator.js.map +1 -0
  43. package/dist/isEmpty.d.ts +3 -0
  44. package/dist/isEmpty.d.ts.map +1 -0
  45. package/dist/isEmpty.js +26 -0
  46. package/dist/isEmpty.js.map +1 -0
  47. package/dist/isPlainObject.d.ts +3 -0
  48. package/dist/isPlainObject.d.ts.map +1 -0
  49. package/dist/isPlainObject.js +10 -0
  50. package/dist/isPlainObject.js.map +1 -0
  51. package/dist/last.d.ts +3 -0
  52. package/dist/last.d.ts.map +1 -0
  53. package/dist/last.js +6 -0
  54. package/dist/last.js.map +1 -0
  55. package/dist/mapAsync.d.ts +3 -0
  56. package/dist/mapAsync.d.ts.map +1 -0
  57. package/dist/mapAsync.js +22 -0
  58. package/dist/mapAsync.js.map +1 -0
  59. package/dist/mapValues.d.ts +6 -0
  60. package/dist/mapValues.d.ts.map +1 -0
  61. package/dist/mapValues.js +18 -0
  62. package/dist/mapValues.js.map +1 -0
  63. package/dist/match.d.ts +8 -0
  64. package/dist/match.d.ts.map +1 -0
  65. package/dist/match.js +20 -0
  66. package/dist/match.js.map +1 -0
  67. package/dist/merge.d.ts +15 -0
  68. package/dist/merge.d.ts.map +1 -0
  69. package/dist/merge.js +28 -0
  70. package/dist/merge.js.map +1 -0
  71. package/dist/mostFrequent.d.ts +3 -0
  72. package/dist/mostFrequent.d.ts.map +1 -0
  73. package/dist/mostFrequent.js +21 -0
  74. package/dist/mostFrequent.js.map +1 -0
  75. package/dist/noop.d.ts +3 -0
  76. package/dist/noop.d.ts.map +1 -0
  77. package/dist/noop.js +6 -0
  78. package/dist/noop.js.map +1 -0
  79. package/dist/omit.d.ts +5 -0
  80. package/dist/omit.d.ts.map +1 -0
  81. package/dist/omit.js +21 -0
  82. package/dist/omit.js.map +1 -0
  83. package/dist/package.json +1 -0
  84. package/dist/pick.d.ts +5 -0
  85. package/dist/pick.d.ts.map +1 -0
  86. package/dist/pick.js +20 -0
  87. package/dist/pick.js.map +1 -0
  88. package/dist/pull.d.ts +3 -0
  89. package/dist/pull.d.ts.map +1 -0
  90. package/dist/pull.js +14 -0
  91. package/dist/pull.js.map +1 -0
  92. package/dist/remove.d.ts +3 -0
  93. package/dist/remove.d.ts.map +1 -0
  94. package/dist/remove.js +18 -0
  95. package/dist/remove.js.map +1 -0
  96. package/dist/rethrow.d.ts +3 -0
  97. package/dist/rethrow.d.ts.map +1 -0
  98. package/dist/rethrow.js +8 -0
  99. package/dist/rethrow.js.map +1 -0
  100. package/dist/scale.d.ts +3 -0
  101. package/dist/scale.d.ts.map +1 -0
  102. package/dist/scale.js +8 -0
  103. package/dist/scale.js.map +1 -0
  104. package/dist/seq.d.ts +8 -0
  105. package/dist/seq.d.ts.map +1 -0
  106. package/dist/seq.js +45 -0
  107. package/dist/seq.js.map +1 -0
  108. package/dist/set.d.ts +7 -0
  109. package/dist/set.d.ts.map +1 -0
  110. package/dist/set.js +25 -0
  111. package/dist/set.js.map +1 -0
  112. package/dist/setImmutable.d.ts +8 -0
  113. package/dist/setImmutable.d.ts.map +1 -0
  114. package/dist/setImmutable.js +59 -0
  115. package/dist/setImmutable.js.map +1 -0
  116. package/dist/sortBy.d.ts +3 -0
  117. package/dist/sortBy.d.ts.map +1 -0
  118. package/dist/sortBy.js +15 -0
  119. package/dist/sortBy.js.map +1 -0
  120. package/dist/throttle.d.ts +13 -0
  121. package/dist/throttle.d.ts.map +1 -0
  122. package/dist/throttle.js +82 -0
  123. package/dist/throttle.js.map +1 -0
  124. package/dist/truthy.d.ts +3 -0
  125. package/dist/truthy.d.ts.map +1 -0
  126. package/dist/truthy.js +8 -0
  127. package/dist/truthy.js.map +1 -0
  128. package/dist/wait.d.ts +3 -0
  129. package/dist/wait.d.ts.map +1 -0
  130. package/dist/wait.js +10 -0
  131. package/dist/wait.js.map +1 -0
  132. package/dist/waitFor.d.ts +3 -0
  133. package/dist/waitFor.d.ts.map +1 -0
  134. package/dist/waitFor.js +34 -0
  135. package/dist/waitFor.js.map +1 -0
  136. package/dist/waitSync.d.ts +3 -0
  137. package/dist/waitSync.d.ts.map +1 -0
  138. package/dist/waitSync.js +9 -0
  139. package/dist/waitSync.js.map +1 -0
  140. package/docs/.nojekyll +1 -0
  141. package/docs/assets/highlight.css +78 -0
  142. package/docs/assets/main.js +58 -0
  143. package/docs/assets/pages.css +14 -0
  144. package/docs/assets/search.js +1 -0
  145. package/docs/assets/style.css +1280 -0
  146. package/docs/functions/cap.html +116 -0
  147. package/docs/functions/capitalize.html +121 -0
  148. package/docs/functions/coalesce.html +127 -0
  149. package/docs/functions/ensureArray.html +114 -0
  150. package/docs/functions/ensureError.html +110 -0
  151. package/docs/functions/get.html +138 -0
  152. package/docs/functions/getMultiple.html +129 -0
  153. package/docs/functions/insertSeparator.html +123 -0
  154. package/docs/functions/isEmpty.html +139 -0
  155. package/docs/functions/isPlainObject.html +108 -0
  156. package/docs/functions/last.html +126 -0
  157. package/docs/functions/mapAsync.html +144 -0
  158. package/docs/functions/mapValues.html +130 -0
  159. package/docs/functions/match.html +122 -0
  160. package/docs/functions/merge.html +464 -0
  161. package/docs/functions/mostFrequent.html +111 -0
  162. package/docs/functions/noop.html +101 -0
  163. package/docs/functions/omit.html +129 -0
  164. package/docs/functions/pick.html +129 -0
  165. package/docs/functions/pull.html +113 -0
  166. package/docs/functions/remove.html +129 -0
  167. package/docs/functions/rethrow.html +106 -0
  168. package/docs/functions/scale.html +117 -0
  169. package/docs/functions/seq.html +128 -0
  170. package/docs/functions/seqEarlyBreak.html +126 -0
  171. package/docs/functions/set.html +138 -0
  172. package/docs/functions/setImmutable.html +137 -0
  173. package/docs/functions/sortBy.html +135 -0
  174. package/docs/functions/throttle.html +131 -0
  175. package/docs/functions/truthy.html +118 -0
  176. package/docs/functions/wait.html +109 -0
  177. package/docs/functions/waitFor.html +130 -0
  178. package/docs/functions/waitSync.html +109 -0
  179. package/docs/index.html +182 -0
  180. package/docs/interfaces/GetMultipleSource.html +106 -0
  181. package/docs/interfaces/GetSource.html +106 -0
  182. package/docs/interfaces/SetImmutableSource.html +106 -0
  183. package/docs/interfaces/SetSource.html +106 -0
  184. package/docs/interfaces/ThrottleOptions.html +80 -0
  185. package/docs/interfaces/ThrottledFunctionExtras.html +96 -0
  186. package/docs/modules.html +152 -0
  187. package/docs/pages/Introduction.html +94 -0
  188. package/docs/types/MapValuesFn.html +134 -0
  189. package/docs/types/MatchCallback.html +113 -0
  190. package/docs/types/SeqEarlyBreaker.html +114 -0
  191. package/docs/types/SeqFn.html +112 -0
  192. package/docs/types/SeqFunctions.html +105 -0
  193. package/docs/types/SetImmutablePath.html +99 -0
  194. package/docs/types/ThrottledFunction.html +113 -0
  195. package/docs/variables/mapValuesUNSET.html +101 -0
  196. package/docs/variables/mergeUNSET.html +103 -0
  197. package/esm/cap.d.ts +3 -0
  198. package/esm/cap.d.ts.map +1 -0
  199. package/esm/cap.js +11 -0
  200. package/esm/cap.js.map +1 -0
  201. package/esm/capitalize.d.ts +3 -0
  202. package/esm/capitalize.d.ts.map +1 -0
  203. package/esm/capitalize.js +6 -0
  204. package/esm/capitalize.js.map +1 -0
  205. package/esm/coalesce.d.ts +3 -0
  206. package/esm/coalesce.d.ts.map +1 -0
  207. package/esm/coalesce.js +11 -0
  208. package/esm/coalesce.js.map +1 -0
  209. package/esm/ensureArray.d.ts +3 -0
  210. package/esm/ensureArray.d.ts.map +1 -0
  211. package/esm/ensureArray.js +8 -0
  212. package/esm/ensureArray.js.map +1 -0
  213. package/esm/ensureError.d.ts +3 -0
  214. package/esm/ensureError.d.ts.map +1 -0
  215. package/esm/ensureError.js +8 -0
  216. package/esm/ensureError.js.map +1 -0
  217. package/esm/get.d.ts +7 -0
  218. package/esm/get.d.ts.map +1 -0
  219. package/esm/get.js +16 -0
  220. package/esm/get.js.map +1 -0
  221. package/esm/getMultiple.d.ts +7 -0
  222. package/esm/getMultiple.d.ts.map +1 -0
  223. package/esm/getMultiple.js +15 -0
  224. package/esm/getMultiple.js.map +1 -0
  225. package/esm/index.d.ts +33 -0
  226. package/esm/index.d.ts.map +1 -0
  227. package/esm/index.js +33 -0
  228. package/esm/index.js.map +1 -0
  229. package/esm/insertSeparator.d.ts +3 -0
  230. package/esm/insertSeparator.d.ts.map +1 -0
  231. package/esm/insertSeparator.js +15 -0
  232. package/esm/insertSeparator.js.map +1 -0
  233. package/esm/isEmpty.d.ts +3 -0
  234. package/esm/isEmpty.d.ts.map +1 -0
  235. package/esm/isEmpty.js +23 -0
  236. package/esm/isEmpty.js.map +1 -0
  237. package/esm/isPlainObject.d.ts +3 -0
  238. package/esm/isPlainObject.d.ts.map +1 -0
  239. package/esm/isPlainObject.js +7 -0
  240. package/esm/isPlainObject.js.map +1 -0
  241. package/esm/last.d.ts +3 -0
  242. package/esm/last.d.ts.map +1 -0
  243. package/esm/last.js +3 -0
  244. package/esm/last.js.map +1 -0
  245. package/esm/mapAsync.d.ts +3 -0
  246. package/esm/mapAsync.d.ts.map +1 -0
  247. package/esm/mapAsync.js +19 -0
  248. package/esm/mapAsync.js.map +1 -0
  249. package/esm/mapValues.d.ts +6 -0
  250. package/esm/mapValues.d.ts.map +1 -0
  251. package/esm/mapValues.js +14 -0
  252. package/esm/mapValues.js.map +1 -0
  253. package/esm/match.d.ts +8 -0
  254. package/esm/match.d.ts.map +1 -0
  255. package/esm/match.js +17 -0
  256. package/esm/match.js.map +1 -0
  257. package/esm/merge.d.ts +15 -0
  258. package/esm/merge.d.ts.map +1 -0
  259. package/esm/merge.js +24 -0
  260. package/esm/merge.js.map +1 -0
  261. package/esm/mostFrequent.d.ts +3 -0
  262. package/esm/mostFrequent.d.ts.map +1 -0
  263. package/esm/mostFrequent.js +18 -0
  264. package/esm/mostFrequent.js.map +1 -0
  265. package/esm/noop.d.ts +3 -0
  266. package/esm/noop.d.ts.map +1 -0
  267. package/esm/noop.js +3 -0
  268. package/esm/noop.js.map +1 -0
  269. package/esm/omit.d.ts +5 -0
  270. package/esm/omit.d.ts.map +1 -0
  271. package/esm/omit.js +20 -0
  272. package/esm/omit.js.map +1 -0
  273. package/esm/pick.d.ts +5 -0
  274. package/esm/pick.d.ts.map +1 -0
  275. package/esm/pick.js +17 -0
  276. package/esm/pick.js.map +1 -0
  277. package/esm/pull.d.ts +3 -0
  278. package/esm/pull.d.ts.map +1 -0
  279. package/esm/pull.js +11 -0
  280. package/esm/pull.js.map +1 -0
  281. package/esm/remove.d.ts +3 -0
  282. package/esm/remove.d.ts.map +1 -0
  283. package/esm/remove.js +15 -0
  284. package/esm/remove.js.map +1 -0
  285. package/esm/rethrow.d.ts +3 -0
  286. package/esm/rethrow.d.ts.map +1 -0
  287. package/esm/rethrow.js +5 -0
  288. package/esm/rethrow.js.map +1 -0
  289. package/esm/scale.d.ts +3 -0
  290. package/esm/scale.d.ts.map +1 -0
  291. package/esm/scale.js +5 -0
  292. package/esm/scale.js.map +1 -0
  293. package/esm/seq.d.ts +8 -0
  294. package/esm/seq.d.ts.map +1 -0
  295. package/esm/seq.js +41 -0
  296. package/esm/seq.js.map +1 -0
  297. package/esm/set.d.ts +7 -0
  298. package/esm/set.d.ts.map +1 -0
  299. package/esm/set.js +22 -0
  300. package/esm/set.js.map +1 -0
  301. package/esm/setImmutable.d.ts +8 -0
  302. package/esm/setImmutable.d.ts.map +1 -0
  303. package/esm/setImmutable.js +56 -0
  304. package/esm/setImmutable.js.map +1 -0
  305. package/esm/sortBy.d.ts +3 -0
  306. package/esm/sortBy.d.ts.map +1 -0
  307. package/esm/sortBy.js +12 -0
  308. package/esm/sortBy.js.map +1 -0
  309. package/esm/throttle.d.ts +13 -0
  310. package/esm/throttle.d.ts.map +1 -0
  311. package/esm/throttle.js +79 -0
  312. package/esm/throttle.js.map +1 -0
  313. package/esm/truthy.d.ts +3 -0
  314. package/esm/truthy.d.ts.map +1 -0
  315. package/esm/truthy.js +5 -0
  316. package/esm/truthy.js.map +1 -0
  317. package/esm/wait.d.ts +3 -0
  318. package/esm/wait.d.ts.map +1 -0
  319. package/esm/wait.js +7 -0
  320. package/esm/wait.js.map +1 -0
  321. package/esm/waitFor.d.ts +3 -0
  322. package/esm/waitFor.d.ts.map +1 -0
  323. package/esm/waitFor.js +31 -0
  324. package/esm/waitFor.js.map +1 -0
  325. package/esm/waitSync.d.ts +3 -0
  326. package/esm/waitSync.d.ts.map +1 -0
  327. package/esm/waitSync.js +6 -0
  328. package/esm/waitSync.js.map +1 -0
  329. package/package.json +75 -0
  330. package/src/cap.spec.ts +36 -0
  331. package/src/cap.ts +19 -0
  332. package/src/capitalize.spec.ts +18 -0
  333. package/src/capitalize.ts +16 -0
  334. package/src/coalesce.spec.ts +23 -0
  335. package/src/coalesce.ts +21 -0
  336. package/src/ensureArray.spec.ts +87 -0
  337. package/src/ensureArray.ts +13 -0
  338. package/src/ensureError.spec.ts +29 -0
  339. package/src/ensureError.ts +15 -0
  340. package/src/get.spec.ts +183 -0
  341. package/src/get.ts +53 -0
  342. package/src/getMultiple.spec.ts +25 -0
  343. package/src/getMultiple.ts +47 -0
  344. package/src/index.ts +33 -0
  345. package/src/insertSeparator.spec.ts +29 -0
  346. package/src/insertSeparator.ts +22 -0
  347. package/src/isEmpty.spec.ts +130 -0
  348. package/src/isEmpty.ts +50 -0
  349. package/src/isPlainObject.spec.ts +42 -0
  350. package/src/isPlainObject.ts +18 -0
  351. package/src/last.spec.ts +88 -0
  352. package/src/last.ts +12 -0
  353. package/src/mapAsync.spec.ts +39 -0
  354. package/src/mapAsync.ts +41 -0
  355. package/src/mapValues.spec.ts +178 -0
  356. package/src/mapValues.ts +57 -0
  357. package/src/match.spec.ts +11 -0
  358. package/src/match.ts +30 -0
  359. package/src/merge.spec.ts +69 -0
  360. package/src/merge.ts +58 -0
  361. package/src/mostFrequent.spec.ts +35 -0
  362. package/src/mostFrequent.ts +27 -0
  363. package/src/noop.ts +8 -0
  364. package/src/omit.spec.ts +181 -0
  365. package/src/omit.ts +43 -0
  366. package/src/pick.spec.ts +168 -0
  367. package/src/pick.ts +39 -0
  368. package/src/pull.spec.ts +54 -0
  369. package/src/pull.ts +18 -0
  370. package/src/remove.spec.ts +63 -0
  371. package/src/remove.ts +26 -0
  372. package/src/rethrow.ts +12 -0
  373. package/src/scale.ts +18 -0
  374. package/src/seq.spec.ts +157 -0
  375. package/src/seq.ts +104 -0
  376. package/src/set.spec.ts +348 -0
  377. package/src/set.ts +59 -0
  378. package/src/setImmutable.spec.ts +241 -0
  379. package/src/setImmutable.ts +104 -0
  380. package/src/sortBy.spec.ts +56 -0
  381. package/src/sortBy.ts +24 -0
  382. package/src/throttle.spec.ts +136 -0
  383. package/src/throttle.ts +153 -0
  384. package/src/truthy.spec.ts +21 -0
  385. package/src/truthy.ts +13 -0
  386. package/src/wait.spec.ts +21 -0
  387. package/src/wait.ts +14 -0
  388. package/src/waitFor.ts +47 -0
  389. package/src/waitSync.spec.ts +21 -0
  390. package/src/waitSync.ts +14 -0
  391. package/tutorials/Introduction.md +1 -0
@@ -0,0 +1,157 @@
1
+ import { seq, seqEarlyBreak } from "./seq.js";
2
+
3
+ describe("seq", () => {
4
+ // @TODO more tests
5
+
6
+ it("calls methods one by one", async () => {
7
+ let firstCalled = false,
8
+ secondCalled = false;
9
+ const list = [
10
+ () => {
11
+ firstCalled = true;
12
+ return Promise.resolve(1);
13
+ },
14
+ () => {
15
+ secondCalled = true;
16
+ return Promise.resolve(2);
17
+ },
18
+ ];
19
+
20
+ const res = await seq(list);
21
+ res.must.equal(1);
22
+ firstCalled.must.equal(true);
23
+ secondCalled.must.equal(false);
24
+ });
25
+
26
+ it("returns first resolved value", async () => {
27
+ let firstCalled = false,
28
+ secondCalled = false;
29
+ const list = [
30
+ () => {
31
+ firstCalled = true;
32
+ return Promise.reject(new Error("Nope"));
33
+ },
34
+ () => {
35
+ secondCalled = true;
36
+ return Promise.resolve(2);
37
+ },
38
+ ];
39
+
40
+ const res = await seq(list);
41
+ res.must.equal(2);
42
+ firstCalled.must.equal(true);
43
+ secondCalled.must.equal(true);
44
+ });
45
+
46
+ it("works with non-async functions", async () => {
47
+ let firstCalled = false,
48
+ secondCalled = false;
49
+ const list = [
50
+ () => {
51
+ firstCalled = true;
52
+ throw new Error("Nope");
53
+ },
54
+ () => {
55
+ secondCalled = true;
56
+ return 2;
57
+ },
58
+ ];
59
+
60
+ const res = await seq(list);
61
+ res.must.equal(2);
62
+ firstCalled.must.equal(true);
63
+ secondCalled.must.equal(true);
64
+ });
65
+
66
+ it("rejects if everything rejects", async () => {
67
+ const list = [
68
+ () => {
69
+ throw new Error("Nope");
70
+ },
71
+ () => {
72
+ throw new Error("Also nope");
73
+ },
74
+ ];
75
+
76
+ let caught = false;
77
+ try {
78
+ await seq(list);
79
+ }
80
+ catch (e: unknown) {
81
+ caught = true;
82
+ e.must.be.instanceof(Error);
83
+ if (e instanceof Error) {
84
+ e.message.must.equal("Every function had thrown.");
85
+ const errors = e.details?.errors as Error[];
86
+ errors.must.be.an.array();
87
+ errors.must.have.length(2);
88
+ errors[0].must.be.instanceof(Error);
89
+ errors[0].message.must.equal("Nope");
90
+ errors[1].must.be.instanceof(Error);
91
+ errors[1].message.must.equal("Also nope");
92
+ }
93
+ }
94
+
95
+ caught.must.be.true();
96
+ });
97
+
98
+ it("rejects if no functions given", async () => {
99
+ let caught = false;
100
+ try {
101
+ await seq();
102
+ }
103
+ catch (e: unknown) {
104
+ caught = true;
105
+ e.must.be.instanceof(TypeError);
106
+ if (e instanceof TypeError) {
107
+ e.message.must.equal("At least one function must be provided.");
108
+ }
109
+ }
110
+
111
+ caught.must.be.true();
112
+ });
113
+
114
+ it("allows to break early on rejection", async () => {
115
+ let firstCalled = false,
116
+ secondCalled = false,
117
+ thirdCalled = false;
118
+
119
+ const list = [
120
+ () => {
121
+ firstCalled = true;
122
+ // eslint-disable-next-line prefer-promise-reject-errors
123
+ return Promise.reject(1);
124
+ },
125
+ () => {
126
+ secondCalled = true;
127
+ // eslint-disable-next-line prefer-promise-reject-errors
128
+ return Promise.reject(2);
129
+ },
130
+ () => {
131
+ thirdCalled = true;
132
+ // eslint-disable-next-line prefer-promise-reject-errors
133
+ return Promise.reject(3);
134
+ },
135
+ ];
136
+
137
+ let caught = false;
138
+ try {
139
+ await seqEarlyBreak((e: unknown) => {
140
+ if (e === 2) {
141
+ return true;
142
+ }
143
+ return false;
144
+ }, list);
145
+ }
146
+ catch (e: unknown) {
147
+ caught = true;
148
+ e.must.be.equal(2);
149
+
150
+ firstCalled.must.be.true();
151
+ secondCalled.must.be.true();
152
+ thirdCalled.must.be.false();
153
+ }
154
+
155
+ caught.must.be.true();
156
+ });
157
+ });
package/src/seq.ts ADDED
@@ -0,0 +1,104 @@
1
+ const fail = function() {
2
+ return Promise.reject(new TypeError("At least one function must be provided."));
3
+ };
4
+
5
+ /**
6
+ * Function to be called.
7
+ *
8
+ * @see {@link seq}
9
+ */
10
+ type Fn<T> = () => T;
11
+
12
+ /**
13
+ * Functions to call sequentially.
14
+ * Either an array of functions or a list of functions.
15
+ *
16
+ * @see {@link seq}
17
+ */
18
+ type Functions<T> = Fn<T>[] | [Fn<T>[]];
19
+
20
+ /**
21
+ * A function that decides if the sequential run should be early stopped.
22
+ *
23
+ * @param {*} e - Value thrown by the function (usually Error).
24
+ * @see {@link seq}
25
+ */
26
+ type EarlyBreaker = (e: unknown) => boolean;
27
+
28
+ const run = <T>(list: Fn<T>[], earlyBreaker?: EarlyBreaker): Promise<T> => {
29
+ if (!list.length) {
30
+ return fail();
31
+ }
32
+
33
+ const promises = [...list];
34
+
35
+ return new Promise((resolve, reject) => {
36
+ let promise: Promise<T | undefined> = Promise.resolve(undefined);
37
+
38
+ const errors: Error[] = [];
39
+
40
+ const doTry = function(error?: Error) {
41
+ if (error !== undefined && earlyBreaker && earlyBreaker(error)) {
42
+ reject(error);
43
+ return;
44
+ }
45
+
46
+ if (error) {
47
+ errors.push(error);
48
+ }
49
+ const fn = promises.shift();
50
+ if (typeof fn !== "function") {
51
+ const finalError = new Error("Every function had thrown.");
52
+ // @ts-expect-error More details on error object are wanted
53
+ finalError.details = { errors };
54
+ reject(finalError);
55
+ }
56
+ promise = (promise.then(fn).then(resolve, doTry)) as unknown as Promise<T>;
57
+ };
58
+
59
+ doTry();
60
+ });
61
+ };
62
+
63
+ /**
64
+ * The same as {@link seq} but accepts a function that gets run after each error and using that erro decides if continue
65
+ * to try next functions.
66
+ *
67
+ * @example seqEarlyBreak(
68
+ * (e) => e.message === "important error",
69
+ * () => { throw new Error("important error") }, () => 2, () => 3
70
+ * ) // throws given important error, does not run next functions
71
+ * @param {EarlyBreaker} earlyBreaker - function that decides about early breaking the sequential run
72
+ * @param {...Functions<*>[]} args - functions to run, you can either pass them as many arguments or just single
73
+ * arguments with array
74
+ * @returns {*} - whatever gets returned from given functions
75
+ */
76
+ const seqEarlyBreak = <T>(earlyBreaker: EarlyBreaker | undefined, ...args: Functions<T>) => {
77
+ if (args.length === 1) {
78
+ return run(Array.isArray(args[0]) ? args[0] : [args[0]], earlyBreaker);
79
+ }
80
+ return run(args as Fn<T>[], earlyBreaker);
81
+ };
82
+
83
+ /**
84
+ * Runs given functions sequentially one by one, until any returns value. Supports async functions. Throws with new
85
+ * Error when every function throws.
86
+ *
87
+ * @example seq(() => 1, () => 2, () => 3) // returns 1
88
+ * @example seq(() => { throw new Error("1") }, () => 2, () => 3) // returns 2
89
+ * @example seq(() => { throw new Error("1") }, () => { throw new Error("2") } }) // throws
90
+ * @param {...Functions<*>[]} fns - functions to run, you can either pass them as many arguments or just single
91
+ * arguments with array
92
+ * @returns {*} - whatever gets returned from given functions
93
+ */
94
+ const seq = <T>(...fns: Functions<T>) => {
95
+ return seqEarlyBreak(undefined, ...fns);
96
+ };
97
+
98
+ export { seq, seqEarlyBreak };
99
+
100
+ export type {
101
+ EarlyBreaker as SeqEarlyBreaker,
102
+ Functions as SeqFunctions,
103
+ Fn as SeqFn,
104
+ };
@@ -0,0 +1,348 @@
1
+ import { set as _set } from "lodash";
2
+
3
+ import { set } from "./set.js";
4
+
5
+ describe("set", () => {
6
+ it("replaces existing value, behaves like lodash", () => {
7
+ lodash: {
8
+ const obj = {
9
+ title: "Hello",
10
+ };
11
+ _set(obj, "title", "World");
12
+ obj.title.must.equal("World");
13
+ }
14
+
15
+ bottom: {
16
+ const obj = {
17
+ title: "Hello",
18
+ };
19
+ set(obj, "title", "World");
20
+ obj.title.must.equal("World");
21
+ }
22
+ });
23
+
24
+ it("adds new value, behaves like lodash", () => {
25
+ lodash: {
26
+ const obj = {
27
+ title: "Hello",
28
+ };
29
+ _set(obj, "name", "World");
30
+ obj.title.must.equal("Hello");
31
+ obj.name.must.equal("World");
32
+ }
33
+
34
+ bottom: {
35
+ const obj = {
36
+ title: "Hello",
37
+ };
38
+ set(obj, "name", "World");
39
+ obj.title.must.equal("Hello");
40
+ obj.name.must.equal("World");
41
+ }
42
+ });
43
+
44
+ it("allows deep set, behaves like lodash", () => {
45
+ lodash: {
46
+ const obj = {
47
+ data: {
48
+ title: "Bye",
49
+ },
50
+ };
51
+
52
+ const dataBefore = obj.data;
53
+
54
+ _set(obj, "data.title", "Hi");
55
+
56
+ obj.data.title.must.equal("Hi");
57
+ obj.data.must.equal(dataBefore);
58
+ }
59
+
60
+ bottom: {
61
+ const obj = {
62
+ data: {
63
+ title: "Bye",
64
+ },
65
+ };
66
+
67
+ const dataBefore = obj.data;
68
+
69
+ set(obj, "data.title", "Hi");
70
+
71
+ obj.data.title.must.equal("Hi");
72
+ obj.data.must.equal(dataBefore);
73
+ }
74
+ });
75
+
76
+ it("adds objects if not existing on the way, behaves like lodash", () => {
77
+ lodash1: {
78
+ const obj = {
79
+ data: null,
80
+ };
81
+ _set(obj, "data.title", "Hi");
82
+ obj.data.must.be.an.object();
83
+ obj.data.title.must.equal("Hi");
84
+ }
85
+
86
+ lodash2: {
87
+ const obj = {};
88
+ _set(obj, "data.title.short", "Hi");
89
+ obj.data.must.be.an.object();
90
+ obj.data.title.must.be.an.object();
91
+ obj.data.title.short.must.equal("Hi");
92
+ }
93
+
94
+ bottom1: {
95
+ const obj = {
96
+ data: null,
97
+ };
98
+ set(obj, "data.title", "Hi");
99
+ obj.data.must.be.an.object();
100
+ obj.data.title.must.equal("Hi");
101
+ }
102
+
103
+ bottom2: {
104
+ const obj = {};
105
+ set(obj, "data.title.short", "Hi");
106
+ obj.data.must.be.an.object();
107
+ obj.data.title.must.be.an.object();
108
+ obj.data.title.short.must.equal("Hi");
109
+ }
110
+ });
111
+
112
+ it("replaces primitives with objects on the way, behaves like lodash", () => {
113
+ lodash1: {
114
+ const obj = {
115
+ data: true,
116
+ };
117
+ _set(obj, "data.title.short", "Hi");
118
+ obj.data.must.be.an.object();
119
+ obj.data.title.must.be.an.object();
120
+ obj.data.title.short.must.equal("Hi");
121
+ }
122
+
123
+ lodash2: {
124
+ const obj = {
125
+ data: "empty",
126
+ };
127
+ _set(obj, "data.title.short", "Hi");
128
+ obj.data.must.be.an.object();
129
+ obj.data.title.must.be.an.object();
130
+ obj.data.title.short.must.equal("Hi");
131
+ }
132
+
133
+ bottom1: {
134
+ const obj = {
135
+ data: true,
136
+ };
137
+ set(obj, "data.title.short", "Hi");
138
+ obj.data.must.be.an.object();
139
+ obj.data.title.must.be.an.object();
140
+ obj.data.title.short.must.equal("Hi");
141
+ }
142
+
143
+ bottom2: {
144
+ const obj = {
145
+ data: "empty",
146
+ };
147
+ set(obj, "data.title.short", "Hi");
148
+ obj.data.must.be.an.object();
149
+ obj.data.title.must.be.an.object();
150
+ obj.data.title.short.must.equal("Hi");
151
+ }
152
+ });
153
+
154
+ it("doesn't crash on nulls as source, but returns new object, behaves NOT like lodash", () => {
155
+ lodash: {
156
+ const obj = null;
157
+ (() => {
158
+ _set(obj, "data.title.short", "Hi");
159
+ }).must.not.throw();
160
+
161
+ const newObj = _set(obj, "data.title.short", "Hi");
162
+ (newObj === null).must.be.true();
163
+ }
164
+
165
+ bottom: {
166
+ const obj = null;
167
+ (() => {
168
+ set(obj, "data.title.short", "Hi");
169
+ }).must.not.throw();
170
+
171
+ const newObj = set(obj, "data.title.short", "Hi");
172
+ newObj.must.be.an.object();
173
+ newObj.data.title.short.must.equal("Hi");
174
+ }
175
+ });
176
+
177
+ it("doesn't crash on primitives as source", () => {
178
+ lodash1: {
179
+ const obj = "hello";
180
+ (() => {
181
+ _set(obj, "data.title.short", "Hi");
182
+ }).must.not.throw();
183
+
184
+ const newObj = _set(obj, "data.title.short", "Hi");
185
+ newObj.must.equal("hello");
186
+ }
187
+
188
+ lodash2: {
189
+ const obj = 88;
190
+ (() => {
191
+ _set(obj, "data.title.short", "Hi");
192
+ }).must.not.throw();
193
+
194
+ const newObj = _set(obj, "data.title.short", "Hi");
195
+ newObj.must.equal(88);
196
+ }
197
+
198
+ bottom1: {
199
+ const obj = "hello";
200
+ (() => {
201
+ set(obj, "data.title.short", "Hi");
202
+ }).must.not.throw();
203
+
204
+ const newObj = set(obj, "data.title.short", "Hi");
205
+ newObj.must.be.an.object();
206
+ newObj.data.title.short.must.equal("Hi");
207
+ }
208
+
209
+ bottom2: {
210
+ const obj = 88;
211
+ (() => {
212
+ set(obj, "data.title.short", "Hi");
213
+ }).must.not.throw();
214
+
215
+ const newObj = set(obj, "data.title.short", "Hi");
216
+ newObj.must.be.an.object();
217
+ newObj.data.title.short.must.equal("Hi");
218
+ }
219
+ });
220
+
221
+ it("works on functions as source, behaves like lodash", () => {
222
+ lodash: {
223
+ const obj = () => {};
224
+ (() => {
225
+ _set(obj, "lo.da", "sh");
226
+ }).must.not.throw();
227
+
228
+ obj.lo.da.must.equal("sh");
229
+ }
230
+
231
+ bottom: {
232
+ const obj = () => {};
233
+ (() => {
234
+ set(obj, "lo.da", "sh");
235
+ }).must.not.throw();
236
+
237
+ obj.lo.da.must.equal("sh");
238
+ }
239
+ });
240
+
241
+ it("creates an object when numeric index is used and target isn't set, behaves NOT like lodash", () => {
242
+ lodash1: {
243
+ const obj = {};
244
+ _set(obj, ["items", 5, "title"], "Jan Padeusz");
245
+ obj.items.must.be.an.array();
246
+ obj.items.must.eql([,,,,, // eslint-disable-line no-sparse-arrays
247
+ {
248
+ title: "Jan Padeusz",
249
+ }]);
250
+ }
251
+
252
+ lodash2: {
253
+ const obj = {};
254
+ _set(obj, "items.5.title", "Jan Padeusz");
255
+ obj.items.must.be.an.array();
256
+ obj.items.must.eql([,,,,, // eslint-disable-line no-sparse-arrays
257
+ {
258
+ title: "Jan Padeusz",
259
+ }]);
260
+ }
261
+
262
+ bottom1: {
263
+ const obj = {};
264
+ set(obj, ["items", 5, "title"], "Jan Padeusz");
265
+ obj.items.must.not.be.an.array();
266
+ obj.items.must.eql({
267
+ 5: {
268
+ title: "Jan Padeusz",
269
+ },
270
+ });
271
+ }
272
+
273
+ bottom2: {
274
+ const obj = {};
275
+ set(obj, "items.5.title", "Jan Padeusz");
276
+ obj.items.must.not.be.an.array();
277
+ obj.items.must.eql({
278
+ 5: {
279
+ title: "Jan Padeusz",
280
+ },
281
+ });
282
+ }
283
+ });
284
+
285
+ it("doesn't parse path string as js code, behaves NOT like lodash", () => {
286
+ lodash: {
287
+ const obj = {};
288
+ _set(obj, "hello[world]", "is it me you're looking for?");
289
+ obj.hello.world.must.equal("is it me you're looking for?");
290
+ }
291
+
292
+ bottom: {
293
+ const obj = {};
294
+ set(obj, "hello[world]", "is it me you're looking for?");
295
+ obj.must.not.have.property("hello");
296
+ obj.must.have.property("hello[world]");
297
+ obj["hello[world]"].must.equal("is it me you're looking for?");
298
+ }
299
+ });
300
+
301
+ it("allow dot separated path, behaves like lodash", () => {
302
+ lodash: {
303
+ const obj = {};
304
+ _set(obj, "hello.world", "is it me you're looking for?");
305
+ obj.hello.world.must.equal("is it me you're looking for?");
306
+ }
307
+
308
+ bottom: {
309
+ const obj = {};
310
+ set(obj, "hello.world", "is it me you're looking for?");
311
+ obj.hello.world.must.equal("is it me you're looking for?");
312
+ }
313
+ });
314
+
315
+ it("allow arrays as path, behaves like lodash", () => {
316
+ lodash: {
317
+ const obj = {};
318
+ _set(obj, ["hello", "world"], "is it me you're looking for?");
319
+ obj.hello.world.must.equal("is it me you're looking for?");
320
+ }
321
+
322
+ bottom: {
323
+ const obj = {};
324
+ set(obj, ["hello", "world"], "is it me you're looking for?");
325
+ obj.hello.world.must.equal("is it me you're looking for?");
326
+ }
327
+ });
328
+
329
+ it("doesn't parse mixed array/dot-strings paths, behaves like lodash", () => {
330
+ lodash: {
331
+ const obj = {};
332
+ _set(obj, ["hello", "world.5"], "is it me you're looking for?");
333
+ obj.hello.must.not.have.property("world");
334
+ obj.hello.must.have.property("world.5");
335
+ obj.hello["world.5"].must.equal("is it me you're looking for?");
336
+ }
337
+
338
+ bottom: {
339
+ const obj = {};
340
+ set(obj, ["hello", "world.5"], "is it me you're looking for?");
341
+ obj.hello.must.not.have.property("world");
342
+ obj.hello.must.have.property("world.5");
343
+ obj.hello["world.5"].must.equal("is it me you're looking for?");
344
+ }
345
+ });
346
+
347
+ // @todo test Maps, Sets, etc.
348
+ });
package/src/set.ts ADDED
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Source object to mutate.
3
+ *
4
+ * @see {@link set}.
5
+ */
6
+ interface Source { [key: string]: unknown }
7
+
8
+ const isObject = (value: unknown) => (typeof value === "object" || typeof value === "function") && value !== null;
9
+
10
+ /**
11
+ * Updates the value at given path of given object. It mutates the object. If path is not found then objects are created
12
+ * "on the way". If non-objects are found, they are replaced with new plain objects. If primitives are used as source
13
+ * they are ignored and returned value is empty object with updated value at given path.
14
+ *
15
+ * This is still too dynamic in nature to get full TypeScript support. Properties are not typed, return type is unknown.
16
+ * If you are okay with NOT mutating the object but get new one instead it is recommended to use `immutable-assign`
17
+ * package instead.
18
+ *
19
+ * @param {Object} source - source object to mutate
20
+ * @param {string|Array<string>} path - path where value should be stored, written as dot-separated property names or
21
+ * array with property names. Use Array when your keys includes dots.
22
+ * @param {*} value - value to be set
23
+ * @example set(object, "deep.property", value)
24
+ * @example set(object, ["deep", "property"], value)
25
+ * @example set({}, "deep[0].property", value)
26
+ * // will create this structure:
27
+ * { "deep[0]": { "property": value }}
28
+ * @example set({}, "items.0", value)
29
+ * // will create object, not array
30
+ * { "items": { "0": value }}
31
+ * @returns {Object} - given object or new object if source was primitive
32
+ */
33
+ const set = (source: Source, path: string | string[], value: unknown): Source | unknown => { // eslint-disable-line @typescript-eslint/no-redundant-type-constituents,max-len
34
+ const pathParts = typeof path === "string" ? path.split(".") : path;
35
+ const len = pathParts.length;
36
+
37
+ const result = isObject(source) ? source : {};
38
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
39
+ let current: Source | unknown = result;
40
+ for (let i = 0; i < len; i++) {
41
+ const isLast = i === len - 1;
42
+ const key = pathParts[i];
43
+ if (isLast) {
44
+ (current as Source)[key] = value;
45
+ return result;
46
+ }
47
+ if (!isObject((current as Source)[key])) {
48
+ (current as Source)[key] = {};
49
+ }
50
+ current = (current as Source)[key];
51
+ }
52
+ return result;
53
+ };
54
+
55
+ export { set };
56
+
57
+ export type {
58
+ Source as SetSource,
59
+ };