@naturalcycles/js-lib 14.80.0 → 14.83.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/asyncMemo.decorator.d.ts +22 -0
- package/dist/decorators/asyncMemo.decorator.js +96 -0
- package/dist/decorators/memo.decorator.d.ts +8 -0
- package/dist/decorators/memo.decorator.js +11 -6
- package/dist/decorators/memo.util.d.ts +32 -8
- package/dist/decorators/memo.util.js +17 -2
- package/dist/decorators/retry.decorator.js +1 -1
- package/dist/error/error.model.d.ts +6 -0
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -2
- package/dist/json-schema/jsonSchemaBuilder.d.ts +2 -2
- package/dist/json-schema/jsonSchemaBuilder.js +4 -4
- package/dist/json-schema/jsonSchemas.d.ts +2 -2
- package/dist/promise/AggregatedError.d.ts +1 -1
- package/dist/promise/AggregatedError.js +2 -7
- package/dist/promise/pFilter.d.ts +2 -3
- package/dist/promise/pFilter.js +4 -4
- package/dist/promise/pMap.d.ts +1 -1
- package/dist/promise/pMap.js +67 -19
- package/dist/promise/pRetry.d.ts +17 -2
- package/dist/promise/pRetry.js +72 -40
- package/dist/types.d.ts +5 -5
- package/dist-esm/decorators/asyncMemo.decorator.js +104 -0
- package/dist-esm/decorators/memo.decorator.js +11 -5
- package/dist-esm/decorators/memo.util.js +15 -1
- package/dist-esm/decorators/retry.decorator.js +2 -2
- package/dist-esm/index.js +3 -3
- package/dist-esm/json-schema/jsonSchemaBuilder.js +4 -4
- package/dist-esm/promise/AggregatedError.js +2 -7
- package/dist-esm/promise/pFilter.js +4 -4
- package/dist-esm/promise/pMap.js +79 -19
- package/dist-esm/promise/pRetry.js +70 -39
- package/package.json +1 -1
- package/src/decorators/asyncMemo.decorator.ts +151 -0
- package/src/decorators/memo.decorator.ts +16 -5
- package/src/decorators/memo.util.ts +49 -10
- package/src/decorators/retry.decorator.ts +2 -2
- package/src/error/error.model.ts +7 -0
- package/src/index.ts +5 -3
- package/src/json-schema/jsonSchemaBuilder.ts +4 -4
- package/src/promise/AggregatedError.ts +3 -8
- package/src/promise/pFilter.ts +5 -14
- package/src/promise/pMap.ts +72 -21
- package/src/promise/pRetry.ts +105 -41
- package/src/types.ts +5 -5
- package/dist/promise/pBatch.d.ts +0 -7
- package/dist/promise/pBatch.js +0 -30
- package/dist-esm/promise/pBatch.js +0 -23
- package/src/promise/pBatch.ts +0 -31
package/src/promise/pMap.ts
CHANGED
|
@@ -55,36 +55,85 @@ export interface PMapOptions {
|
|
|
55
55
|
* })();
|
|
56
56
|
*/
|
|
57
57
|
export async function pMap<IN, OUT>(
|
|
58
|
-
iterable: Iterable<IN
|
|
58
|
+
iterable: Iterable<IN>,
|
|
59
59
|
mapper: AbortableAsyncMapper<IN, OUT>,
|
|
60
60
|
opt: PMapOptions = {},
|
|
61
61
|
): Promise<OUT[]> {
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
const ret: (OUT | typeof SKIP)[] = []
|
|
63
|
+
// const iterator = iterable[Symbol.iterator]()
|
|
64
|
+
const items = [...iterable]
|
|
65
|
+
const itemsLength = items.length
|
|
66
|
+
if (itemsLength === 0) return [] // short circuit
|
|
67
|
+
|
|
68
|
+
const { concurrency = itemsLength, errorMode = ErrorMode.THROW_IMMEDIATELY } = opt
|
|
69
|
+
|
|
70
|
+
const errors: Error[] = []
|
|
71
|
+
let isSettled = false
|
|
72
|
+
let resolvingCount = 0
|
|
73
|
+
let currentIndex = 0
|
|
74
|
+
|
|
75
|
+
// Special cases that are able to preserve async stack traces
|
|
76
|
+
|
|
77
|
+
if (concurrency === 1) {
|
|
78
|
+
// Special case for concurrency == 1
|
|
79
|
+
|
|
80
|
+
for await (const item of items) {
|
|
81
|
+
try {
|
|
82
|
+
const r = await mapper(item, currentIndex++)
|
|
83
|
+
if (r === END) break
|
|
84
|
+
if (r !== SKIP) ret.push(r)
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (errorMode === ErrorMode.THROW_IMMEDIATELY) throw err
|
|
87
|
+
if (errorMode === ErrorMode.THROW_AGGREGATED) {
|
|
88
|
+
errors.push(err as Error)
|
|
89
|
+
}
|
|
90
|
+
// otherwise, suppress completely
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (errors.length) {
|
|
95
|
+
throw new AggregatedError(errors, ret)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return ret as OUT[]
|
|
99
|
+
} else if (!opt.concurrency || items.length <= opt.concurrency) {
|
|
100
|
+
// Special case for concurrency == infinity or iterable.length < concurrency
|
|
101
|
+
|
|
102
|
+
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
103
|
+
return (await Promise.all(items.map((item, i) => mapper(item, i)))).filter(
|
|
104
|
+
r => r !== SKIP && r !== END,
|
|
105
|
+
) as OUT[]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
;(await Promise.allSettled(items.map((item, i) => mapper(item, i)))).forEach(r => {
|
|
109
|
+
if (r.status === 'fulfilled') {
|
|
110
|
+
if (r.value !== SKIP && r.value !== END) ret.push(r.value)
|
|
111
|
+
} else if (errorMode === ErrorMode.THROW_AGGREGATED) {
|
|
112
|
+
errors.push(r.reason)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
if (errors.length) {
|
|
117
|
+
throw new AggregatedError(errors, ret)
|
|
118
|
+
}
|
|
64
119
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const errors: Error[] = []
|
|
68
|
-
let isSettled = false
|
|
69
|
-
let isIterableDone = false
|
|
70
|
-
let resolvingCount = 0
|
|
71
|
-
let currentIndex = 0
|
|
120
|
+
return ret as OUT[]
|
|
121
|
+
}
|
|
72
122
|
|
|
73
|
-
|
|
123
|
+
return new Promise<OUT[]>((resolve, reject) => {
|
|
124
|
+
const next = () => {
|
|
74
125
|
if (isSettled) {
|
|
75
126
|
return
|
|
76
127
|
}
|
|
77
128
|
|
|
78
|
-
const nextItem =
|
|
79
|
-
const i = currentIndex
|
|
80
|
-
if (!skipped) currentIndex++
|
|
81
|
-
|
|
82
|
-
if (nextItem.done) {
|
|
83
|
-
isIterableDone = true
|
|
129
|
+
const nextItem = items[currentIndex]!
|
|
130
|
+
const i = currentIndex++
|
|
84
131
|
|
|
132
|
+
if (currentIndex > itemsLength) {
|
|
85
133
|
if (resolvingCount === 0) {
|
|
134
|
+
isSettled = true
|
|
86
135
|
const r = ret.filter(r => r !== SKIP) as OUT[]
|
|
87
|
-
if (errors.length
|
|
136
|
+
if (errors.length) {
|
|
88
137
|
reject(new AggregatedError(errors, r))
|
|
89
138
|
} else {
|
|
90
139
|
resolve(r)
|
|
@@ -96,7 +145,7 @@ export async function pMap<IN, OUT>(
|
|
|
96
145
|
|
|
97
146
|
resolvingCount++
|
|
98
147
|
|
|
99
|
-
Promise.resolve(nextItem
|
|
148
|
+
Promise.resolve(nextItem)
|
|
100
149
|
.then(async element => await mapper(element, i))
|
|
101
150
|
.then(
|
|
102
151
|
value => {
|
|
@@ -114,7 +163,9 @@ export async function pMap<IN, OUT>(
|
|
|
114
163
|
isSettled = true
|
|
115
164
|
reject(err)
|
|
116
165
|
} else {
|
|
117
|
-
|
|
166
|
+
if (errorMode === ErrorMode.THROW_AGGREGATED) {
|
|
167
|
+
errors.push(err)
|
|
168
|
+
}
|
|
118
169
|
resolvingCount--
|
|
119
170
|
next()
|
|
120
171
|
}
|
|
@@ -125,7 +176,7 @@ export async function pMap<IN, OUT>(
|
|
|
125
176
|
for (let i = 0; i < concurrency; i++) {
|
|
126
177
|
next()
|
|
127
178
|
|
|
128
|
-
if (
|
|
179
|
+
if (isSettled) {
|
|
129
180
|
break
|
|
130
181
|
}
|
|
131
182
|
}
|
package/src/promise/pRetry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { _since, _stringifyAny, AnyFunction, CommonLogger } from '..'
|
|
2
|
+
import { TimeoutError } from './pTimeout'
|
|
2
3
|
|
|
3
4
|
export interface PRetryOptions {
|
|
4
5
|
/**
|
|
@@ -7,6 +8,13 @@ export interface PRetryOptions {
|
|
|
7
8
|
*/
|
|
8
9
|
name?: string
|
|
9
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Timeout for each Try, in milliseconds.
|
|
13
|
+
*
|
|
14
|
+
* Defaults to no timeout.
|
|
15
|
+
*/
|
|
16
|
+
timeout?: number
|
|
17
|
+
|
|
10
18
|
/**
|
|
11
19
|
* How many attempts to try.
|
|
12
20
|
* First attempt is not a retry, but "initial try". It still counts.
|
|
@@ -34,7 +42,7 @@ export interface PRetryOptions {
|
|
|
34
42
|
*
|
|
35
43
|
* @default () => true
|
|
36
44
|
*/
|
|
37
|
-
predicate?: (err:
|
|
45
|
+
predicate?: (err: Error, attempt: number, maxAttempts: number) => boolean
|
|
38
46
|
|
|
39
47
|
/**
|
|
40
48
|
* Log the first attempt (which is not a "retry" yet).
|
|
@@ -74,76 +82,132 @@ export interface PRetryOptions {
|
|
|
74
82
|
* Default to `console`
|
|
75
83
|
*/
|
|
76
84
|
logger?: CommonLogger
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Defaults to true.
|
|
88
|
+
* If true - preserves the stack trace in case of a Timeout (usually - very useful!).
|
|
89
|
+
* It has a certain perf cost.
|
|
90
|
+
*
|
|
91
|
+
* @experimental
|
|
92
|
+
*/
|
|
93
|
+
keepStackTrace?: boolean
|
|
77
94
|
}
|
|
78
95
|
|
|
79
96
|
/**
|
|
80
97
|
* Returns a Function (!), enhanced with retry capabilities.
|
|
81
98
|
* Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
|
|
82
99
|
*/
|
|
83
|
-
|
|
84
|
-
|
|
100
|
+
export function pRetryFn<T extends AnyFunction>(fn: T, opt: PRetryOptions = {}): T {
|
|
101
|
+
return async function pRetryFunction(this: any, ...args: any[]) {
|
|
102
|
+
return await pRetry(() => fn.call(this, ...args), opt)
|
|
103
|
+
} as any
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function pRetry<T>(
|
|
107
|
+
fn: (attempt: number) => Promise<T>,
|
|
108
|
+
opt: PRetryOptions = {},
|
|
109
|
+
): Promise<T> {
|
|
85
110
|
const {
|
|
86
111
|
maxAttempts = 4,
|
|
87
112
|
delay: initialDelay = 1000,
|
|
88
113
|
delayMultiplier = 2,
|
|
89
114
|
predicate,
|
|
90
115
|
logger = console,
|
|
91
|
-
name
|
|
116
|
+
name,
|
|
117
|
+
keepStackTrace = true,
|
|
118
|
+
timeout,
|
|
92
119
|
} = opt
|
|
93
120
|
|
|
121
|
+
const fakeError = keepStackTrace ? new Error('RetryError') : undefined
|
|
122
|
+
|
|
94
123
|
let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt
|
|
95
124
|
|
|
96
125
|
if (opt.logAll) {
|
|
97
|
-
logFirstAttempt = logRetries = logFailures = true
|
|
126
|
+
logSuccess = logFirstAttempt = logRetries = logFailures = true
|
|
98
127
|
}
|
|
99
128
|
if (opt.logNone) {
|
|
100
129
|
logSuccess = logFirstAttempt = logRetries = logFailures = false
|
|
101
130
|
}
|
|
102
131
|
|
|
103
|
-
const fname =
|
|
132
|
+
const fname = name || fn.name || 'pRetry function'
|
|
104
133
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
134
|
+
let delay = initialDelay
|
|
135
|
+
let attempt = 0
|
|
136
|
+
let timer: NodeJS.Timeout | undefined
|
|
137
|
+
let timedOut = false
|
|
108
138
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
139
|
+
return await new Promise((resolve, reject) => {
|
|
140
|
+
const rejectWithTimeout = () => {
|
|
141
|
+
timedOut = true // to prevent more tries
|
|
142
|
+
const err = new TimeoutError(`"${fname}" timed out after ${timeout} ms`)
|
|
143
|
+
if (fakeError) {
|
|
144
|
+
// keep original stack
|
|
145
|
+
err.stack = fakeError.stack!.replace('Error: RetryError', 'TimeoutError')
|
|
146
|
+
}
|
|
147
|
+
reject(err)
|
|
148
|
+
}
|
|
112
149
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
116
|
-
logger.log(`${fname} attempt #${attempt}...`)
|
|
117
|
-
}
|
|
150
|
+
const next = async () => {
|
|
151
|
+
if (timedOut) return
|
|
118
152
|
|
|
119
|
-
|
|
153
|
+
if (timeout) {
|
|
154
|
+
timer = setTimeout(rejectWithTimeout, timeout)
|
|
155
|
+
}
|
|
120
156
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
)
|
|
133
|
-
}
|
|
157
|
+
const started = Date.now()
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
attempt++
|
|
161
|
+
if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
|
|
162
|
+
logger.log(`${fname} attempt #${attempt}...`)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const r = await fn(attempt)
|
|
166
|
+
|
|
167
|
+
clearTimeout(timer!)
|
|
134
168
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
169
|
+
if (logSuccess) {
|
|
170
|
+
logger.log(`${fname} attempt #${attempt} succeeded in ${_since(started)}`)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
resolve(r)
|
|
174
|
+
} catch (err) {
|
|
175
|
+
clearTimeout(timer!)
|
|
176
|
+
|
|
177
|
+
if (logFailures) {
|
|
178
|
+
logger.warn(
|
|
179
|
+
`${fname} attempt #${attempt} error in ${_since(started)}:`,
|
|
180
|
+
_stringifyAny(err, {
|
|
181
|
+
includeErrorData: true,
|
|
182
|
+
}),
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (
|
|
187
|
+
attempt >= maxAttempts ||
|
|
188
|
+
(predicate && !predicate(err as Error, attempt, maxAttempts))
|
|
189
|
+
) {
|
|
190
|
+
// Give up
|
|
191
|
+
|
|
192
|
+
if (fakeError) {
|
|
193
|
+
// Preserve the original call stack
|
|
194
|
+
Object.defineProperty(err, 'stack', {
|
|
195
|
+
value:
|
|
196
|
+
(err as Error).stack +
|
|
197
|
+
'\n --' +
|
|
198
|
+
fakeError.stack!.replace('Error: RetryError', ''),
|
|
199
|
+
})
|
|
142
200
|
}
|
|
201
|
+
|
|
202
|
+
reject(err)
|
|
203
|
+
} else {
|
|
204
|
+
// Retry after delay
|
|
205
|
+
delay *= delayMultiplier
|
|
206
|
+
setTimeout(next, delay)
|
|
143
207
|
}
|
|
144
208
|
}
|
|
209
|
+
}
|
|
145
210
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
} as any
|
|
211
|
+
void next()
|
|
212
|
+
})
|
|
149
213
|
}
|
package/src/types.ts
CHANGED
|
@@ -169,8 +169,8 @@ export type UnixTimestamp = number
|
|
|
169
169
|
/**
|
|
170
170
|
* Base interface for any Entity that was saved to DB.
|
|
171
171
|
*/
|
|
172
|
-
export interface SavedDBEntity {
|
|
173
|
-
id:
|
|
172
|
+
export interface SavedDBEntity<ID = string> {
|
|
173
|
+
id: ID
|
|
174
174
|
|
|
175
175
|
/**
|
|
176
176
|
* unixTimestamp of when the entity was first created (in the DB).
|
|
@@ -189,10 +189,10 @@ export interface SavedDBEntity {
|
|
|
189
189
|
* hence `id`, `created` and `updated` fields CAN BE undefined (yet).
|
|
190
190
|
* When it's known to be saved - `SavedDBEntity` interface can be used instead.
|
|
191
191
|
*/
|
|
192
|
-
export type BaseDBEntity = Partial<SavedDBEntity
|
|
192
|
+
export type BaseDBEntity<ID = string> = Partial<SavedDBEntity<ID>>
|
|
193
193
|
|
|
194
|
-
export type Saved<E> = Merge<E, SavedDBEntity
|
|
195
|
-
export type Unsaved<E> = Merge<E, BaseDBEntity
|
|
194
|
+
export type Saved<E, ID = string> = Merge<E, SavedDBEntity<ID>>
|
|
195
|
+
export type Unsaved<E, ID = string> = Merge<E, BaseDBEntity<ID>>
|
|
196
196
|
|
|
197
197
|
/**
|
|
198
198
|
* Named type for JSON.parse / JSON.stringify second argument
|
package/dist/promise/pBatch.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { AbortableAsyncMapper, BatchResult } from '..';
|
|
2
|
-
/**
|
|
3
|
-
* Like pMap, but doesn't fail on errors, instead returns both successful results and errors.
|
|
4
|
-
*/
|
|
5
|
-
export declare function pBatch<IN, OUT>(iterable: Iterable<IN | PromiseLike<IN>>, mapper: AbortableAsyncMapper<IN, OUT>, opt?: {
|
|
6
|
-
concurrency?: number;
|
|
7
|
-
}): Promise<BatchResult<OUT>>;
|
package/dist/promise/pBatch.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pBatch = void 0;
|
|
4
|
-
const __1 = require("..");
|
|
5
|
-
const pMap_1 = require("./pMap");
|
|
6
|
-
/**
|
|
7
|
-
* Like pMap, but doesn't fail on errors, instead returns both successful results and errors.
|
|
8
|
-
*/
|
|
9
|
-
async function pBatch(iterable, mapper, opt) {
|
|
10
|
-
try {
|
|
11
|
-
const results = await (0, pMap_1.pMap)(iterable, mapper, {
|
|
12
|
-
...opt,
|
|
13
|
-
errorMode: __1.ErrorMode.THROW_AGGREGATED,
|
|
14
|
-
});
|
|
15
|
-
return {
|
|
16
|
-
results,
|
|
17
|
-
errors: [],
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
catch (err) {
|
|
21
|
-
const { errors, results } = err;
|
|
22
|
-
if (!errors || !results)
|
|
23
|
-
throw err; // not an AggregatedError
|
|
24
|
-
return {
|
|
25
|
-
results,
|
|
26
|
-
errors,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
exports.pBatch = pBatch;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ErrorMode } from '..';
|
|
2
|
-
import { pMap } from './pMap';
|
|
3
|
-
/**
|
|
4
|
-
* Like pMap, but doesn't fail on errors, instead returns both successful results and errors.
|
|
5
|
-
*/
|
|
6
|
-
export async function pBatch(iterable, mapper, opt) {
|
|
7
|
-
try {
|
|
8
|
-
const results = await pMap(iterable, mapper, Object.assign(Object.assign({}, opt), { errorMode: ErrorMode.THROW_AGGREGATED }));
|
|
9
|
-
return {
|
|
10
|
-
results,
|
|
11
|
-
errors: [],
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
catch (err) {
|
|
15
|
-
const { errors, results } = err;
|
|
16
|
-
if (!errors || !results)
|
|
17
|
-
throw err; // not an AggregatedError
|
|
18
|
-
return {
|
|
19
|
-
results,
|
|
20
|
-
errors,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
}
|
package/src/promise/pBatch.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { AbortableAsyncMapper, BatchResult, ErrorMode } from '..'
|
|
2
|
-
import { AggregatedError } from './AggregatedError'
|
|
3
|
-
import { pMap } from './pMap'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Like pMap, but doesn't fail on errors, instead returns both successful results and errors.
|
|
7
|
-
*/
|
|
8
|
-
export async function pBatch<IN, OUT>(
|
|
9
|
-
iterable: Iterable<IN | PromiseLike<IN>>,
|
|
10
|
-
mapper: AbortableAsyncMapper<IN, OUT>,
|
|
11
|
-
opt?: { concurrency?: number },
|
|
12
|
-
): Promise<BatchResult<OUT>> {
|
|
13
|
-
try {
|
|
14
|
-
const results = await pMap(iterable, mapper, {
|
|
15
|
-
...opt,
|
|
16
|
-
errorMode: ErrorMode.THROW_AGGREGATED,
|
|
17
|
-
})
|
|
18
|
-
return {
|
|
19
|
-
results,
|
|
20
|
-
errors: [],
|
|
21
|
-
}
|
|
22
|
-
} catch (err) {
|
|
23
|
-
const { errors, results } = err as AggregatedError<OUT>
|
|
24
|
-
if (!errors || !results) throw err // not an AggregatedError
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
results,
|
|
28
|
-
errors,
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|