@naturalcycles/js-lib 14.75.0 → 14.78.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.
- package/dist/decorators/logMethod.decorator.js +1 -3
- package/dist/decorators/timeout.decorator.js +9 -1
- package/dist/error/try.d.ts +20 -0
- package/dist/error/try.js +47 -1
- package/dist/index.d.ts +20 -20
- package/dist/index.js +20 -52
- package/dist/promise/pRetry.d.ts +7 -2
- package/dist/promise/pRetry.js +4 -4
- package/dist/promise/pTimeout.d.ts +10 -1
- package/dist/promise/pTimeout.js +43 -29
- package/dist/seq/seq.d.ts +1 -0
- package/dist/seq/seq.js +10 -0
- package/dist/string/stringifyAny.js +12 -10
- package/dist/types.d.ts +2 -2
- package/dist/unit/size.util.d.ts +6 -0
- package/dist/unit/size.util.js +33 -10
- package/dist-esm/decorators/logMethod.decorator.js +2 -4
- package/dist-esm/decorators/timeout.decorator.js +9 -1
- package/dist-esm/error/try.js +43 -0
- package/dist-esm/index.js +20 -20
- package/dist-esm/promise/pRetry.js +4 -4
- package/dist-esm/promise/pTimeout.js +40 -28
- package/dist-esm/seq/seq.js +10 -0
- package/dist-esm/string/stringifyAny.js +12 -10
- package/dist-esm/unit/size.util.js +31 -9
- package/package.json +1 -1
- package/src/decorators/logMethod.decorator.ts +2 -4
- package/src/decorators/timeout.decorator.ts +12 -1
- package/src/error/try.ts +44 -0
- package/src/index.ts +20 -60
- package/src/promise/pRetry.ts +13 -5
- package/src/promise/pTimeout.ts +41 -29
- package/src/seq/seq.ts +10 -0
- package/src/string/stringifyAny.ts +13 -9
- package/src/types.ts +12 -3
- package/src/unit/size.util.ts +22 -6
package/src/promise/pRetry.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import { _since, _stringifyAny, CommonLogger } from '..'
|
|
1
|
+
import { _since, _stringifyAny, AnyFunction, CommonLogger } from '..'
|
|
2
2
|
|
|
3
3
|
export interface PRetryOptions {
|
|
4
|
+
/**
|
|
5
|
+
* If set - will be included in the error message.
|
|
6
|
+
* Can be used to identify the place in the code that failed.
|
|
7
|
+
*/
|
|
8
|
+
name?: string
|
|
9
|
+
|
|
4
10
|
/**
|
|
5
11
|
* How many attempts to try.
|
|
6
12
|
* First attempt is not a retry, but "initial try". It still counts.
|
|
@@ -75,13 +81,14 @@ export interface PRetryOptions {
|
|
|
75
81
|
* Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
|
|
76
82
|
*/
|
|
77
83
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
78
|
-
export function pRetry<T extends
|
|
84
|
+
export function pRetry<T extends AnyFunction>(fn: T, opt: PRetryOptions = {}): T {
|
|
79
85
|
const {
|
|
80
86
|
maxAttempts = 4,
|
|
81
87
|
delay: initialDelay = 1000,
|
|
82
88
|
delayMultiplier = 2,
|
|
83
89
|
predicate,
|
|
84
90
|
logger = console,
|
|
91
|
+
name = fn.name,
|
|
85
92
|
} = opt
|
|
86
93
|
|
|
87
94
|
let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt
|
|
@@ -93,7 +100,7 @@ export function pRetry<T extends Function>(fn: T, opt: PRetryOptions = {}): T {
|
|
|
93
100
|
logSuccess = logFirstAttempt = logRetries = logFailures = false
|
|
94
101
|
}
|
|
95
102
|
|
|
96
|
-
const fname = ['pRetry',
|
|
103
|
+
const fname = ['pRetry', name].filter(Boolean).join('.')
|
|
97
104
|
|
|
98
105
|
return async function (this: any, ...args: any[]) {
|
|
99
106
|
let delay = initialDelay
|
|
@@ -118,9 +125,10 @@ export function pRetry<T extends Function>(fn: T, opt: PRetryOptions = {}): T {
|
|
|
118
125
|
} catch (err) {
|
|
119
126
|
if (logFailures) {
|
|
120
127
|
logger.warn(
|
|
121
|
-
`${fname} attempt #${attempt} error in ${_since(started)}
|
|
128
|
+
`${fname} attempt #${attempt} error in ${_since(started)}:`,
|
|
129
|
+
_stringifyAny(err, {
|
|
122
130
|
includeErrorData: true,
|
|
123
|
-
})
|
|
131
|
+
}),
|
|
124
132
|
)
|
|
125
133
|
}
|
|
126
134
|
|
package/src/promise/pTimeout.ts
CHANGED
|
@@ -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
|
|
28
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
}
|
package/src/seq/seq.ts
CHANGED
|
@@ -140,6 +140,16 @@ export class Sequence<T> implements Iterable<T> {
|
|
|
140
140
|
a.push(v)
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
+
|
|
144
|
+
forEach(fn: (v: T, i: number) => void): void {
|
|
145
|
+
let i = -1
|
|
146
|
+
// eslint-disable-next-line no-constant-condition
|
|
147
|
+
while (true) {
|
|
148
|
+
const v = this.next()
|
|
149
|
+
if (v === END) return
|
|
150
|
+
fn(v, ++i)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
143
153
|
}
|
|
144
154
|
|
|
145
155
|
/**
|
|
@@ -76,16 +76,20 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
76
76
|
// Error or ErrorObject
|
|
77
77
|
//
|
|
78
78
|
|
|
79
|
+
// Omit "default" error name as non-informative
|
|
80
|
+
// UPD: no, it's still important to understand that we're dealing with Error and not just some string
|
|
81
|
+
// if (obj?.name === 'Error') {
|
|
82
|
+
// s = obj.message
|
|
83
|
+
// }
|
|
84
|
+
s = [obj.name, obj.message].join(': ')
|
|
85
|
+
|
|
79
86
|
if (opt.includeErrorStack && obj.stack) {
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
// s = obj.message
|
|
87
|
-
// }
|
|
88
|
-
s = [obj.name, obj.message].join(': ')
|
|
87
|
+
// Here we're using the previously-generated "title line" (e.g "Error: some_message"),
|
|
88
|
+
// concatenating it with the Stack (but without the title line of the Stack)
|
|
89
|
+
// This is to fix the rare error (happened with Got) where `err.message` was changed,
|
|
90
|
+
// but err.stack had "old" err.message
|
|
91
|
+
// This should "fix" that
|
|
92
|
+
s = [s, ...obj.stack.split('\n').slice(1)].join('\n')
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
if (_isErrorObject(obj)) {
|
package/src/types.ts
CHANGED
|
@@ -122,9 +122,18 @@ export type ValueOf<T> = T[keyof T]
|
|
|
122
122
|
|
|
123
123
|
export type KeyValueTuple<K, V> = [key: K, value: V]
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
// Exclude<something, undefined> is used here to support StringMap<OBJ> (because values of StringMap add `undefined`)
|
|
126
|
+
export type ObjectMapper<OBJ, OUT> = (
|
|
127
|
+
key: string,
|
|
128
|
+
value: Exclude<OBJ[keyof OBJ], undefined>,
|
|
129
|
+
obj: OBJ,
|
|
130
|
+
) => OUT
|
|
131
|
+
|
|
132
|
+
export type ObjectPredicate<OBJ> = (
|
|
133
|
+
key: keyof OBJ,
|
|
134
|
+
value: Exclude<OBJ[keyof OBJ], undefined>,
|
|
135
|
+
obj: OBJ,
|
|
136
|
+
) => boolean
|
|
128
137
|
|
|
129
138
|
/**
|
|
130
139
|
* Allows to identify instance of Class by `instanceId`.
|
package/src/unit/size.util.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
export function _gb(b: number): number {
|
|
2
|
-
return Math.round(b /
|
|
2
|
+
return Math.round(b / 1024 ** 3)
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
export function _mb(b: number): number {
|
|
6
|
-
return Math.round(b /
|
|
6
|
+
return Math.round(b / 1024 ** 2)
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function _kb(b: number): number {
|
|
@@ -14,8 +14,24 @@ export function _kb(b: number): number {
|
|
|
14
14
|
* Byte size to Human byte size string
|
|
15
15
|
*/
|
|
16
16
|
export function _hb(b = 0): string {
|
|
17
|
-
if (b <
|
|
18
|
-
if (b <
|
|
19
|
-
if (b <
|
|
20
|
-
return `${
|
|
17
|
+
if (b < 1024) return `${Math.round(b)} byte`
|
|
18
|
+
if (b < 1024 ** 2) return `${(b / 1024).toPrecision(3)} Kb`
|
|
19
|
+
if (b < 1024 ** 3) return `${(b / 1024 ** 2).toPrecision(3)} Mb`
|
|
20
|
+
if (b < 1024 ** 4) return `${(b / 1024 ** 3).toPrecision(3)} Gb`
|
|
21
|
+
if (b < 1024 ** 5) return `${(b / 1024 ** 4).toPrecision(3)} Tb`
|
|
22
|
+
return `${Math.round(b / 1024 ** 4)} Tb`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* hc stands for "human count", similar to "human bytes" `_hb` function.
|
|
27
|
+
* Helpful to print big numbers, as it adds `K` (kilo), `M` (mega), etc to make
|
|
28
|
+
* them more readable.
|
|
29
|
+
*/
|
|
30
|
+
export function _hc(c = 0): string {
|
|
31
|
+
if (c < 10 ** 4) return String(c)
|
|
32
|
+
if (c < 10 ** 6) return (c / 10 ** 3).toPrecision(3) + ' K'
|
|
33
|
+
if (c < 10 ** 9) return (c / 10 ** 6).toPrecision(3) + ' M' // million
|
|
34
|
+
if (c < 10 ** 12) return (c / 10 ** 9).toPrecision(3) + ' B' // billion
|
|
35
|
+
if (c < 10 ** 15) return (c / 10 ** 12).toPrecision(3) + ' T' // trillion
|
|
36
|
+
return Math.round(c / 10 ** 12) + ' T'
|
|
21
37
|
}
|