@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,241 @@
1
+ import { setImmutable } from "./setImmutable.js";
2
+
3
+ describe("setImmutable", () => {
4
+ it("updates existing value", () => {
5
+ const obj = {
6
+ title: "Hello",
7
+ };
8
+ const result = setImmutable(obj, "title", "World");
9
+ obj.must.not.equal(result);
10
+ obj.title.must.equal("Hello");
11
+ result.title.must.equal("World");
12
+ });
13
+
14
+ it("adds new value", () => {
15
+ const obj = {
16
+ title: "Hello",
17
+ };
18
+ const result = setImmutable(obj, "name", "World");
19
+ obj.must.not.equal(result);
20
+ obj.must.eql({
21
+ title: "Hello",
22
+ });
23
+ result.must.eql({
24
+ title: "Hello",
25
+ name: "World",
26
+ });
27
+ });
28
+
29
+ it("matches the value when path is type number", () => {
30
+ setImmutable({ a: 1 }, 1, "World").must.eql({
31
+ a: 1,
32
+ 1: "World",
33
+ });
34
+ setImmutable([1], 1, "World").must.eql([
35
+ 1, "World",
36
+ ]);
37
+ });
38
+
39
+ it("disallows other types than string, number or array of strings and numbers", () => {
40
+ (() => setImmutable({}, null, "test")).must.throw();
41
+ (() => setImmutable({}, undefined, "test")).must.throw();
42
+ (() => setImmutable({}, () => {}, "test")).must.throw();
43
+ (() => setImmutable({}, {}, "test")).must.throw();
44
+ (() => setImmutable({}, Symbol("abc"), "test")).must.throw();
45
+
46
+ (() => setImmutable({}, [1, "s"], "test")).must.not.throw();
47
+
48
+ (() => setImmutable({}, [1, "s", []], "test")).must.throw();
49
+ (() => setImmutable({}, [() => {}], "test")).must.throw();
50
+ });
51
+
52
+ it("disallows empty path paths", () => {
53
+ (() => setImmutable({}, "", "test")).must.throw();
54
+ (() => setImmutable({}, ["a", "", "b"], "test")).must.throw();
55
+ (() => setImmutable({}, ["a", "b", ""], "test")).must.throw();
56
+ (() => setImmutable({}, [], "test")).must.throw();
57
+ (() => setImmutable({}, "a.b..c", "test")).must.throw();
58
+ });
59
+
60
+ it("deeply sets value", () => {
61
+ const object = {
62
+ data: {
63
+ title: "666",
64
+ },
65
+ other: {
66
+ a: 5,
67
+ },
68
+ };
69
+ const newObject = setImmutable(object, "data.name", "santa");
70
+
71
+ object.must.not.equal(newObject);
72
+ object.data.must.not.equal(newObject.data);
73
+
74
+ object.other.must.equal(newObject.other);
75
+
76
+ newObject.must.eql({
77
+ data: {
78
+ title: "666",
79
+ name: "santa",
80
+ },
81
+ other: {
82
+ a: 5,
83
+ },
84
+ });
85
+ object.must.eql({
86
+ data: {
87
+ title: "666",
88
+ },
89
+ other: {
90
+ a: 5,
91
+ },
92
+ });
93
+ });
94
+
95
+ it("keeps arrays in place", () => {
96
+ const object = {
97
+ items: [0, 1],
98
+ };
99
+ const newObject = setImmutable(object, "items.2", 2);
100
+
101
+ newObject.must.eql({
102
+ items: [0, 1, 2],
103
+ });
104
+ });
105
+
106
+ it("replaces functions with objects", () => {
107
+ const fn = () => {};
108
+ fn.publicValue = 5;
109
+
110
+ const object = {
111
+ fn,
112
+ };
113
+
114
+ const result = setImmutable(object, "fn.value", 666);
115
+
116
+ result.fn.must.be.an.object();
117
+ result.fn.must.not.be.a.function();
118
+ result.fn.must.not.equal(fn);
119
+ result.must.eql({
120
+ fn: {
121
+ publicValue: 5,
122
+ value: 666,
123
+ },
124
+ });
125
+ });
126
+
127
+ it("adds objects if not existing on the way", () => {
128
+ const object = {
129
+ data: {},
130
+ };
131
+
132
+ const result = setImmutable(object, "data.gallery.items", [1, 2, 3]);
133
+
134
+ object.must.not.equal(result);
135
+ object.data.must.not.equal(result.data);
136
+ object.must.eql({
137
+ data: {},
138
+ });
139
+ result.must.eql({
140
+ data: {
141
+ gallery: {
142
+ items: [1, 2, 3],
143
+ },
144
+ },
145
+ });
146
+ });
147
+
148
+ it("replaces primitives on the way", () => {
149
+ {
150
+ const object = {
151
+ data: "string",
152
+ };
153
+
154
+ const result = setImmutable(object, "data.gallery.items", [1, 2, 3]);
155
+
156
+ result.must.eql({
157
+ data: {
158
+ gallery: {
159
+ items: [1, 2, 3],
160
+ },
161
+ },
162
+ });
163
+ }
164
+
165
+ {
166
+ const object = {
167
+ data: 45678,
168
+ };
169
+
170
+ const result = setImmutable(object, "data.gallery.items", [1, 2, 3]);
171
+
172
+ result.must.eql({
173
+ data: {
174
+ gallery: {
175
+ items: [1, 2, 3],
176
+ },
177
+ },
178
+ });
179
+ }
180
+ });
181
+
182
+ it("works with primitives as source", () => {
183
+ setImmutable(undefined, "hi", "mom").must.eql({
184
+ hi: "mom",
185
+ });
186
+
187
+ setImmutable(null, "hi", "mom").must.eql({
188
+ hi: "mom",
189
+ });
190
+
191
+ setImmutable(69, "hi", "mom").must.eql({
192
+ hi: "mom",
193
+ });
194
+ });
195
+
196
+ it("creates an object when using numeric key and source object has missing data there", () => {
197
+ const result = setImmutable(null, ["data", 5], 100);
198
+ result.data.must.not.be.an.array();
199
+ result.must.eql({
200
+ data: {
201
+ 5: 100,
202
+ },
203
+ });
204
+ });
205
+
206
+ it("doesn't parse path string as js code", () => {
207
+ const result = setImmutable(null, "data[title]", "Horror movie");
208
+ result.must.not.have.property("data");
209
+ result.must.have.property("data[title]");
210
+ result.must.eql({
211
+ "data[title]": "Horror movie",
212
+ });
213
+ });
214
+
215
+ it("allows dot separated path", () => {
216
+ setImmutable(null, "data.title", "Horror movie").must.eql({
217
+ data: {
218
+ title: "Horror movie",
219
+ },
220
+ });
221
+ });
222
+
223
+ it("allows array as path", () => {
224
+ setImmutable(null, ["data", "title"], "Horror movie").must.eql({
225
+ data: {
226
+ title: "Horror movie",
227
+ },
228
+ });
229
+ });
230
+
231
+ it("doesn't parse mixed array/dot-strings paths", () => {
232
+ const result = setImmutable(null, ["data", "title.name"], "Horror movie");
233
+ result.data.must.not.have.property("title");
234
+ result.data.must.have.property("title.name");
235
+ result.must.eql({
236
+ data: {
237
+ "title.name": "Horror movie",
238
+ },
239
+ });
240
+ });
241
+ });
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Source object.
3
+ *
4
+ * @see {@link setImmutable}.
5
+ */
6
+ interface Source { [key: string]: unknown }
7
+
8
+ const isObject = (value: unknown) => (typeof value === "object" || typeof value === "function") && value !== null;
9
+
10
+ const clone = (value: unknown[] | Source) => {
11
+ if (Array.isArray(value)) {
12
+ return [...value as never as unknown[]]; // typecasts to satisfy eslint
13
+ }
14
+ return { ...value };
15
+ };
16
+
17
+ const hasOnlyValidPathParts = (array: unknown[]) => {
18
+ if (!array.length) {
19
+ return false;
20
+ }
21
+ return array.every(item => {
22
+ const type = typeof item;
23
+ return (type === "string" && item !== "") || type === "number";
24
+ });
25
+ };
26
+
27
+ /**
28
+ * Argument of {@link setImmutable}.
29
+ *
30
+ * Path to store value in. Can be a dot-separated string or an array of strings.
31
+ * Numbers are allowed for convenience.
32
+ */
33
+ type Path = number | string | (number | string)[];
34
+
35
+ const getPathParts = (path: Path) => {
36
+ if (typeof path === "number") {
37
+ return [String(path)];
38
+ }
39
+ if (typeof path === "string") {
40
+ return path.split(".");
41
+ }
42
+ if (Array.isArray(path)) {
43
+ return path;
44
+ }
45
+ throw new TypeError("Path must be a string, a number or an array of strings and numbers");
46
+ };
47
+
48
+ /**
49
+ * Updates the value at given path of given object. It does not mutate the object but returns a new one. If path is not
50
+ * found then objects are created "on the way". If non-objects are found, they are replaced with new plain objects. If
51
+ * primitives are used as source they are ignored and returned value is empty object with updated value at given path.
52
+ *
53
+ * This is still too dynamic in nature to get full TypeScript support. Properties are not typed, return type is unknown.
54
+ * It is recommended to use `immutable-assign` package instead.
55
+ *
56
+ * @param {Object} source - source object to mutate
57
+ * @param {Path} path - path where value should be stored, written as dot-separated
58
+ * property names or array with property names. Use Array when your keys includes dots.
59
+ * @param {*} value - value to be set
60
+ * @example set(object, "deep.property", value)
61
+ * @example set(object, ["deep", "property"], value)
62
+ * @example set({}, "deep[0].property", value)
63
+ * // will create this structure:
64
+ * { "deep[0]": { "property": value }}
65
+ * @example set({}, "items.0", value)
66
+ * // will create an object, not an array
67
+ * { "items": { "0": value }}
68
+ * @returns {Object} - given object or new object if source was primitive
69
+ */
70
+ const setImmutable = (source: Source, path: Path, value: unknown): Source | unknown => { // eslint-disable-line max-statements, max-len, @typescript-eslint/no-redundant-type-constituents
71
+ const pathParts = getPathParts(path);
72
+ const isValidPath = hasOnlyValidPathParts(pathParts);
73
+ if (!isValidPath) {
74
+ throw new TypeError("Path must not be empty or contain empty parts");
75
+ }
76
+ const len = pathParts.length;
77
+
78
+ const result = isObject(source) ? clone(source) : {};
79
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
80
+ let current: Source | unknown = result;
81
+ for (let i = 0; i < len; i++) {
82
+ const isLast = i === len - 1;
83
+ const key = pathParts[i];
84
+ if (isLast) {
85
+ (current as Source)[key] = value;
86
+ return result;
87
+ }
88
+ if (!isObject((current as Source)[key])) { // @todo probably can be removed
89
+ (current as Source)[key] = {};
90
+ }
91
+ else {
92
+ (current as Source)[key] = clone((current as Source)[key] as Source | unknown[]);
93
+ }
94
+ current = (current as Source)[key];
95
+ }
96
+ return result;
97
+ };
98
+
99
+ export { setImmutable };
100
+
101
+ export type {
102
+ Path as SetImmutablePath,
103
+ Source as SetImmutableSource,
104
+ };
@@ -0,0 +1,56 @@
1
+ import { sortBy } from "./sortBy.js";
2
+
3
+ describe("sortBy", () => {
4
+ const items = [
5
+ {
6
+ name: "Astra",
7
+ },
8
+ {
9
+ name: "Zorro",
10
+ },
11
+ {
12
+ name: "Ewelina",
13
+ },
14
+ ];
15
+
16
+ it("must sort asc", () => {
17
+ items.sort(sortBy("name")).must.eql([
18
+ {
19
+ name: "Astra",
20
+ },
21
+ {
22
+ name: "Ewelina",
23
+ },
24
+ {
25
+ name: "Zorro",
26
+ },
27
+ ]);
28
+ });
29
+
30
+ it("must sort desc", () => {
31
+ items.sort(sortBy("name", false)).must.eql([
32
+ {
33
+ name: "Zorro",
34
+ },
35
+ {
36
+ name: "Ewelina",
37
+ },
38
+ {
39
+ name: "Astra",
40
+ },
41
+ ]);
42
+ });
43
+
44
+ it("must sort given property", () => {
45
+ const sizes = [
46
+ { size: 30 },
47
+ { size: 90 },
48
+ { size: 50 },
49
+ ];
50
+ sizes.sort(sortBy("size")).must.eql([
51
+ { size: 30 },
52
+ { size: 50 },
53
+ { size: 90 },
54
+ ]);
55
+ });
56
+ });
package/src/sortBy.ts ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Returns a function that can be used as a callback to `.sort()` method. Returned function will sort array by given
3
+ * property.
4
+ * @param {string} propertyName - name of the property to sort by
5
+ * @param {boolean} [asc=true] - should sort be ascending?
6
+ * @param {*} defaultValue - value to use when property is not defined in one of the objects
7
+ */
8
+ const sortBy = <T>(propertyName: keyof T, asc = true, defaultValue: unknown = null) => (a: T, b: T) => {
9
+ if (a[propertyName] === b[propertyName]) {
10
+ return 0;
11
+ }
12
+ // @ts-expect-error We don't care about types here, it's for runtime pure JS too
13
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
14
+ if ((a[propertyName] ?? defaultValue) > (b[propertyName] ?? defaultValue)) {
15
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
16
+ return asc ? 1 : -1;
17
+ }
18
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
19
+ return asc ? -1 : 1;
20
+ };
21
+
22
+ export {
23
+ sortBy,
24
+ };
@@ -0,0 +1,136 @@
1
+ import { throttle } from "./throttle.js";
2
+ import { wait } from "./wait.js";
3
+
4
+ // @TODO much more tests required
5
+ // @TODO for some reason these tests are useless, at 2cfbbc81 they should fail, but they weren't
6
+
7
+ describe("throttle", function() {
8
+ it("should work with single value timeout", async function() {
9
+ const runTimes: number[] = [];
10
+ const fn = () => {
11
+ runTimes.push(Date.now());
12
+ };
13
+
14
+ const throttled = throttle(fn, 100);
15
+ const start = Date.now();
16
+ while (Date.now() - start < 2000) { // for 2 seconds
17
+ throttled();
18
+ await wait(1);
19
+ }
20
+
21
+ runTimes.length.must.be.gte(10); // just to be sure something run
22
+ runTimes.length.must.be.lt(21);
23
+
24
+ runTimes.forEach((time, key) => {
25
+ if (!key) {
26
+ return;
27
+ }
28
+ const lastTime = runTimes[key - 1];
29
+ const diff = time - lastTime;
30
+ diff.must.be.gte(100);
31
+ });
32
+ });
33
+
34
+ it("should works with multi value timeout", async function() {
35
+ const runTimes: number[] = [];
36
+ const fn = () => {
37
+ runTimes.push(Date.now());
38
+ };
39
+
40
+ const throttled = throttle(fn, [100, 200, 300]);
41
+ const start = Date.now();
42
+ while (Date.now() - start < 2000) { // for 2 seconds
43
+ throttled();
44
+ await wait(1);
45
+ }
46
+
47
+ const diff1 = runTimes[1] - runTimes[0];
48
+ diff1.must.be.gte(100);
49
+ diff1.must.be.lte(150); // cpu hogs can bump this value, fingers crossed it won't be higher to crash the tests
50
+
51
+ const diff2 = runTimes[2] - runTimes[1];
52
+ diff2.must.be.gte(200);
53
+ diff2.must.be.lte(250);
54
+
55
+ const diff3 = runTimes[3] - runTimes[2];
56
+ diff3.must.be.gte(300);
57
+ diff3.must.be.lte(350);
58
+
59
+ const diff4 = runTimes[4] - runTimes[3];
60
+ diff4.must.be.gte(300);
61
+ diff4.must.be.lte(350);
62
+ });
63
+
64
+ it("allows to reset multi value timeout", async function() {
65
+ const runTimes: number[] = [];
66
+ const fn = () => {
67
+ runTimes.push(Date.now());
68
+ };
69
+
70
+ const throttled = throttle(fn, [100, 200, 300]);
71
+ let start = Date.now();
72
+ while (Date.now() - start < 2000) { // for 2 seconds
73
+ throttled();
74
+ await wait(1);
75
+ }
76
+
77
+ throttled.cancel();
78
+ runTimes.length = 0;
79
+
80
+ start = Date.now();
81
+ while (Date.now() - start < 2000) { // for 2 seconds
82
+ throttled();
83
+ await wait(1);
84
+ }
85
+
86
+ const diff1 = runTimes[1] - runTimes[0];
87
+ diff1.must.be.gte(100);
88
+ diff1.must.be.lte(150); // cpu hogs can bump this value, fingers crossed it won't be higher to crash the tests
89
+
90
+ const diff2 = runTimes[2] - runTimes[1];
91
+ diff2.must.be.gte(200);
92
+ diff2.must.be.lte(250);
93
+
94
+ const diff3 = runTimes[3] - runTimes[2];
95
+ diff3.must.be.gte(300);
96
+ diff3.must.be.lte(350);
97
+
98
+ const diff4 = runTimes[4] - runTimes[3];
99
+ diff4.must.be.gte(300);
100
+ diff4.must.be.lte(350);
101
+ });
102
+
103
+ it("works with multiple values with trailing calls", async function() {
104
+ const runTimes: number[] = [];
105
+ const fn = () => {
106
+ runTimes.push(Date.now());
107
+ };
108
+
109
+ const throttled = throttle(fn, [100, 200, 300]);
110
+ throttled();
111
+ throttled();
112
+ await wait(201);
113
+ throttled();
114
+ await wait(201);
115
+ throttled();
116
+ await wait(201);
117
+ throttled();
118
+ await wait(201);
119
+ throttled();
120
+
121
+ const diffs = runTimes.map((time, key) => {
122
+ if (!key) {
123
+ return null;
124
+ }
125
+ return time - runTimes[key - 1];
126
+ }).filter(v => v !== null);
127
+ diffs[0].must.be.gte(100);
128
+ diffs[0].must.be.lt(150);
129
+
130
+ diffs[1].must.be.gte(200);
131
+ diffs[1].must.be.lt(250);
132
+
133
+ diffs[2].must.be.gte(300);
134
+ diffs[2].must.be.lt(350);
135
+ });
136
+ });