@naturalcycles/js-lib 14.73.0 → 14.76.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 (38) hide show
  1. package/dist/decorators/logMethod.decorator.js +1 -3
  2. package/dist/decorators/timeout.decorator.js +9 -1
  3. package/dist/error/try.d.ts +20 -0
  4. package/dist/error/try.js +47 -1
  5. package/dist/index.d.ts +20 -20
  6. package/dist/index.js +20 -52
  7. package/dist/math/math.util.d.ts +4 -0
  8. package/dist/math/math.util.js +18 -1
  9. package/dist/math/stack.util.d.ts +3 -2
  10. package/dist/math/stack.util.js +8 -5
  11. package/dist/promise/pTimeout.d.ts +10 -1
  12. package/dist/promise/pTimeout.js +43 -29
  13. package/dist/seq/seq.d.ts +30 -7
  14. package/dist/seq/seq.js +126 -9
  15. package/dist/string/stringifyAny.js +12 -10
  16. package/dist/unit/size.util.d.ts +6 -0
  17. package/dist/unit/size.util.js +33 -10
  18. package/dist-esm/decorators/logMethod.decorator.js +2 -4
  19. package/dist-esm/decorators/timeout.decorator.js +9 -1
  20. package/dist-esm/error/try.js +43 -0
  21. package/dist-esm/index.js +20 -20
  22. package/dist-esm/math/math.util.js +16 -0
  23. package/dist-esm/math/stack.util.js +9 -6
  24. package/dist-esm/promise/pTimeout.js +40 -28
  25. package/dist-esm/seq/seq.js +124 -8
  26. package/dist-esm/string/stringifyAny.js +12 -10
  27. package/dist-esm/unit/size.util.js +31 -9
  28. package/package.json +1 -1
  29. package/src/decorators/logMethod.decorator.ts +2 -4
  30. package/src/decorators/timeout.decorator.ts +12 -1
  31. package/src/error/try.ts +44 -0
  32. package/src/index.ts +20 -60
  33. package/src/math/math.util.ts +21 -0
  34. package/src/math/stack.util.ts +11 -7
  35. package/src/promise/pTimeout.ts +41 -29
  36. package/src/seq/seq.ts +144 -13
  37. package/src/string/stringifyAny.ts +13 -9
  38. package/src/unit/size.util.ts +22 -6
@@ -1,4 +1,4 @@
1
- import { END, SKIP } from '../types';
1
+ import { END, SKIP, } from '../types';
2
2
  /**
3
3
  * Inspired by Kotlin Sequences.
4
4
  * Similar to arrays, but with lazy evaluation, abortable.
@@ -7,7 +7,7 @@ import { END, SKIP } from '../types';
7
7
  *
8
8
  * @experimental
9
9
  */
