@naturalcycles/js-lib 14.134.0 → 14.136.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 +2 -2
- package/dist/env.d.ts +14 -0
- package/dist/env.js +23 -0
- package/dist/error/error.util.d.ts +1 -1
- package/dist/error/error.util.js +2 -0
- package/dist/error/tryCatch.js +1 -3
- package/dist/http/fetcher.d.ts +2 -0
- package/dist/http/fetcher.js +104 -92
- package/dist/http/fetcher.model.d.ts +14 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/promise/abortable.d.ts +20 -0
- package/dist/promise/abortable.js +36 -0
- package/dist/promise/pDefer.d.ts +14 -1
- package/dist/promise/pDefer.js +2 -0
- package/dist/promise/pDelay.d.ts +18 -0
- package/dist/promise/pDelay.js +37 -2
- package/dist/promise/pRetry.d.ts +0 -8
- package/dist/promise/pRetry.js +37 -63
- package/dist/promise/pTimeout.d.ts +4 -6
- package/dist/promise/pTimeout.js +8 -10
- package/dist/string/stringifyAny.d.ts +0 -6
- package/dist/string/stringifyAny.js +0 -5
- package/dist/types.d.ts +3 -0
- package/dist/vendor/is.d.ts +2 -2
- package/dist-esm/decorators/logMethod.decorator.js +2 -2
- package/dist-esm/env.js +18 -0
- package/dist-esm/error/error.util.js +2 -0
- package/dist-esm/error/tryCatch.js +2 -4
- package/dist-esm/http/fetcher.js +111 -98
- package/dist-esm/index.js +2 -0
- package/dist-esm/promise/abortable.js +32 -0
- package/dist-esm/promise/pDefer.js +2 -0
- package/dist-esm/promise/pDelay.js +35 -1
- package/dist-esm/promise/pRetry.js +38 -61
- package/dist-esm/promise/pTimeout.js +8 -7
- package/dist-esm/string/stringifyAny.js +0 -5
- package/package.json +1 -1
- package/src/decorators/logMethod.decorator.ts +2 -2
- package/src/env.ts +19 -0
- package/src/error/error.util.ts +3 -1
- package/src/error/tryCatch.ts +2 -6
- package/src/http/fetcher.model.ts +14 -3
- package/src/http/fetcher.ts +117 -95
- package/src/index.ts +2 -0
- package/src/promise/abortable.ts +34 -0
- package/src/promise/pDefer.ts +19 -1
- package/src/promise/pDelay.ts +44 -2
- package/src/promise/pRetry.ts +41 -89
- package/src/promise/pState.ts +1 -1
- package/src/promise/pTimeout.ts +12 -14
- package/src/string/stringifyAny.ts +0 -13
- package/src/types.ts +3 -0
- package/src/vendor/is.ts +3 -3
package/src/promise/pDefer.ts
CHANGED
|
@@ -3,7 +3,22 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export interface DeferredPromise<T = void> extends Promise<T> {
|
|
5
5
|
resolve: (a?: T) => void
|
|
6
|
-
reject: (
|
|
6
|
+
reject: (err?: Error) => void
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Can be overridden.
|
|
10
|
+
* Otherwise will reject with "Aborted" or "Aborted: $reason" on abort().
|
|
11
|
+
*
|
|
12
|
+
* @experimental
|
|
13
|
+
*/
|
|
14
|
+
abort: (reason?: string) => void
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Rejects the promise with `new Error('Aborted: $reason')`.
|
|
18
|
+
*
|
|
19
|
+
* @experimental
|
|
20
|
+
*/
|
|
21
|
+
rejectAborted: (reason?: string) => void
|
|
7
22
|
}
|
|
8
23
|
|
|
9
24
|
/* eslint-disable @typescript-eslint/promise-function-async */
|
|
@@ -22,6 +37,9 @@ export function pDefer<T = void>(): DeferredPromise<T> {
|
|
|
22
37
|
|
|
23
38
|
promise.resolve = resolve
|
|
24
39
|
promise.reject = reject
|
|
40
|
+
promise.rejectAborted = reason =>
|
|
41
|
+
reject(new Error(['Aborted', reason].filter(Boolean).join(': ')))
|
|
42
|
+
promise.abort = reason => promise.rejectAborted(reason)
|
|
25
43
|
|
|
26
44
|
return promise
|
|
27
45
|
}
|
package/src/promise/pDelay.ts
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { PromisableFunction } from '../types'
|
|
2
|
+
import { DeferredPromise, pDefer } from './pDefer'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Promisified version of setTimeout.
|
|
6
|
+
*
|
|
7
|
+
* Can return a value.
|
|
8
|
+
* If value is instanceof Error - rejects the Promise instead of resolving.
|
|
9
|
+
*/
|
|
10
|
+
export async function pDelay<T>(ms = 0, value?: T): Promise<T> {
|
|
11
|
+
return await new Promise<T>((resolve, reject) =>
|
|
12
|
+
setTimeout(value instanceof Error ? reject : resolve, ms, value),
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* eslint-disable @typescript-eslint/promise-function-async */
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Promisified version of setTimeout.
|
|
20
|
+
*
|
|
21
|
+
* Wraps the passed function with try/catch,
|
|
22
|
+
* catch will propagate to pDelayFn rejection,
|
|
23
|
+
* otherwise pDelayFn will resolve with returned value.
|
|
24
|
+
*
|
|
25
|
+
* On abort() - clears the Timeout and immediately resolves the Promise with void.
|
|
26
|
+
*/
|
|
27
|
+
export function pDelayFn<T>(ms = 0, fn: PromisableFunction<T>): DeferredPromise<T> {
|
|
28
|
+
const p = pDefer<T>()
|
|
29
|
+
|
|
30
|
+
const timer = setTimeout(async () => {
|
|
31
|
+
try {
|
|
32
|
+
p.resolve(await fn())
|
|
33
|
+
} catch (err) {
|
|
34
|
+
p.reject(err as Error)
|
|
35
|
+
}
|
|
36
|
+
}, ms)
|
|
37
|
+
|
|
38
|
+
p.abort = () => {
|
|
39
|
+
clearTimeout(timer)
|
|
40
|
+
// p.rejectAborted(reason) // nope
|
|
41
|
+
p.resolve()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return p
|
|
3
45
|
}
|
package/src/promise/pRetry.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { AnyFunction,
|
|
2
|
-
import { _since,
|
|
3
|
-
import { TimeoutError } from './pTimeout'
|
|
1
|
+
import type { AnyFunction, CommonLogger, ErrorData } from '..'
|
|
2
|
+
import { _errorDataAppend, _since, pDelay, pTimeout } from '..'
|
|
4
3
|
|
|
5
4
|
export interface PRetryOptions {
|
|
6
5
|
/**
|
|
@@ -84,15 +83,6 @@ export interface PRetryOptions {
|
|
|
84
83
|
*/
|
|
85
84
|
logger?: CommonLogger
|
|
86
85
|
|
|
87
|
-
/**
|
|
88
|
-
* Defaults to true.
|
|
89
|
-
* If true - preserves the stack trace in case of a Timeout (usually - very useful!).
|
|
90
|
-
* It has a certain perf cost.
|
|
91
|
-
*
|
|
92
|
-
* @experimental
|
|
93
|
-
*/
|
|
94
|
-
keepStackTrace?: boolean
|
|
95
|
-
|
|
96
86
|
/**
|
|
97
87
|
* Will be merged with `err.data` object.
|
|
98
88
|
*/
|
|
@@ -120,12 +110,10 @@ export async function pRetry<T>(
|
|
|
120
110
|
predicate,
|
|
121
111
|
logger = console,
|
|
122
112
|
name,
|
|
123
|
-
keepStackTrace = true,
|
|
124
113
|
timeout,
|
|
125
114
|
} = opt
|
|
126
115
|
|
|
127
|
-
const fakeError =
|
|
128
|
-
|
|
116
|
+
const fakeError = timeout ? new Error('TimeoutError') : undefined
|
|
129
117
|
let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt
|
|
130
118
|
|
|
131
119
|
if (opt.logAll) {
|
|
@@ -139,86 +127,50 @@ export async function pRetry<T>(
|
|
|
139
127
|
|
|
140
128
|
let delay = initialDelay
|
|
141
129
|
let attempt = 0
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (
|
|
150
|
-
|
|
151
|
-
err.stack = fakeError.stack!.replace('Error: RetryError', 'TimeoutError')
|
|
130
|
+
|
|
131
|
+
/* eslint-disable no-await-in-loop, no-constant-condition */
|
|
132
|
+
while (true) {
|
|
133
|
+
const started = Date.now()
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
attempt++
|
|
137
|
+
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
138
|
+
logger.log(`${fname} attempt #${attempt}...`)
|
|
152
139
|
}
|
|
153
|
-
reject(err)
|
|
154
|
-
}
|
|
155
140
|
|
|
156
|
-
|
|
157
|
-
if (timedOut) return
|
|
141
|
+
let result: any
|
|
158
142
|
|
|
159
143
|
if (timeout) {
|
|
160
|
-
|
|
144
|
+
await pTimeout(async () => await fn(attempt), {
|
|
145
|
+
timeout,
|
|
146
|
+
name: fname,
|
|
147
|
+
errorData: opt.errorData,
|
|
148
|
+
fakeError,
|
|
149
|
+
})
|
|
150
|
+
} else {
|
|
151
|
+
result = await fn(attempt)
|
|
161
152
|
}
|
|
162
153
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const r = await fn(attempt)
|
|
172
|
-
|
|
173
|
-
clearTimeout(timer)
|
|
174
|
-
|
|
175
|
-
if (logSuccess) {
|
|
176
|
-
logger.log(`${fname} attempt #${attempt} succeeded in ${_since(started)}`)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
resolve(r)
|
|
180
|
-
} catch (err) {
|
|
181
|
-
clearTimeout(timer)
|
|
182
|
-
|
|
183
|
-
if (logFailures) {
|
|
184
|
-
logger.warn(
|
|
185
|
-
`${fname} attempt #${attempt} error in ${_since(started)}:`,
|
|
186
|
-
_stringifyAny(err, {
|
|
187
|
-
includeErrorData: true,
|
|
188
|
-
}),
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (
|
|
193
|
-
attempt >= maxAttempts ||
|
|
194
|
-
(predicate && !predicate(err as Error, attempt, maxAttempts))
|
|
195
|
-
) {
|
|
196
|
-
// Give up
|
|
197
|
-
|
|
198
|
-
if (fakeError) {
|
|
199
|
-
// Preserve the original call stack
|
|
200
|
-
Object.defineProperty(err, 'stack', {
|
|
201
|
-
value:
|
|
202
|
-
(err as Error).stack +
|
|
203
|
-
'\n --' +
|
|
204
|
-
fakeError.stack!.replace('Error: RetryError', ''),
|
|
205
|
-
})
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
;(err as AppError).data = {
|
|
209
|
-
...(err as AppError).data,
|
|
210
|
-
...opt.errorData,
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
reject(err)
|
|
214
|
-
} else {
|
|
215
|
-
// Retry after delay
|
|
216
|
-
delay *= delayMultiplier
|
|
217
|
-
setTimeout(next, delay)
|
|
218
|
-
}
|
|
154
|
+
if (logSuccess) {
|
|
155
|
+
logger.log(`${fname} attempt #${attempt} succeeded in ${_since(started)}`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return result
|
|
159
|
+
} catch (err) {
|
|
160
|
+
if (logFailures) {
|
|
161
|
+
logger.warn(`${fname} attempt #${attempt} error in ${_since(started)}:`, err)
|
|
219
162
|
}
|
|
220
|
-
}
|
|
221
163
|
|
|
222
|
-
|
|
223
|
-
|
|
164
|
+
if (attempt >= maxAttempts || (predicate && !predicate(err as Error, attempt, maxAttempts))) {
|
|
165
|
+
// Give up
|
|
166
|
+
_errorDataAppend(err, opt.errorData)
|
|
167
|
+
throw err
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Retry after delay
|
|
171
|
+
delay *= delayMultiplier
|
|
172
|
+
await pDelay(delay)
|
|
173
|
+
// back to while(true) loop
|
|
174
|
+
}
|
|
175
|
+
}
|
|
224
176
|
}
|
package/src/promise/pState.ts
CHANGED
package/src/promise/pTimeout.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AppError } from '../error/app.error'
|
|
2
2
|
import type { ErrorData } from '../error/error.model'
|
|
3
|
+
import { _errorDataAppend } from '../error/error.util'
|
|
3
4
|
import type { AnyAsyncFunction } from '../types'
|
|
4
5
|
|
|
5
6
|
export class TimeoutError extends AppError {
|
|
@@ -30,13 +31,11 @@ export interface PTimeoutOptions {
|
|
|
30
31
|
onTimeout?: (err: TimeoutError) => any
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* @experimental
|
|
34
|
+
* If passed - fakeError.stack will be used as a stacktrace.
|
|
35
|
+
* This is to "keep stacktrace" when pTimeout is called from another
|
|
36
|
+
* function (like pRetry).
|
|
38
37
|
*/
|
|
39
|
-
|
|
38
|
+
fakeError?: Error
|
|
40
39
|
|
|
41
40
|
/**
|
|
42
41
|
* Will be merged with `err.data` object.
|
|
@@ -66,25 +65,24 @@ export function pTimeoutFn<T extends AnyAsyncFunction>(fn: T, opt: PTimeoutOptio
|
|
|
66
65
|
* If the Function rejects - passes this rejection further.
|
|
67
66
|
*/
|
|
68
67
|
export async function pTimeout<T>(fn: AnyAsyncFunction<T>, opt: PTimeoutOptions): Promise<T> {
|
|
69
|
-
const { timeout, name = fn.name || 'pTimeout function', onTimeout
|
|
70
|
-
const fakeError =
|
|
68
|
+
const { timeout, name = fn.name || 'pTimeout function', onTimeout } = opt
|
|
69
|
+
const fakeError = opt.fakeError || new Error('TimeoutError')
|
|
71
70
|
|
|
72
71
|
// eslint-disable-next-line no-async-promise-executor
|
|
73
72
|
return await new Promise(async (resolve, reject) => {
|
|
74
73
|
// Prepare the timeout timer
|
|
75
74
|
const timer = setTimeout(() => {
|
|
76
75
|
const err = new TimeoutError(`"${name}" timed out after ${timeout} ms`, opt.errorData)
|
|
77
|
-
|
|
76
|
+
// keep original stack
|
|
77
|
+
err.stack = fakeError.stack!.replace('Error: TimeoutError', 'TimeoutError: ' + err.message)
|
|
78
78
|
|
|
79
79
|
if (onTimeout) {
|
|
80
80
|
try {
|
|
81
81
|
resolve(onTimeout(err))
|
|
82
82
|
} catch (err: any) {
|
|
83
|
-
|
|
84
|
-
err.
|
|
85
|
-
|
|
86
|
-
...opt.errorData,
|
|
87
|
-
}
|
|
83
|
+
// keep original stack
|
|
84
|
+
err.stack = fakeError.stack!.replace('Error: TimeoutError', err.name + ': ' + err.message)
|
|
85
|
+
_errorDataAppend(err, opt.errorData)
|
|
88
86
|
reject(err)
|
|
89
87
|
}
|
|
90
88
|
return
|
|
@@ -33,13 +33,6 @@ export interface StringifyAnyOptions {
|
|
|
33
33
|
*/
|
|
34
34
|
maxLen?: number
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Pass true to include "stringified" `error.data` in the output.
|
|
38
|
-
*
|
|
39
|
-
* @default false
|
|
40
|
-
*/
|
|
41
|
-
includeErrorData?: boolean
|
|
42
|
-
|
|
43
36
|
/**
|
|
44
37
|
* Set to true to print Error.stack instead of just Error.message.
|
|
45
38
|
*
|
|
@@ -130,12 +123,6 @@ export function _stringifyAny(obj: any, opt: StringifyAnyOptions = {}): string {
|
|
|
130
123
|
// `replace` here works ONCE, exactly as we need it
|
|
131
124
|
s = s.replace('HttpError', `HttpError(${obj.data.httpStatusCode})`)
|
|
132
125
|
}
|
|
133
|
-
|
|
134
|
-
// Here we ensure it has `data`
|
|
135
|
-
const { data } = obj
|
|
136
|
-
if (opt.includeErrorData && Object.keys(data).length > 0) {
|
|
137
|
-
s = [s, _stringifyAny(data, opt)].join('\n')
|
|
138
|
-
}
|
|
139
126
|
} else if (typeof (obj as any).code === 'string') {
|
|
140
127
|
// Error that has no `data`, but has `code` property
|
|
141
128
|
s = [s, `code: ${(obj as any).code}`].join('\n')
|
package/src/types.ts
CHANGED
|
@@ -80,6 +80,9 @@ export type UnsavedId<T extends Partial<ObjectWithId>> = Omit<T, 'id'> & {
|
|
|
80
80
|
*/
|
|
81
81
|
export type AnyFunction<T = any> = (...args: any[]) => T
|
|
82
82
|
export type AnyAsyncFunction<T = any> = (...args: any[]) => Promise<T>
|
|
83
|
+
export type AsyncFunction<T = any> = () => Promise<T>
|
|
84
|
+
export type AnyPromisableFunction<T = any> = (...args: any[]) => Promisable<T>
|
|
85
|
+
export type PromisableFunction<T = any> = () => Promisable<T>
|
|
83
86
|
|
|
84
87
|
/**
|
|
85
88
|
* Symbol to indicate END of Sequence.
|
package/src/vendor/is.ts
CHANGED
|
@@ -25,7 +25,7 @@ const typedArrayTypeNames = [
|
|
|
25
25
|
'BigUint64Array',
|
|
26
26
|
] as const
|
|
27
27
|
|
|
28
|
-
type TypedArrayTypeName = typeof typedArrayTypeNames[number]
|
|
28
|
+
type TypedArrayTypeName = (typeof typedArrayTypeNames)[number]
|
|
29
29
|
|
|
30
30
|
function isTypedArrayName(name: unknown): name is TypedArrayTypeName {
|
|
31
31
|
return typedArrayTypeNames.includes(name as TypedArrayTypeName)
|
|
@@ -60,7 +60,7 @@ const objectTypeNames = [
|
|
|
60
60
|
...typedArrayTypeNames,
|
|
61
61
|
] as const
|
|
62
62
|
|
|
63
|
-
type ObjectTypeName = typeof objectTypeNames[number]
|
|
63
|
+
type ObjectTypeName = (typeof objectTypeNames)[number]
|
|
64
64
|
|
|
65
65
|
function isObjectTypeName(name: unknown): name is ObjectTypeName {
|
|
66
66
|
return objectTypeNames.includes(name as ObjectTypeName)
|
|
@@ -76,7 +76,7 @@ const primitiveTypeNames = [
|
|
|
76
76
|
'symbol',
|
|
77
77
|
] as const
|
|
78
78
|
|
|
79
|
-
type PrimitiveTypeName = typeof primitiveTypeNames[number]
|
|
79
|
+
type PrimitiveTypeName = (typeof primitiveTypeNames)[number]
|
|
80
80
|
|
|
81
81
|
function isPrimitiveTypeName(name: unknown): name is PrimitiveTypeName {
|
|
82
82
|
return primitiveTypeNames.includes(name as PrimitiveTypeName)
|