@naturalcycles/js-lib 14.81.0 → 14.84.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 (43) hide show
  1. package/dist/decorators/asyncMemo.decorator.d.ts +22 -0
  2. package/dist/decorators/asyncMemo.decorator.js +96 -0
  3. package/dist/decorators/memo.decorator.d.ts +8 -0
  4. package/dist/decorators/memo.decorator.js +11 -6
  5. package/dist/decorators/memo.util.d.ts +27 -8
  6. package/dist/decorators/memo.util.js +1 -1
  7. package/dist/error/error.model.d.ts +6 -0
  8. package/dist/index.d.ts +3 -3
  9. package/dist/index.js +1 -1
  10. package/dist/promise/AggregatedError.d.ts +1 -1
  11. package/dist/promise/AggregatedError.js +2 -7
  12. package/dist/promise/pFilter.d.ts +2 -3
  13. package/dist/promise/pFilter.js +4 -4
  14. package/dist/promise/pMap.d.ts +1 -1
  15. package/dist/promise/pMap.js +67 -19
  16. package/dist/promise/pRetry.d.ts +6 -2
  17. package/dist/promise/pRetry.js +6 -1
  18. package/dist/promise/pTimeout.d.ts +5 -1
  19. package/dist/promise/pTimeout.js +5 -1
  20. package/dist-esm/decorators/asyncMemo.decorator.js +104 -0
  21. package/dist-esm/decorators/memo.decorator.js +11 -5
  22. package/dist-esm/decorators/memo.util.js +1 -1
  23. package/dist-esm/index.js +1 -1
  24. package/dist-esm/promise/AggregatedError.js +2 -7
  25. package/dist-esm/promise/pFilter.js +4 -4
  26. package/dist-esm/promise/pMap.js +79 -19
  27. package/dist-esm/promise/pRetry.js +3 -1
  28. package/dist-esm/promise/pTimeout.js +2 -1
  29. package/package.json +1 -1
  30. package/src/decorators/asyncMemo.decorator.ts +151 -0
  31. package/src/decorators/memo.decorator.ts +16 -5
  32. package/src/decorators/memo.util.ts +36 -10
  33. package/src/error/error.model.ts +7 -0
  34. package/src/index.ts +3 -2
  35. package/src/promise/AggregatedError.ts +3 -8
  36. package/src/promise/pFilter.ts +5 -14
  37. package/src/promise/pMap.ts +72 -21
  38. package/src/promise/pRetry.ts +13 -3
  39. package/src/promise/pTimeout.ts +14 -2
  40. package/dist/promise/pBatch.d.ts +0 -7
  41. package/dist/promise/pBatch.js +0 -30
  42. package/dist-esm/promise/pBatch.js +0 -23
  43. package/src/promise/pBatch.ts +0 -31
package/src/index.ts CHANGED
@@ -12,7 +12,8 @@ export * from './decorators/debounce.decorator'
12
12
  export * from './decorators/decorator.util'
13
13
  export * from './decorators/logMethod.decorator'
14
14
  export * from './decorators/memo.decorator'
15
- import { MemoCache } from './decorators/memo.util'
15
+ export * from './decorators/asyncMemo.decorator'
16
+ import { MemoCache, AsyncMemoCache } from './decorators/memo.util'
16
17
  export * from './decorators/memoFn'
17
18
  export * from './decorators/retry.decorator'
18
19
  export * from './decorators/timeout.decorator'
@@ -67,7 +68,6 @@ export * from './object/object.util'
67
68
  export * from './object/sortObject'
68
69
  export * from './object/sortObjectDeep'
69
70
  import { AggregatedError } from './promise/AggregatedError'
70
- export * from './promise/pBatch'
71
71
  import { DeferredPromise, pDefer } from './promise/pDefer'
72
72
  export * from './promise/pDelay'
73
73
  export * from './promise/pFilter'