10
- export class Seq {
10
+ export class Sequence {
11
11
  constructor(initialValue, nextFn) {
12
12
  this.nextFn = nextFn;
13
13
  this.sentInitialValue = false;
@@ -23,18 +23,18 @@ export class Seq {
23
23
  };
24
24
  }
25
25
  static create(initialValue, nextFn) {
26
- return new Seq(initialValue, nextFn);
26
+ return new Sequence(initialValue, nextFn);
27
27
  }
28
28
  static range(minIncl, maxExcl, step = 1) {
29
29
  const max = maxExcl - step;
30
- return new Seq(minIncl, n => (n < max ? n + step : END));
30
+ return new Sequence(minIncl, n => (n < max ? n + step : END));
31
31
  }
32
32
  static from(a) {
33
33
  const it = a[Symbol.iterator]();
34
34
  const v = it.next();
35
35
  if (v.done)
36
- return new Seq(END, () => { });
37
- return new Seq(v.value, () => {
36
+ return new Sequence(END, () => { });
37
+ return new Sequence(v.value, () => {
38
38
  const v = it.next();
39
39
  if (v.done)
40
40
  return END;
@@ -42,7 +42,7 @@ export class Seq {
42
42
  });
43
43
  }
44
44
  static empty() {
45
- return new Seq(END, () => { });
45
+ return new Sequence(END, () => { });
46
46
  }
47
47
  next() {
48
48
  if (this.currentValue === END)
@@ -127,10 +127,126 @@ export class Seq {
127
127
  a.push(v);
128
128
  }
129
129
  }
130
+ forEach(fn) {
131
+ let i = -1;
132
+ // eslint-disable-next-line no-constant-condition
133
+ while (true) {
134
+ const v = this.next();
135
+ if (v === END)
136
+ return;
137
+ fn(v, ++i);
138
+ }
139
+ }
130
140
  }
131
141
  /**
132
142
  * Convenience function to create a Sequence.
133
143
  */
134
144
  export function _seq(initialValue, nextFn) {
135
- return Seq.create(initialValue, nextFn);
145
+ return Sequence.create(initialValue, nextFn);
146
+ }
147
+ /* eslint-disable no-await-in-loop */
148
+ /**
149
+ * Experimental.
150
+ * Feasibility to be proven.
151
+ *
152
+ * @experimental
153
+ */
154
+ export class AsyncSequence {
155
+ constructor(initialValue, nextFn) {
156
+ this.nextFn = nextFn;
157
+ this.sentInitialValue = false;
158
+ this.i = -1;
159
+ this.currentValue = initialValue;
160
+ }
161
+ [Symbol.asyncIterator]() {
162
+ return {
163
+ next: async () => {
164
+ const value = await this.next();
165
+ return value === END ? { done: true, value: undefined } : { value };
166
+ },
167
+ };
168
+ }
169
+ static create(initialValue, nextFn) {
170
+ return new AsyncSequence(initialValue, nextFn);
171
+ }
172
+ static async from(a) {
173
+ const it = a[Symbol.asyncIterator]();
174
+ const v = await it.next();
175
+ if (v.done)
176
+ return new AsyncSequence(END, () => { });
177
+ return new AsyncSequence(v.value, async () => {
178
+ const v = await it.next();
179
+ if (v.done)
180
+ return END;
181
+ return v.value;
182
+ });
183
+ }
184
+ static empty() {
185
+ return new AsyncSequence(END, () => { });
186
+ }
187
+ async next() {
188
+ if (this.currentValue === END)
189
+ return END;
190
+ this.i++;
191
+ let v;
192
+ if (!this.sentInitialValue) {
193
+ this.sentInitialValue = true;
194
+ v = this.currentValue;
195
+ }
196
+ else {
197
+ v = await this.nextFn(this.currentValue, this.i);
198
+ }
199
+ // console.log(`_seq`, v)
200
+ if (v === SKIP)
201
+ return await this.next();
202
+ return (this.currentValue = v);
203
+ }
204
+ // Final functions - return final value, not a chained sequence
205
+ async find(predicate) {
206
+ do {
207
+ const v = await this.next();
208
+ if (v === END)
209
+ return; // not found, end of sequence
210
+ const r = await predicate(v, this.i);
211
+ if (r === END)
212
+ return;
213
+ if (r)
214
+ return v;
215
+ // otherwise proceed
216
+ } while (true); // eslint-disable-line no-constant-condition
217
+ }
218
+ async some(predicate) {
219
+ do {
220
+ const v = await this.next();
221
+ if (v === END)
222
+ return false;
223
+ const r = await predicate(v, this.i);
224
+ if (r === END)
225
+ return false;
226
+ if (r)
227
+ return true;
228
+ } while (true); // eslint-disable-line no-constant-condition
229
+ }
230
+ async every(predicate) {
231
+ do {
232
+ const v = await this.next();
233
+ if (v === END)
234
+ return true;
235
+ const r = await predicate(v, this.i);
236
+ if (r === END)
237
+ return true;
238
+ if (!r)
239
+ return false;
240
+ } while (true); // eslint-disable-line no-constant-condition
241
+ }
242
+ async toArray() {
243
+ const a = [];
244
+ // eslint-disable-next-line no-constant-condition
245
+ while (true) {
246
+ const v = await this.next();
247
+ if (v === END)
248
+ return a;
249
+ a.push(v);
250
+ }
251
+ }
136
252
  }
@@ -41,17 +41,19 @@ export function _stringifyAny(obj, opt = {}) {
41
41
  //
42
42
  // Error or ErrorObject
43
43
  //
44
+ // Omit "default" error name as non-informative
45
+ // UPD: no, it's still important to understand that we're dealing with Error and not just some string
46
+ // if (obj?.name === 'Error') {
47
+ // s = obj.message
48
+ // }
49
+ s = [obj.name, obj.message].join(': ');
44
50
  if (opt.includeErrorStack && obj.stack) {
45
- // Stack includes message
46
- s = obj.stack;
47
- }
48
- else {
49
- // Omit "default" error name as non-informative
50
- // UPD: no, it's still important to understand that we're dealing with Error and not just some string
51
- // if (obj?.name === 'Error') {
52
- // s = obj.message
53
- // }
54
- s = [obj.name, obj.message].join(': ');
51
+ // Here we're using the previously-generated "title line" (e.g "Error: some_message"),
52
+ // concatenating it with the Stack (but without the title line of the Stack)
53
+ // This is to fix the rare error (happened with Got) where `err.message` was changed,
54
+ // but err.stack had "old" err.message
55
+ // This should "fix" that
56
+ s = [s, ...obj.stack.split('\n').slice(1)].join('\n');
55
57
  }
56
58
  if (_isErrorObject(obj)) {
57
59
  if (_isHttpErrorObject(obj)) {
@@ -1,8 +1,8 @@
1
1
  export function _gb(b) {
2
- return Math.round(b / (1024 * 1024 * 1024));
2
+ return Math.round(b / 1024 ** 3);
3
3
  }
4
4
  export function _mb(b) {
5
- return Math.round(b / (1024 * 1024));
5
+ return Math.round(b / 1024 ** 2);
6
6
  }
7
7
  export function _kb(b) {
8
8
  return Math.round(b / 1024);
@@ -11,11 +11,33 @@ export function _kb(b) {
11
11
  * Byte size to Human byte size string
12
12
  */
13
13
  export function _hb(b = 0) {
14
- if (b < 800)
15
- return `${Math.round(b)} byte(s)`;
16
- if (b < 800 * 1024)
17
- return `${Math.round(b / 1024)} Kb`;
18
- if (b < 800 * 1024 * 1024)
19
- return `${Math.round(b / 1024 / 1024)} Mb`;
20
- return `${Math.round(b / 1024 / 1024 / 1024)} Gb`;
14
+ if (b < 1024)
15
+ return `${Math.round(b)} byte`;
16
+ if (b < 1024 ** 2)
17
+ return `${(b / 1024).toPrecision(3)} Kb`;
18
+ if (b < 1024 ** 3)
19
+ return `${(b / 1024 ** 2).toPrecision(3)} Mb`;
20
+ if (b < 1024 ** 4)
21
+ return `${(b / 1024 ** 3).toPrecision(3)} Gb`;
22
+ if (b < 1024 ** 5)
23
+ return `${(b / 1024 ** 4).toPrecision(3)} Tb`;
24
+ return `${Math.round(b / 1024 ** 4)} Tb`;
25
+ }
26
+ /**
27
+ * hc stands for "human count", similar to "human bytes" `_hb` function.
28
+ * Helpful to print big numbers, as it adds `K` (kilo), `M` (mega), etc to make
29
+ * them more readable.
30
+ */
31
+ export function _hc(c = 0) {
32
+ if (c < 10 ** 4)
33
+ return String(c);
34
+ if (c < 10 ** 6)
35
+ return (c / 10 ** 3).toPrecision(3) + ' K';
36
+ if (c < 10 ** 9)
37
+ return (c / 10 ** 6).toPrecision(3) + ' M'; // million
38
+ if (c < 10 ** 12)
39
+ return (c / 10 ** 9).toPrecision(3) + ' B'; // billion
40
+ if (c < 10 ** 15)
41
+ return (c / 10 ** 12).toPrecision(3) + ' T'; // trillion
42
+ return Math.round(c / 10 ** 12) + ' T';
21
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.73.0",
3
+ "version": "14.76.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -1,4 +1,4 @@
1
- import { SimpleMovingAverage, _stringifyAny, CommonLogger } from '..'
1
+ import { SimpleMovingAverage, _stringifyAny, CommonLogger, _assert } from '..'
2
2
  import { _ms } from '../time/time.util'
3
3
  import { _getArgsSignature, _getMethodSignature } from './decorator.util'
4
4
 
@@ -69,9 +69,7 @@ export interface LogMethodOptions {
69
69
  // eslint-disable-next-line @typescript-eslint/naming-convention
70
70
  export function _LogMethod(opt: LogMethodOptions = {}): MethodDecorator {
71
71
  return (target, key, descriptor) => {
72
- if (typeof descriptor.value !== 'function') {
73
- throw new TypeError('@_LogMethod can be applied only to methods')
74
- }
72
+ _assert(typeof descriptor.value === 'function', '@_LogMethod can be applied only to methods')
75
73
 
76
74
  const originalFn = descriptor.value
77
75
  const keyStr = String(key)
@@ -1,10 +1,21 @@
1
+ import { _assert } from '../error/assert'
1
2
  import { pTimeout, PTimeoutOptions } from '../promise/pTimeout'
3
+ import { _getMethodSignature } from './decorator.util'
2
4
 
3
5
  // eslint-disable-next-line @typescript-eslint/naming-convention
4
6
  export function _Timeout(opt: PTimeoutOptions): MethodDecorator {
5
7
  return (target, key, descriptor) => {
8
+ _assert(typeof descriptor.value === 'function', '@_Timeout can be applied only to methods')
9
+
6
10
  const originalFn = descriptor.value
7
- descriptor.value = pTimeout(originalFn as any, opt)
11
+ const keyStr = String(key)
12
+
13
+ descriptor.value = async function (this: typeof target, ...args: any[]) {
14
+ const ctx = this
15
+ opt.name ||= _getMethodSignature(ctx, keyStr)
16
+ return await pTimeout(originalFn.apply(this, args), opt)
17
+ } as any
18
+
8
19
  return descriptor
9
20
  }
10
21
  }
package/src/error/try.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { AnyFunction } from '../types'
2
+ import { AppError } from './app.error'
3
+
1
4
  /**
2
5
  * Calls a function, returns a Tuple of [error, value].
3
6
  * Allows to write shorter code that avoids `try/catch`.
@@ -42,3 +45,44 @@ export async function pTry<ERR = unknown, RETURN = void>(
42
45
  return [err as ERR, undefined as any]
43
46
  }
44
47
  }
48
+
49
+ /**
50
+ * It is thrown when Error was expected, but didn't happen
51
+ * ("pass" happened instead).
52
+ * "Pass" means "no error".
53
+ */
54
+ export class UnexpectedPassError extends AppError {
55
+ constructor() {
56
+ super('_expectedError passed unexpectedly')
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Calls `fn`, expects is to throw, catches the expected error and returns.
62
+ * If error was NOT thrown - throws UnexpectedPassError instead.
63
+ */
64
+ export function _expectedError<ERR = Error>(fn: AnyFunction): ERR {
65
+ try {
66
+ fn()
67
+ // Unexpected!
68
+ throw new UnexpectedPassError()
69
+ } catch (err) {
70
+ if (err instanceof UnexpectedPassError) throw err // re-throw
71
+ return err as ERR // this is expected!
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Awaits passed `promise`, expects is to throw (reject), catches the expected error and returns.
77
+ * If error was NOT thrown - throws UnexpectedPassError instead.
78
+ */
79
+ export async function pExpectedError<ERR = Error>(promise: Promise<any>): Promise<ERR> {
80
+ try {
81
+ await promise
82
+ // Unexpected!
83
+ throw new UnexpectedPassError()
84
+ } catch (err) {
85
+ if (err instanceof UnexpectedPassError) throw err // re-throw
86
+ return err as ERR // this is expected!
87
+ }
88
+ }
package/src/index.ts CHANGED
@@ -7,26 +7,17 @@ import {
7
7
  PromiseDecoratorResp,
8
8
  _createPromiseDecorator,
9
9
  } from './decorators/createPromiseDecorator'
10
- import { _debounce, _throttle } from './decorators/debounce'
11
- import { _Debounce, _Throttle } from './decorators/debounce.decorator'
12
- import { _getArgsSignature } from './decorators/decorator.util'
13
- import { _LogMethod } from './decorators/logMethod.decorator'
14
- import { _Memo } from './decorators/memo.decorator'
10
+ export * from './decorators/debounce'
11
+ export * from './decorators/debounce.decorator'
12
+ export * from './decorators/decorator.util'
13
+ export * from './decorators/logMethod.decorator'
14
+ export * from './decorators/memo.decorator'
15
15
  import { MemoCache } from './decorators/memo.util'
16
- import { _memoFn } from './decorators/memoFn'
17
- import { _Retry } from './decorators/retry.decorator'
18
- import { _Timeout } from './decorators/timeout.decorator'
19
- import { AppError } from './error/app.error'
20
- import {
21
- AssertionError,
22
- _assert,
23
- _assertDeepEquals,
24
- _assertEquals,
25
- _assertIsError,
26
- _assertIsNumber,
27
- _assertIsString,
28
- _assertTypeOf,
29
- } from './error/assert'
16
+ export * from './decorators/memoFn'
17
+ export * from './decorators/retry.decorator'
18
+ export * from './decorators/timeout.decorator'
19
+ export * from './error/app.error'
20
+ export * from './error/assert'
30
21
  import {
31
22
  Admin401ErrorData,
32
23
  Admin403ErrorData,
@@ -37,11 +28,11 @@ import {
37
28
  } from './error/error.model'
38
29
  export * from './error/error.util'
39
30
  import { ErrorMode } from './error/errorMode'
40
- import { HttpError } from './error/http.error'
41
- import { _try, pTry } from './error/try'
31
+ export * from './error/http.error'
32
+ export * from './error/try'
42
33
  import { TryCatchOptions, _TryCatch, _tryCatch } from './error/tryCatch'
43
- import { generateJsonSchemaFromData } from './json-schema/from-data/generateJsonSchemaFromData'
44
- import { JSON_SCHEMA_ORDER } from './json-schema/jsonSchema.cnst'
34
+ export * from './json-schema/from-data/generateJsonSchemaFromData'
35
+ export * from './json-schema/jsonSchema.cnst'
45
36
  import {
46
37
  JsonSchema,
47
38
  JsonSchemaAllOf,
@@ -61,7 +52,7 @@ import {
61
52
  JsonSchemaString,
62
53
  JsonSchemaTuple,
63
54
  } from './json-schema/jsonSchema.model'
64
- import { mergeJsonSchemaObjects } from './json-schema/jsonSchema.util'
55
+ export * from './json-schema/jsonSchema.util'
65
56
  import {
66
57
  jsonSchema,
67
58
  JsonSchemaAnyBuilder,
@@ -85,13 +76,13 @@ import { pMap, PMapOptions } from './promise/pMap'
85
76
  export * from './promise/pProps'
86
77
  import { pRetry, PRetryOptions } from './promise/pRetry'
87
78
  export * from './promise/pState'
88
- import { pTimeout, PTimeoutOptions } from './promise/pTimeout'
79
+ import { pTimeout, pTimeoutFn, PTimeoutOptions } from './promise/pTimeout'
89
80
  export * from './promise/pTuple'
90
81
  export * from './string/case'
91
82
  export * from './string/json.util'
92
83
  export * from './string/string.util'
93
84
  import { JsonStringifyFunction, StringifyAnyOptions, _stringifyAny } from './string/stringifyAny'
94
- import { _ms, _since } from './time/time.util'
85
+ export * from './time/time.util'
95
86
  import {
96
87
  Class,
97
88
  ConditionalExcept,
@@ -144,7 +135,7 @@ import {
144
135
  _stringMapEntries,
145
136
  _stringMapValues,
146
137
  } from './types'
147
- import { _gb, _hb, _kb, _mb } from './unit/size.util'
138
+ export * from './unit/size.util'
148
139
  import { is } from './vendor/is'
149
140
  import {
150
141
  CommonLogLevel,
@@ -158,7 +149,7 @@ import {
158
149
  CommonLogWithLevelFunction,
159
150
  commonLoggerCreate,
160
151
  } from './log/commonLogger'
161
- import { _safeJsonStringify } from './string/safeJsonStringify'
152
+ export * from './string/safeJsonStringify'
162
153
  import { PQueue, PQueueCfg } from './promise/pQueue'
163
154
  export * from './seq/seq'
164
155
  export * from './math/stack.util'
@@ -245,28 +236,10 @@ export type {
245
236
 
246
237
  export {
247
238
  is,
248
- _Memo,
249
- _memoFn,
250
- _LogMethod,
251
- _getArgsSignature,
252
239
  _createPromiseDecorator,
253
- AppError,
254
- HttpError,
255
- AssertionError,
256
- _assert,
257
- _assertEquals,
258
- _assertDeepEquals,
259
- _assertIsError,
260
- _assertIsString,
261
- _assertIsNumber,
262
- _assertTypeOf,
263
240
  _stringMapValues,
264
241
  _stringMapEntries,
265
242
  _objectKeys,
266
- _debounce,
267
- _throttle,
268
- _Debounce,
269
- _Throttle,
270
243
  pMap,
271
244
  _passthroughMapper,
272
245
  _passUndefinedMapper,
@@ -278,31 +251,18 @@ export {
278
251
  AggregatedError,
279
252
  pRetry,
280
253
  pTimeout,
281
- _Retry,
282
- _Timeout,
254
+ pTimeoutFn,
283
255
  _tryCatch,
284
256
  _TryCatch,
285
- _try,
286
- pTry,
287
257
  _stringifyAny,
288
- _ms,
289
- _since,
290
- _hb,
291
- _gb,
292
- _mb,
293
- _kb,
294
- mergeJsonSchemaObjects,
295
258
  jsonSchema,
296
259
  JsonSchemaAnyBuilder,
297
- JSON_SCHEMA_ORDER,
298
- generateJsonSchemaFromData,
299
260
  commonLoggerMinLevel,
300
261
  commonLoggerNoop,
301
262
  commonLogLevelNumber,
302
263
  commonLoggerPipe,
303
264
  commonLoggerPrefix,
304
265
  commonLoggerCreate,
305
- _safeJsonStringify,
306
266
  PQueue,
307
267
  END,
308
268
  SKIP,
@@ -47,6 +47,27 @@ export function _percentile(values: number[], pc: number): number {
47
47
  return _averageWeighted([sorted[floorPos]!, sorted[ceilPos]!], [1 - dec, dec])
48
48
  }
49
49
 
50
+ /**
51
+ * A tiny bit more efficient function than calling _percentile individually.
52
+ */
53
+ export function _percentiles(values: number[], pcs: number[]): Record<number, number> {
54
+ const r = {} as Record<number, number>
55
+
56
+ const sorted = _sortNumbers(values)
57
+
58
+ pcs.forEach(pc => {
59
+ // Floating pos in the range of [0; length - 1]
60
+ const pos = ((values.length - 1) * pc) / 100
61
+ const dec = pos % 1
62
+ const floorPos = Math.floor(pos)
63
+ const ceilPos = Math.ceil(pos)
64
+
65
+ r[pc] = _averageWeighted([sorted[floorPos]!, sorted[ceilPos]!], [1 - dec, dec])
66
+ })
67
+
68
+ return r
69
+ }
70
+
50
71
  /**
51
72
  * @example
52
73
  *
@@ -1,4 +1,4 @@
1
- import { _average, _percentile, _range } from '../index'
1
+ import { _average, _percentile, _percentiles, _range } from '../index'
2
2
 
3
3
  /**
4
4
  * Implements a "round-robin" Stack ("first-in last-out" aka FILO) with a limited size.
@@ -64,6 +64,14 @@ export class NumberStack extends Stack<number> {
64
64
  return this.items.length === 0 ? null : _average(this.items)
65
65
  }
66
66
 
67
+ median(): number {
68
+ return _percentile(this.items, 50)
69
+ }
70
+
71
+ medianOrNull(): number | null {
72
+ return this.items.length === 0 ? null : _percentile(this.items, 50)
73
+ }
74
+
67
75
  /**
68
76
  * `pc` is a number from 0 to 100 inclusive.
69
77
  */
@@ -80,11 +88,7 @@ export class NumberStack extends Stack<number> {
80
88
  return this.items.length === 0 ? null : _percentile(this.items, pc)
81
89
  }
82
90
 
83
- median(): number {
84
- return _percentile(this.items, 50)
85
- }
86
-
87
- medianOrNull(): number | null {
88
- return this.items.length === 0 ? null : _percentile(this.items, 50)
91
+ percentiles(pcs: number[]): Record<number, number> {
92
+ return _percentiles(this.items, pcs)
89
93
  }
90
94
  }
@@ -1,5 +1,8 @@
1
+ import { AppError } from '../error/app.error'
1
2
  import { AnyFunction } from '../types'
2
3
 
4
+ export class TimeoutError extends AppError {}
5
+
3
6
  export interface PTimeoutOptions {
4
7
  /**
5
8
  * Timeout in milliseconds.
@@ -24,37 +27,46 @@ export interface PTimeoutOptions {
24
27
  * Throws an Error if the Function is not resolved in a certain time.
25
28
  * If the Function rejects - passes this rejection further.
26
29
  */
27
- export function pTimeout<T extends AnyFunction>(fn: T, opt: PTimeoutOptions): T {
28
- // const fname = fn.name || 'function'
30
+ export function pTimeoutFn<T extends AnyFunction>(fn: T, opt: PTimeoutOptions): T {
31
+ opt.name ||= fn.name
32
+
33
+ return async function pTimeoutInternalFn(this: any, ...args: any[]) {
34
+ return await pTimeout(fn.apply(this, args), opt)
35
+ } as any
36
+ }
37
+
38
+ /**
39
+ * Decorates a Function with a timeout and immediately calls it.
40
+ * Throws an Error if the Function is not resolved in a certain time.
41
+ * If the Function rejects - passes this rejection further.
42
+ */
43
+ export async function pTimeout<T>(promise: Promise<T>, opt: PTimeoutOptions): Promise<T> {
44
+ // todo: check how we can automatically infer function name (only applicable to named functions)
29
45
  const { timeout, name, onTimeout } = opt
30
46
 
31
- return async function (this: any, ...args: any[]) {
32
- // eslint-disable-next-line no-async-promise-executor
33
- return await new Promise(async (resolve, reject) => {
34
- // Prepare the timeout timer
35
- const timer = setTimeout(() => {
36
- if (onTimeout) {
37
- try {
38
- resolve(onTimeout())
39
- } catch (err) {
40
- reject(err)
41
- }
42
- return
47
+ // eslint-disable-next-line no-async-promise-executor
48
+ return await new Promise(async (resolve, reject) => {
49
+ // Prepare the timeout timer
50
+ const timer = setTimeout(() => {
51
+ if (onTimeout) {
52
+ try {
53
+ resolve(onTimeout())
54
+ } catch (err) {
55
+ reject(err)
43
56
  }
44
-
45
- reject(
46
- new Error(`"${name || fn.name || 'pTimeout function'}" timed out after ${timeout} ms`),
47
- )
48
- }, timeout)
49
-
50
- // Execute the Function
51
- try {
52
- resolve(await fn.apply(this, args))
53
- } catch (err) {
54
- reject(err)
55
- } finally {
56
- clearTimeout(timer)
57
+ return
57
58
  }
58
- })
59
- } as any
59
+
60
+ reject(new TimeoutError(`"${name || 'pTimeout function'}" timed out after ${timeout} ms`))
61
+ }, timeout)
62
+
63
+ // Execute the Function
64
+ try {
65
+ resolve(await promise)
66
+ } catch (err) {
67
+ reject(err)
68
+ } finally {
69
+ clearTimeout(timer)
70
+ }
71
+ })
60
72
  }