@ezez/utils 4.8.0 → 4.8.2

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 (188) hide show
  1. package/.claude/settings.local.json +14 -0
  2. package/CHANGELOG.md +18 -0
  3. package/CLAUDE.md +137 -0
  4. package/dist/get.d.ts.map +1 -1
  5. package/dist/get.js +1 -1
  6. package/dist/get.js.map +1 -1
  7. package/dist/mostFrequent.d.ts +1 -1
  8. package/dist/mostFrequent.d.ts.map +1 -1
  9. package/dist/mostFrequent.js +2 -5
  10. package/dist/mostFrequent.js.map +1 -1
  11. package/dist/samples.d.ts.map +1 -1
  12. package/dist/samples.js +2 -1
  13. package/dist/samples.js.map +1 -1
  14. package/dist/scale.d.ts.map +1 -1
  15. package/dist/scale.js +3 -0
  16. package/dist/scale.js.map +1 -1
  17. package/dist/serializeToBuffer/unserializeFromBuffer.d.ts.map +1 -1
  18. package/dist/serializeToBuffer/unserializeFromBuffer.js +1 -3
  19. package/dist/serializeToBuffer/unserializeFromBuffer.js.map +1 -1
  20. package/dist/sortByMultiple.d.ts.map +1 -1
  21. package/dist/sortByMultiple.js.map +1 -1
  22. package/dist/throttle.d.ts +3 -3
  23. package/dist/throttle.d.ts.map +1 -1
  24. package/dist/throttle.js +2 -3
  25. package/dist/throttle.js.map +1 -1
  26. package/dist/trimEnd.d.ts.map +1 -1
  27. package/dist/trimEnd.js +3 -0
  28. package/dist/trimEnd.js.map +1 -1
  29. package/dist/trimStart.d.ts.map +1 -1
  30. package/dist/trimStart.js +3 -0
  31. package/dist/trimStart.js.map +1 -1
  32. package/dist/waitFor.d.ts.map +1 -1
  33. package/dist/waitFor.js +19 -11
  34. package/dist/waitFor.js.map +1 -1
  35. package/docs/documents/Changelog.html +72 -54
  36. package/docs/functions/index.assertProps.html +2 -2
  37. package/docs/functions/index.cap.html +2 -2
  38. package/docs/functions/index.capitalize.html +2 -2
  39. package/docs/functions/index.coalesce.html +2 -2
  40. package/docs/functions/index.compareArrays.html +2 -2
  41. package/docs/functions/index.compareProps.html +2 -2
  42. package/docs/functions/index.deserialize.html +2 -2
  43. package/docs/functions/index.ensureArray.html +2 -2
  44. package/docs/functions/index.ensureDate.html +2 -2
  45. package/docs/functions/index.ensureError.html +2 -2
  46. package/docs/functions/index.ensurePrefix.html +2 -2
  47. package/docs/functions/index.ensureSuffix.html +2 -2
  48. package/docs/functions/index.ensureTimestamp.html +2 -2
  49. package/docs/functions/index.escapeRegExp.html +2 -2
  50. package/docs/functions/index.formatDate.html +2 -2
  51. package/docs/functions/index.formatHash.html +2 -2
  52. package/docs/functions/index.get.html +3 -3
  53. package/docs/functions/index.getMultiple.html +2 -2
  54. package/docs/functions/index.hasProps.html +2 -2
  55. package/docs/functions/index.ignore.html +2 -2
  56. package/docs/functions/index.insertSeparator.html +2 -2
  57. package/docs/functions/index.isEmpty.html +2 -2
  58. package/docs/functions/index.isNumericString.html +2 -2
  59. package/docs/functions/index.isPlainObject.html +2 -2
  60. package/docs/functions/index.last.html +2 -2
  61. package/docs/functions/index.later-1.html +2 -2
  62. package/docs/functions/index.mapAsync.html +2 -2
  63. package/docs/functions/index.mapValues.html +2 -2
  64. package/docs/functions/index.match.html +2 -2
  65. package/docs/functions/index.memoize.html +2 -2
  66. package/docs/functions/index.merge.html +2 -2
  67. package/docs/functions/index.mostFrequent.html +2 -2
  68. package/docs/functions/index.noop.html +2 -2
  69. package/docs/functions/index.occurrences.html +2 -2
  70. package/docs/functions/index.omit.html +2 -2
  71. package/docs/functions/index.pick.html +2 -2
  72. package/docs/functions/index.pull.html +2 -2
  73. package/docs/functions/index.race.html +2 -2
  74. package/docs/functions/index.remove.html +2 -2
  75. package/docs/functions/index.removeCommonProperties.html +2 -2
  76. package/docs/functions/index.replace.html +2 -2
  77. package/docs/functions/index.replaceDeep.html +2 -2
  78. package/docs/functions/index.rethrow.html +2 -2
  79. package/docs/functions/index.retry.html +2 -2
  80. package/docs/functions/index.round.html +2 -2
  81. package/docs/functions/index.safe.html +2 -2
  82. package/docs/functions/index.sample.html +2 -2
  83. package/docs/functions/index.samples.html +2 -2
  84. package/docs/functions/index.scale.html +3 -2
  85. package/docs/functions/index.seq.html +2 -2
  86. package/docs/functions/index.seqEarlyBreak.html +2 -2
  87. package/docs/functions/index.serialize.html +2 -2
  88. package/docs/functions/index.serializeToBuffer.html +2 -2
  89. package/docs/functions/index.set.html +2 -2
  90. package/docs/functions/index.setImmutable.html +2 -2
  91. package/docs/functions/index.shuffle.html +2 -2
  92. package/docs/functions/index.sortBy.html +2 -2
  93. package/docs/functions/index.sortByMultiple.html +3 -3
  94. package/docs/functions/index.sortProps.html +2 -2
  95. package/docs/functions/index.stripPrefix.html +2 -2
  96. package/docs/functions/index.stripSuffix.html +2 -2
  97. package/docs/functions/index.throttle.html +3 -3
  98. package/docs/functions/index.toggle.html +2 -2
  99. package/docs/functions/index.trim.html +2 -2
  100. package/docs/functions/index.trimEnd.html +2 -2
  101. package/docs/functions/index.trimStart.html +2 -2
  102. package/docs/functions/index.truthy.html +2 -2
  103. package/docs/functions/index.unique.html +2 -2
  104. package/docs/functions/index.unserializeFromBuffer.html +2 -2
  105. package/docs/functions/index.wait.html +2 -2
  106. package/docs/functions/index.waitFor.html +2 -2
  107. package/docs/functions/index.waitSync.html +2 -2
  108. package/docs/index.html +2 -2
  109. package/docs/interfaces/index.ComparePropsOptions.html +3 -3
  110. package/docs/interfaces/index.GetMultipleSource.html +2 -2
  111. package/docs/interfaces/index.GetSource.html +2 -2
  112. package/docs/interfaces/index.IsNumericStringOptions.html +2 -2
  113. package/docs/interfaces/index.OccurencesOptions.html +2 -2
  114. package/docs/interfaces/index.SetImmutableSource.html +2 -2
  115. package/docs/interfaces/index.SetSource.html +2 -2
  116. package/docs/interfaces/index.ThrottleOptions.html +3 -3
  117. package/docs/interfaces/index.ThrottledFunctionExtras.html +3 -3
  118. package/docs/modules/index.html +1 -1
  119. package/docs/modules.html +1 -1
  120. package/docs/types/index.CustomDeserializers.html +1 -1
  121. package/docs/types/index.CustomSerializers.html +1 -1
  122. package/docs/types/index.Later.html +2 -2
  123. package/docs/types/index.MapValuesFn.html +2 -2
  124. package/docs/types/index.MatchCallback.html +1 -1
  125. package/docs/types/index.MergeTwo.html +2 -2
  126. package/docs/types/index.SeqEarlyBreaker.html +2 -2
  127. package/docs/types/index.SeqFn.html +2 -2
  128. package/docs/types/index.SeqFunctions.html +2 -2
  129. package/docs/types/index.SetImmutablePath.html +2 -2
  130. package/docs/types/index.ThrottledFunction.html +1 -1
  131. package/docs/variables/index.mapValuesUNSET.html +2 -2
  132. package/docs/variables/index.mergeUNSET.html +2 -2
  133. package/esm/get.d.ts.map +1 -1
  134. package/esm/get.js +1 -1
  135. package/esm/get.js.map +1 -1
  136. package/esm/mostFrequent.d.ts +1 -1
  137. package/esm/mostFrequent.d.ts.map +1 -1
  138. package/esm/mostFrequent.js +2 -5
  139. package/esm/mostFrequent.js.map +1 -1
  140. package/esm/samples.d.ts.map +1 -1
  141. package/esm/samples.js +2 -1
  142. package/esm/samples.js.map +1 -1
  143. package/esm/scale.d.ts.map +1 -1
  144. package/esm/scale.js +3 -0
  145. package/esm/scale.js.map +1 -1
  146. package/esm/serializeToBuffer/unserializeFromBuffer.d.ts.map +1 -1
  147. package/esm/serializeToBuffer/unserializeFromBuffer.js +1 -3
  148. package/esm/serializeToBuffer/unserializeFromBuffer.js.map +1 -1
  149. package/esm/sortByMultiple.d.ts.map +1 -1
  150. package/esm/sortByMultiple.js.map +1 -1
  151. package/esm/throttle.d.ts +3 -3
  152. package/esm/throttle.d.ts.map +1 -1
  153. package/esm/throttle.js +2 -3
  154. package/esm/throttle.js.map +1 -1
  155. package/esm/trimEnd.d.ts.map +1 -1
  156. package/esm/trimEnd.js +3 -0
  157. package/esm/trimEnd.js.map +1 -1
  158. package/esm/trimStart.d.ts.map +1 -1
  159. package/esm/trimStart.js +3 -0
  160. package/esm/trimStart.js.map +1 -1
  161. package/esm/waitFor.d.ts.map +1 -1
  162. package/esm/waitFor.js +19 -11
  163. package/esm/waitFor.js.map +1 -1
  164. package/package.json +12 -17
  165. package/pnpm-workspace.yaml +2 -0
  166. package/src/get.ts +1 -1
  167. package/src/mostFrequent.spec.ts +2 -1
  168. package/src/mostFrequent.ts +3 -7
  169. package/src/race.spec.ts +42 -0
  170. package/src/race.ts +0 -2
  171. package/src/retry.spec.ts +87 -0
  172. package/src/retry.ts +0 -2
  173. package/src/samples.spec.ts +9 -0
  174. package/src/samples.ts +4 -1
  175. package/src/scale.spec.ts +18 -0
  176. package/src/scale.ts +4 -0
  177. package/src/serializeToBuffer/unserializeFromBuffer.spec.ts +10 -0
  178. package/src/serializeToBuffer/unserializeFromBuffer.ts +1 -4
  179. package/src/sortByMultiple.ts +1 -0
  180. package/src/throttle.spec.ts +35 -0
  181. package/src/throttle.ts +7 -8
  182. package/src/trimEnd.spec.ts +5 -0
  183. package/src/trimEnd.ts +3 -0
  184. package/src/trimStart.spec.ts +5 -0
  185. package/src/trimStart.ts +3 -0
  186. package/src/waitFor.spec.ts +61 -0
  187. package/src/waitFor.ts +33 -15
  188. package/src/waitSync.ts +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"waitFor.js","sourceRoot":"","sources":["../src/waitFor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAoB5B,MAAM,cAAc,GAAsB;IACtC,QAAQ,EAAE,gBAAgB;IAC1B,OAAO,EAAE,QAAQ;IACjB,QAAQ,EAAE,QAAQ;CACrB,CAAC;AAeF,MAAM,OAAO,GAAG,CAAI,EAAyB,EAAE,UAAmB,cAAc,EAAc,EAAE;IAC5F,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,IAAI,aAAuB,EAAE,SAAmB,CAAC;QAEjD,MAAM,IAAI,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;QAC/C,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YACzD,MAAM,CAAC,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC,CAAC;YACnE,OAAO;QACX,CAAC;QAED,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAChC,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;gBACxB,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;gBACvC,YAAY,CAAC,aAAa,CAAC,CAAC;YAChC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,CAAC;QAGd,MAAM,KAAK,GAAG,CAAC,KAAK,IAAI,EAAE;YACtB,IAAI,CAAC;gBACD,KAAK,EAAE,CAAC;gBACR,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;gBAE1B,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;oBACrC,YAAY,CAAC,SAAS,CAAC,CAAC;oBACxB,YAAY,CAAC,aAAa,CAAC,CAAC;oBAC5B,OAAO,CAAC,MAAM,CAAC,CAAC;gBACpB,CAAC;qBACI,CAAC;oBACF,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;wBAC3D,YAAY,CAAC,SAAS,CAAC,CAAC;wBACxB,YAAY,CAAC,aAAa,CAAC,CAAC;wBAC5B,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,CAAC;wBACjD,OAAO;oBACX,CAAC;oBAED,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;wBAC5B,KAAK,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBACxB,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACzB,CAAC;YACL,CAAC;YACD,OAAO,KAAc,EAAE,CAAC;gBACpB,YAAY,CAAC,SAAS,CAAC,CAAC;gBACxB,YAAY,CAAC,aAAa,CAAC,CAAC;gBAE5B,MAAM,CAAC,GAAkC,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;gBAC9F,CAAC,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,CAAC;gBACtB,MAAM,CAAC,CAAC,CAAC,CAAC;YACd,CAAC;QACL,CAAC,CAAC,CAAC;QACH,KAAK,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AAEF,OAAO,EAAE,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"waitFor.js","sourceRoot":"","sources":["../src/waitFor.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ezez/utils",
3
- "version": "4.8.0",
3
+ "version": "4.8.2",
4
4
  "repository": "https://github.com/dzek69/bottom-line.git",
5
5
  "author": "Jacek Nowacki @dzek69 <git-public@dzek.eu>",
6
6
  "license": "MIT",
@@ -16,25 +16,23 @@
16
16
  "module": "./esm/index.js",
17
17
  "type": "module",
18
18
  "devDependencies": {
19
- "@babel/core": "^7.26.0",
20
- "@babel/preset-env": "^7.26.0",
21
- "@babel/preset-typescript": "^7.26.0",
22
- "@ezez/eslint": "^9.39.3",
19
+ "@babel/core": "^7.29.7",
20
+ "@babel/preset-env": "^7.29.7",
21
+ "@babel/preset-typescript": "^7.29.7",
22
+ "@ezez/eslint": "^9.39.4",
23
23
  "@types/jest": "^29.5.14",
24
- "@types/lodash": "^4.14.168",
25
- "@types/node": "^20.8.3",
24
+ "@types/lodash": "^4.17.24",
25
+ "@types/node": "^20.19.43",
26
26
  "babel-plugin-module-extension": "^0.1.3",
27
- "fs-extra": "^11.3.0",
27
+ "fs-extra": "^11.3.5",
28
28
  "husky": "^8.0.3",
29
- "jest": "^29.7.0",
30
- "lodash": "^4.17.21",
29
+ "jest": "^30.4.2",
30
+ "lodash": "^4.18.1",
31
31
  "must": "^0.13.4",
32
- "nodemon": "^3.1.9",
33
- "prettier": "^3.4.2",
32
+ "prettier": "^3.8.4",
34
33
  "resolve-tspaths": "^0.8.23",
35
- "ts-node": "^10.9.2",
36
34
  "typedoc": "0.27.6",
37
- "typescript": "^5.7.3"
35
+ "typescript": "^5.9.3"
38
36
  },
39
37
  "husky": {
40
38
  "hooks": {
@@ -47,7 +45,6 @@
47
45
  "fixDefaultForCommonJS": false,
48
46
  "jsx": false
49
47
  },
50
- "dependencies": {},
51
48
  "scripts": {
52
49
  "test": "NODE_ENV=test jest",
53
50
  "test:lodashImports": "node test/lodash-tree.cjs",
@@ -58,8 +55,6 @@
58
55
  "typecheck": "tsc --noEmit",
59
56
  "lint": "ezlint src",
60
57
  "lint:fix": "pnpm run lint --fix",
61
- "start:dev": "nodemon",
62
- "start:dev:compatibility": "TS_NODE_FILES=true pnpm run start:dev",
63
58
  "updates": "pnpm dlx npm-check-updates --dep prod",
64
59
  "updates:dev": "pnpm dlx npm-check-updates --dep dev",
65
60
  "updates:all": "pnpm dlx npm-check-updates"
@@ -0,0 +1,2 @@
1
+ allowBuilds:
2
+ unrs-resolver: false
package/src/get.ts CHANGED
@@ -30,7 +30,7 @@ interface Source { [key: string]: unknown }
30
30
  * // else `5` will be returned
31
31
  * @returns {*} - found value or default value
32
32
  */
33
- const get = (source: Source, property: string | string[], defaultValue: unknown = undefined): unknown => {
33
+ const get = (source: Source, property: string | string[], defaultValue?: unknown): unknown => {
34
34
  const properties = typeof property === "string" ? property.split(".") : [...property];
35
35
 
36
36
  let result: unknown = source;
@@ -16,7 +16,8 @@ describe("mostFrequent", () => {
16
16
  });
17
17
 
18
18
  it("returns undefined if array empty", () => {
19
- must(mostFrequent([])).equal(undefined);
19
+ const result = mostFrequent<number>([]);
20
+ must(result).equal(undefined);
20
21
  });
21
22
 
22
23
  it("compares strictly", () => {
@@ -2,24 +2,20 @@
2
2
  * Finds most frequent value in array
3
3
  * @param {Array} array
4
4
  */
5
- const mostFrequent = <T>(array: T[]): T => {
5
+ const mostFrequent = <T>(array: T[]): T | undefined => {
6
6
  let top = 0,
7
7
  topValue = array[0];
8
8
 
9
9
  const map = new Map<T, number>();
10
10
  array.forEach(value => {
11
- if (!map.has(value)) {
12
- map.set(value, 0);
13
- }
14
- const next = map.get(value)! + 1;
15
- map.set(value, map.get(value)! + 1);
11
+ const next = (map.get(value) ?? 0) + 1;
12
+ map.set(value, next);
16
13
  if (next > top) {
17
14
  top = next;
18
15
  topValue = value;
19
16
  }
20
17
  });
21
18
 
22
- // @ts-expect-error - idk if there is a good workaround for this with `noUncheckedIndexedAccess`
23
19
  return topValue;
24
20
  };
25
21
 
@@ -0,0 +1,42 @@
1
+ import must from "must"; // eslint-disable-line @typescript-eslint/no-shadow
2
+
3
+ import { race } from "./race";
4
+ import { wait } from "./wait";
5
+
6
+ describe("race", () => {
7
+ it("resolves with the promise value when it settles before the timeout", async () => {
8
+ const start = Date.now();
9
+ const result = await race(Promise.resolve(42), 100);
10
+ must(result).equal(42);
11
+ must(Date.now() - start).be.below(90);
12
+ });
13
+
14
+ it("rejects with the promise error when it rejects before the timeout", async () => {
15
+ const start = Date.now();
16
+ await race(Promise.reject(new Error("boom")), 100).then(() => {
17
+ throw new Error("Should not resolve");
18
+ }, (e: unknown) => {
19
+ must((e as Error).message).equal("boom");
20
+ must(Date.now() - start).be.below(90);
21
+ });
22
+ });
23
+
24
+ it("rejects with a timeout error when the promise is too slow", async () => {
25
+ const slow = wait(200).then(() => "late");
26
+ await race(slow, 50).then(() => {
27
+ throw new Error("Should not resolve");
28
+ }, (e: unknown) => {
29
+ must(e).instanceOf(Error);
30
+ must((e as Error).message).equal("Race: Timeout");
31
+ });
32
+ });
33
+
34
+ it("uses a custom timeout message", async () => {
35
+ const slow = wait(200).then(() => "late");
36
+ await race(slow, 50, "too slow").then(() => {
37
+ throw new Error("Should not resolve");
38
+ }, (e: unknown) => {
39
+ must((e as Error).message).equal("too slow");
40
+ });
41
+ });
42
+ });
package/src/race.ts CHANGED
@@ -26,5 +26,3 @@ const race = <T>(promise: Promise<T>, timeout: number, message = "Race: Timeout"
26
26
  export {
27
27
  race,
28
28
  };
29
-
30
- // TODO unit tests
@@ -0,0 +1,87 @@
1
+ import must from "must"; // eslint-disable-line @typescript-eslint/no-shadow
2
+
3
+ import { retry } from "./retry";
4
+
5
+ describe("retry", () => {
6
+ it("returns the result on first success without retrying", async () => {
7
+ let calls = 0;
8
+ const result = await retry(async () => {
9
+ calls++;
10
+ return "ok";
11
+ });
12
+ must(result).equal("ok");
13
+ must(calls).equal(1);
14
+ });
15
+
16
+ it("retries until the function succeeds", async () => {
17
+ let calls = 0;
18
+ const result = await retry(async () => {
19
+ calls++;
20
+ if (calls < 3) {
21
+ throw new Error("fail");
22
+ }
23
+ return calls;
24
+ });
25
+ must(result).equal(3);
26
+ must(calls).equal(3);
27
+ });
28
+
29
+ it("with maxRetries 0 runs once and rethrows the error", async () => {
30
+ let calls = 0;
31
+ await retry(async () => {
32
+ calls++;
33
+ throw new Error("nope");
34
+ }, { maxRetries: 0 }).then(() => {
35
+ throw new Error("Should not resolve");
36
+ }, (e: unknown) => {
37
+ must((e as Error).message).equal("nope");
38
+ });
39
+ must(calls).equal(1);
40
+ });
41
+
42
+ it("with maxRetries N runs N+1 times before giving up", async () => {
43
+ let calls = 0;
44
+ await retry(async () => {
45
+ calls++;
46
+ throw new Error("nope");
47
+ }, { maxRetries: 2 }).catch(() => { /* expected to reject */ });
48
+ must(calls).equal(3);
49
+ });
50
+
51
+ it("stops early when earlyBreak returns true", async () => {
52
+ let calls = 0;
53
+ const seenCounts: number[] = [];
54
+ await retry(async () => {
55
+ calls++;
56
+ throw new Error("stop");
57
+ }, {
58
+ maxRetries: 10,
59
+ earlyBreak: (_e, count) => {
60
+ seenCounts.push(count);
61
+ return calls === 2;
62
+ },
63
+ }).catch(() => { /* expected to reject */ });
64
+ must(calls).equal(2);
65
+ must(seenCounts).eql([0, 1]);
66
+ });
67
+
68
+ it("waits between retries, passing the retry count to the wait function", async () => {
69
+ let calls = 0;
70
+ const waitCounts: number[] = [];
71
+ const result = await retry(async () => {
72
+ calls++;
73
+ if (calls < 3) {
74
+ throw new Error("fail");
75
+ }
76
+ return "done";
77
+ }, {
78
+ waitBetween: (count) => {
79
+ waitCounts.push(count);
80
+ return 10;
81
+ },
82
+ });
83
+ must(result).equal("done");
84
+ must(calls).equal(3);
85
+ must(waitCounts).eql([1, 2]);
86
+ });
87
+ });
package/src/retry.ts CHANGED
@@ -49,5 +49,3 @@ const retry = async <T>(fn: () => Promise<T>, options?: Options): Promise<T> =>
49
49
  export {
50
50
  retry,
51
51
  };
52
-
53
- // TODO unit tests
@@ -47,4 +47,13 @@ describe("samples", () => {
47
47
  const elements = [1, 2, 3];
48
48
  must(() => samples(elements, -1)).throw();
49
49
  });
50
+
51
+ it("should not pad with undefined when allowShuffle and elementsToPick exceed length", async () => {
52
+ const elements = [1, 2, 3];
53
+ // BUG: with allowShuffle the early-return is skipped and the loop runs `elementsToPick`
54
+ // times; once the keys are exhausted it pushes `array[NaN]` (undefined) as padding.
55
+ const result = samples(elements, 5, true);
56
+ must(result).have.length(3);
57
+ must(result).not.include(undefined);
58
+ });
50
59
  });
package/src/samples.ts CHANGED
@@ -21,10 +21,13 @@ const samples = <T>(array: T[], elementsToPick: number, allowShuffle = false): T
21
21
  }
22
22
 
23
23
  const keys = Object.keys(array);
24
+ // when shuffling we may be asked for more than we have - never pick more than exists, or we'd
25
+ // run out of keys and pad the result with `undefined`
26
+ const toPick = Math.min(elementsToPick, keys.length);
24
27
  let picked = 0;
25
28
  const result: T[] = [];
26
29
 
27
- while (picked < elementsToPick) {
30
+ while (picked < toPick) {
28
31
  const indexOfKey = Math.floor(Math.random() * keys.length);
29
32
  const indexOfArray = Number(keys[indexOfKey]);
30
33
  const element = array[indexOfArray]!;
@@ -0,0 +1,18 @@
1
+ import must from "must"; // eslint-disable-line @typescript-eslint/no-shadow
2
+
3
+ import { scale } from "./scale";
4
+
5
+ describe("scale", () => {
6
+ it("maps a value from one range to another", async () => {
7
+ must(scale(0, 10, 0, 100, 5)).equal(50);
8
+ must(scale(10, 20, 0, 200, 15)).equal(100);
9
+ });
10
+
11
+ it("handles inverted target ranges", async () => {
12
+ must(scale(0, 10, 100, 0, 5)).equal(50);
13
+ });
14
+
15
+ it("throws when fromMin equals fromMax", async () => {
16
+ must(() => scale(5, 5, 0, 100, 5)).throw(/fromMin and fromMax must not be equal/u);
17
+ });
18
+ });
package/src/scale.ts CHANGED
@@ -8,8 +8,12 @@
8
8
  * @param {number} toMin
9
9
  * @param {number} toMax
10
10
  * @param {number} number
11
+ * @throws {Error} when `fromMin` equals `fromMax` (the source range has zero width)
11
12
  */
12
13
  const scale = (fromMin: number, fromMax: number, toMin: number, toMax: number, number: number): number => {
14
+ if (fromMin === fromMax) {
15
+ throw new Error("[scale] fromMin and fromMax must not be equal");
16
+ }
13
17
  return toMin + ((number - fromMin) / (fromMax - fromMin) * (toMax - toMin));
14
18
  };
15
19
 
@@ -60,4 +60,14 @@ describe("unserialize", () => {
60
60
  0x35, 0x6a, 0x00, 0x22, 0x6e, 0x3a, 0x31, 0x22, 0x00,
61
61
  ]))).eql([1]);
62
62
  });
63
+
64
+ it("unserializes multiple various arguments", async () => {
65
+ must(unserialize(Buffer.from([
66
+ 0x35, 0x66, 0x00, 0x70, 0x69, 0x6e, 0x67, 0x32, 0x00,
67
+ 0x35, 0x6a, 0x00, 0x22, 0x6e, 0x3a, 0x35, 0x22, 0x00,
68
+ 0x35, 0x6a, 0x00, 0x22, 0x62, 0x3a, 0x31, 0x22, 0x00,
69
+ 0x35, 0x6a, 0x00, 0x22, 0x6e, 0x3a, 0x36, 0x22, 0x00,
70
+ 0x35, 0x6a, 0x00, 0x22, 0x6e, 0x3a, 0x39, 0x22, 0x00,
71
+ ]))).eql(["ping2", 5, true, 6, 9]);
72
+ });
63
73
  });
@@ -1,8 +1,6 @@
1
1
  import { deserialize } from "../deserialize.js";
2
2
  import { BINARY_MARK_BIN, BINARY_MARK_MAP, BINARY_MARK_STRING } from "./const.js";
3
3
 
4
- const MAX_DATA_PARTS = 4;
5
-
6
4
  const NOT_FOUND = -1;
7
5
  const LAST_CHAR = -1;
8
6
 
@@ -29,8 +27,7 @@ const unserializeFromBuffer = <RT extends any[] = unknown[]>( // eslint-disable-
29
27
  let startPoint = 0;
30
28
  const result = [];
31
29
 
32
- let i = 0;
33
- while (i++ < MAX_DATA_PARTS) {
30
+ while (startPoint < intData.length) {
34
31
  const dataSplitPoint = intData.indexOf(0, startPoint); // find null
35
32
  if (dataSplitPoint === NOT_FOUND) { // no null found = no data
36
33
  break;
@@ -23,6 +23,7 @@
23
23
  * { height: 480, bitrate: 300 },
24
24
  * { height: 720, bitrate: 200 },
25
25
  * { height: 720 }, // bitrate assumed to be 150
26
+ * { height: 720, bitrate: undefined }, // bitrate assumed to be 150
26
27
  * { height: 720, bitrate: 100 },
27
28
  * ]
28
29
  * ```
@@ -133,4 +133,39 @@ describe("throttle", function() {
133
133
  diffs[2].must.be.gte(300);
134
134
  diffs[2].must.be.lt(350);
135
135
  });
136
+
137
+ describe("flush", function() {
138
+ it("runs the pending trailing call immediately and only once", async function() {
139
+ let calls = 0;
140
+ const fn = () => {
141
+ calls++;
142
+ return calls;
143
+ };
144
+
145
+ const throttled = throttle(fn, 100, { leading: false, trailing: true });
146
+ throttled(); // schedules a trailing call ~100ms from now
147
+ throttled.flush(); // should run that planned call right now
148
+
149
+ calls.must.equal(1);
150
+
151
+ // flush must NOT leave (or schedule) another planned call behind
152
+ await wait(200);
153
+ calls.must.equal(1);
154
+ });
155
+
156
+ it("is a no-op returning the last result when nothing is pending", async function() {
157
+ let calls = 0;
158
+ const fn = () => {
159
+ calls++;
160
+ return calls;
161
+ };
162
+
163
+ const throttled = throttle(fn, 100, { leading: true, trailing: true });
164
+ throttled(); // leading call runs immediately, nothing is pending afterwards
165
+ calls.must.equal(1);
166
+
167
+ throttled.flush();
168
+ calls.must.equal(1); // flush did not call fn again
169
+ });
170
+ });
136
171
  });
package/src/throttle.ts CHANGED
@@ -9,15 +9,15 @@ interface Opts {
9
9
  trailing?: boolean;
10
10
  }
11
11
 
12
- interface Extras {
12
+ interface Extras<R = unknown> {
13
13
  /**
14
14
  * Stops any planned calls (and resets the `time` array progress)
15
15
  */
16
16
  cancel: () => void;
17
17
  /**
18
- * Immediately runs planned call.
18
+ * Immediately runs planned call, returning its result (or the last cached result if nothing is planned).
19
19
  */
20
- flush: () => void;
20
+ flush: () => R | undefined;
21
21
  }
22
22
 
23
23
  const defaultOptions: Required<Opts> = {
@@ -48,7 +48,7 @@ type CanReturnUndefined<F extends (...args: any[]) => any> = (...args: Parameter
48
48
  */
49
49
  const throttle = <RT, F extends (...args: any[]) => RT>( // eslint-disable-line max-lines-per-function, @typescript-eslint/no-explicit-any
50
50
  fn: F, time: number | [number, ...number[]] = 0, options?: Opts,
51
- ): CanReturnUndefined<F> & Extras => {
51
+ ): CanReturnUndefined<F> & Extras<RT> => {
52
52
  const opts: Required<Opts> = {
53
53
  leading: options?.leading ?? defaultOptions.leading,
54
54
  trailing: options?.trailing ?? defaultOptions.trailing,
@@ -109,7 +109,7 @@ const throttle = <RT, F extends (...args: any[]) => RT>( // eslint-disable-line
109
109
  }, lastRun ? (lastTime - diffLastRun + 1) : lastTime);
110
110
 
111
111
  return lastResult;
112
- }) as (CanReturnUndefined<F> & Extras);
112
+ }) as (CanReturnUndefined<F> & Extras<RT>);
113
113
 
114
114
  throttledFn.cancel = () => {
115
115
  timeoutId !== null && clearTimeout(timeoutId);
@@ -130,11 +130,10 @@ const throttle = <RT, F extends (...args: any[]) => RT>( // eslint-disable-line
130
130
  };
131
131
  throttledFn.flush = () => {
132
132
  if (timeoutId !== null) {
133
- lastRun = Date.now();
134
- lastResult = fn(...lastArgs);
135
133
  clearTimeout(timeoutId);
136
134
  timeoutId = null;
137
- return throttledFn(...lastArgs);
135
+ lastRun = Date.now();
136
+ lastResult = fn(...lastArgs);
138
137
  }
139
138
  return lastResult;
140
139
  };
@@ -19,4 +19,9 @@ describe("trimEnd", () => {
19
19
  must(trimEnd("abc", "abc")).equal("");
20
20
  must(trimEnd("a", "a")).equal("");
21
21
  });
22
+
23
+ it("should return the source unchanged when characters is empty", async () => {
24
+ must(trimEnd("hello", "")).equal("hello");
25
+ must(trimEnd("", "")).equal("");
26
+ });
22
27
  });
package/src/trimEnd.ts CHANGED
@@ -10,6 +10,9 @@
10
10
  * trimEnd("!aaa!!", "!"); // "!aaa"
11
11
  */
12
12
  const trimEnd = (source: string, characters: string): string => {
13
+ if (characters === "") {
14
+ return source;
15
+ }
13
16
  let s = source;
14
17
  while (s.endsWith(characters)) {
15
18
  s = s.slice(0, -characters.length);
@@ -19,4 +19,9 @@ describe("trimStart", () => {
19
19
  must(trimStart("abc", "abc")).equal("");
20
20
  must(trimStart("aaa", "a")).equal("");
21
21
  });
22
+
23
+ it("should return the source unchanged when characters is empty", async () => {
24
+ must(trimStart("hello", "")).equal("hello");
25
+ must(trimStart("", "")).equal("");
26
+ });
22
27
  });
package/src/trimStart.ts CHANGED
@@ -10,6 +10,9 @@
10
10
  * trimStart("!!aaa!", "!"); // "aaa!"
11
11
  */
12
12
  const trimStart = (source: string, characters: string): string => {
13
+ if (characters === "") {
14
+ return source;
15
+ }
13
16
  let s = source;
14
17
  while (s.startsWith(characters)) {
15
18
  s = s.slice(characters.length);
@@ -53,6 +53,19 @@ describe("waitFor", () => {
53
53
  must(Date.now() - start).be.gte(300);
54
54
  });
55
55
 
56
+ it("falls back to the default interval when other options are given without `interval`", async () => {
57
+ const spy = createSpy(() => false);
58
+
59
+ // only `timeout` is provided, `interval` is omitted -> should still use the 50ms default.
60
+ // BUG: implementation reads the raw `options.interval` (undefined) instead of the merged
61
+ // `opts.interval`, so setTimeout fires at 0ms and the check busy-loops.
62
+ waitFor(spy, { timeout: 250 }).catch(() => { /* expected to reject on timeout */ });
63
+ await wait(120);
64
+
65
+ // with the default 50ms interval checks land at ~0/50/100ms => ~3 calls by now
66
+ must(spy.__spy.calls.length).be.lte(4);
67
+ });
68
+
56
69
  it("time outs after given time", async () => {
57
70
  const spy = createSpy(() => false);
58
71
  // eslint-disable-next-line @typescript-eslint/await-thenable
@@ -100,6 +113,54 @@ describe("waitFor", () => {
100
113
  });
101
114
  });
102
115
 
116
+ describe("error stack traces point at the caller", () => {
117
+ // Helper with a recognizable name so we can assert it appears in the stack.
118
+ const callerOfWaitFor = <T>(...args: Parameters<typeof waitFor<T>>): Promise<T> => {
119
+ return waitFor<T>(...args);
120
+ };
121
+
122
+ it("on timeout", async () => {
123
+ await callerOfWaitFor(() => false, { interval: 40, timeout: 100 }).then(() => {
124
+ throw new Error("Should not resolve");
125
+ }, (e: unknown) => {
126
+ must((e as Error).message).equal("[waitFor] Timeout");
127
+ must((e as Error).stack).include("callerOfWaitFor");
128
+ });
129
+ });
130
+
131
+ it("on max tries reached", async () => {
132
+ await callerOfWaitFor(() => false, { interval: 40, maxTries: 2 }).then(() => {
133
+ throw new Error("Should not resolve");
134
+ }, (e: unknown) => {
135
+ must((e as Error).message).equal("[waitFor] Max tries reached");
136
+ must((e as Error).stack).include("callerOfWaitFor");
137
+ });
138
+ });
139
+
140
+ it("on invalid maxTries", async () => {
141
+ await callerOfWaitFor(() => null, { maxTries: 0 }).then(() => {
142
+ throw new Error("Should not resolve");
143
+ }, (e: unknown) => {
144
+ must(e).instanceOf(TypeError);
145
+ must((e as Error).stack).include("callerOfWaitFor");
146
+ });
147
+ });
148
+
149
+ it("on check function throwing, and preserves original error as cause", async () => {
150
+ const original = new Error("boom");
151
+ await callerOfWaitFor(() => {
152
+ throw original;
153
+ }, { interval: 40 }).then(() => {
154
+ throw new Error("Should not resolve");
155
+ }, (e: unknown) => {
156
+ must((e as Error).message).equal("[waitFor] check function threw an error");
157
+ must((e as Error).stack).include("callerOfWaitFor");
158
+ must((e as Error & { cause?: unknown }).cause).equal(original);
159
+ must((e as Error & { details?: { error?: unknown } }).details!.error).equal(original);
160
+ });
161
+ });
162
+ });
163
+
103
164
  describe("treats most falsy values as succeeded check", () => {
104
165
  it("numeric zero", async () => {
105
166
  const result = await waitFor(() => 0);