@@ -161,6 +161,7 @@ export type {
161
161
  AbortableAsyncMapper,
162
162
  PQueueCfg,
163
163
  MemoCache,
164
+ AsyncMemoCache,
164
165
  PromiseDecoratorCfg,
165
166
  PromiseDecoratorResp,
166
167
  ErrorData,
@@ -7,20 +7,15 @@ export class AggregatedError<RESULT = any> extends Error {
7
7
  errors!: Error[]
8
8
  results!: RESULT[]
9
9
 
10
- constructor(errors: (Error | string)[], results: RESULT[] = []) {
11
- const mappedErrors = errors.map(e => {
12
- if (typeof e === 'string') return new Error(e)
13
- return e
14
- })
15
-
10
+ constructor(errors: Error[], results: RESULT[] = []) {
16
11
  const message = [
17
12
  `${errors.length} errors:`,
18
- ...mappedErrors.map((e, i) => `${i + 1}. ${e.message}`),
13
+ ...errors.map((e, i) => `${i + 1}. ${e.message}`),
19
14
  ].join('\n')
20
15
 
21
16
  super(message)
22
17
 
23
- this.errors = mappedErrors
18
+ this.errors = errors
24
19
  this.results = results
25
20
 
26
21
  Object.defineProperty(this, 'name', {
@@ -1,16 +1,7 @@
1
- import { AbortableAsyncPredicate } from '../types'
2
- import { pMap, PMapOptions } from './pMap'
1
+ import { AsyncPredicate } from '../types'
3
2
 
4
- export async function pFilter<T>(
5
- iterable: Iterable<T | PromiseLike<T>>,
6
- filterFn: AbortableAsyncPredicate<T>,
7
- opt?: PMapOptions,
8
- ): Promise<T[]> {
9
- const values = await pMap(
10
- iterable,
11
- async (item, index) => await Promise.all([filterFn(item, index), item]),
12
- opt,
13
- )
14
-
15
- return values.filter(value => Boolean(value[0])).map(value => value[1])
3
+ export async function pFilter<T>(iterable: Iterable<T>, filterFn: AsyncPredicate<T>): Promise<T[]> {
4
+ const items = [...iterable]
5
+ const predicates = await Promise.all(items.map((item, i) => filterFn(item, i)))
6
+ return items.filter((item, i) => predicates[i])
16
7
  }
@@ -55,36 +55,85 @@ export interface PMapOptions {
55
55
  * })();
56
56
  */
57
57
  export async function pMap<IN, OUT>(
58
- iterable: Iterable<IN | PromiseLike<IN>>,
58
+ iterable: Iterable<IN>,
59
59
  mapper: AbortableAsyncMapper<IN, OUT>,
60
60
  opt: PMapOptions = {},
61
61
  ): Promise<OUT[]> {
62
- return new Promise<OUT[]>((resolve, reject) => {
63
- const { concurrency = Number.POSITIVE_INFINITY, errorMode = ErrorMode.THROW_IMMEDIATELY } = opt
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
- const ret: (OUT | typeof SKIP)[] = []
66
- const iterator = iterable[Symbol.iterator]()
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
- const next = (skipped = false) => {
123
+ return new Promise<OUT[]>((resolve, reject) => {
124
+ const next = () => {
74
125
  if (isSettled) {
75
126
  return
76
127
  }
77
128
 
78
- const nextItem = iterator.next()
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 && errorMode === ErrorMode.THROW_AGGREGATED) {
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.value)
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
- errors.push(err)
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 (isIterableDone) {
179
+ if (isSettled) {
129
180
  break
130
181
  }
131
182
  }
@@ -1,4 +1,4 @@
1
- import { _since, _stringifyAny, AnyFunction, CommonLogger } from '..'
1
+ import { _since, _stringifyAny, AnyFunction, AnyObject, AppError, CommonLogger } from '..'
2
2
  import { TimeoutError } from './pTimeout'
3
3
 
4
4
  export interface PRetryOptions {
@@ -11,7 +11,7 @@ export interface PRetryOptions {
11
11
  /**
12
12
  * Timeout for each Try, in milliseconds.
13
13
  *
14
- * Defaults to 60_000
14
+ * Defaults to no timeout.
15
15
  */
16
16
  timeout?: number
17
17
 
@@ -91,6 +91,11 @@ export interface PRetryOptions {
91
91
  * @experimental
92
92
  */
93
93
  keepStackTrace?: boolean
94
+
95
+ /**
96
+ * Will be merged with `err.data` object.
97
+ */
98
+ errorData?: AnyObject
94
99
  }
95
100
 
96
101
  /**
@@ -139,7 +144,7 @@ export async function pRetry<T>(
139
144
  return await new Promise((resolve, reject) => {
140
145
  const rejectWithTimeout = () => {
141
146
  timedOut = true // to prevent more tries
142
- const err = new TimeoutError(`"${fname}" timed out after ${timeout} ms`)
147
+ const err = new TimeoutError(`"${fname}" timed out after ${timeout} ms`, opt.errorData)
143
148
  if (fakeError) {
144
149
  // keep original stack
145
150
  err.stack = fakeError.stack!.replace('Error: RetryError', 'TimeoutError')
@@ -199,6 +204,11 @@ export async function pRetry<T>(
199
204
  })
200
205
  }
201
206
 
207
+ ;(err as AppError).data = {
208
+ ...(err as AppError).data,
209
+ ...opt.errorData,
210
+ }
211
+
202
212
  reject(err)
203
213
  } else {
204
214
  // Retry after delay
@@ -1,5 +1,5 @@
1
1
  import { AppError } from '../error/app.error'
2
- import { AnyFunction } from '../types'
2
+ import { AnyFunction, AnyObject } from '../types'
3
3
 
4
4
  export class TimeoutError extends AppError {}
5
5
 
@@ -29,6 +29,11 @@ export interface PTimeoutOptions {
29
29
  * @experimental
30
30
  */
31
31
  keepStackTrace?: boolean
32
+
33
+ /**
34
+ * Will be merged with `err.data` object.
35
+ */
36
+ errorData?: AnyObject
32
37
  }
33
38
 
34
39
  /**
@@ -63,12 +68,19 @@ export async function pTimeout<T>(promise: Promise<T>, opt: PTimeoutOptions): Pr
63
68
  resolve(onTimeout())
64
69
  } catch (err: any) {
65
70
  if (fakeError) err.stack = fakeError.stack // keep original stack
71
+ err.data = {
72
+ ...err.data,
73
+ ...opt.errorData,
74
+ }
66
75
  reject(err)
67
76
  }
68
77
  return
69
78
  }
70
79
 
71
- const err = new TimeoutError(`"${name || 'pTimeout function'}" timed out after ${timeout} ms`)
80
+ const err = new TimeoutError(
81
+ `"${name || 'pTimeout function'}" timed out after ${timeout} ms`,
82
+ opt.errorData,
83
+ )
72
84
  if (fakeError) err.stack = fakeError.stack // keep original stack
73
85
  reject(err)
74
86
  }, timeout)
@@ -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>>;
@@ -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
- }
@@ -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
- }