@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.
Files changed (49) 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 +32 -8
  6. package/dist/decorators/memo.util.js +17 -2
  7. package/dist/decorators/retry.decorator.js +1 -1
  8. package/dist/error/error.model.d.ts +6 -0
  9. package/dist/index.d.ts +5 -5
  10. package/dist/index.js +3 -2
  11. package/dist/json-schema/jsonSchemaBuilder.d.ts +2 -2
  12. package/dist/json-schema/jsonSchemaBuilder.js +4 -4
  13. package/dist/json-schema/jsonSchemas.d.ts +2 -2
  14. package/dist/promise/AggregatedError.d.ts +1 -1
  15. package/dist/promise/AggregatedError.js +2 -7
  16. package/dist/promise/pFilter.d.ts +2 -3
  17. package/dist/promise/pFilter.js +4 -4
  18. package/dist/promise/pMap.d.ts +1 -1
  19. package/dist/promise/pMap.js +67 -19
  20. package/dist/promise/pRetry.d.ts +17 -2
  21. package/dist/promise/pRetry.js +72 -40
  22. package/dist/types.d.ts +5 -5
  23. package/dist-esm/decorators/asyncMemo.decorator.js +104 -0
  24. package/dist-esm/decorators/memo.decorator.js +11 -5
  25. package/dist-esm/decorators/memo.util.js +15 -1
  26. package/dist-esm/decorators/retry.decorator.js +2 -2
  27. package/dist-esm/index.js +3 -3
  28. package/dist-esm/json-schema/jsonSchemaBuilder.js +4 -4
  29. package/dist-esm/promise/AggregatedError.js +2 -7
  30. package/dist-esm/promise/pFilter.js +4 -4
  31. package/dist-esm/promise/pMap.js +79 -19
  32. package/dist-esm/promise/pRetry.js +70 -39
  33. package/package.json +1 -1
  34. package/src/decorators/asyncMemo.decorator.ts +151 -0
  35. package/src/decorators/memo.decorator.ts +16 -5
  36. package/src/decorators/memo.util.ts +49 -10
  37. package/src/decorators/retry.decorator.ts +2 -2
  38. package/src/error/error.model.ts +7 -0
  39. package/src/index.ts +5 -3
  40. package/src/json-schema/jsonSchemaBuilder.ts +4 -4
  41. package/src/promise/AggregatedError.ts +3 -8
  42. package/src/promise/pFilter.ts +5 -14
  43. package/src/promise/pMap.ts +72 -21
  44. package/src/promise/pRetry.ts +105 -41
  45. package/src/types.ts +5 -5
  46. package/dist/promise/pBatch.d.ts +0 -7
  47. package/dist/promise/pBatch.js +0 -30
  48. package/dist-esm/promise/pBatch.js +0 -23
  49. package/src/promise/pBatch.ts +0 -31
@@ -1,58 +1,90 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.pRetry = void 0;
3
+ exports.pRetry = exports.pRetryFn = void 0;
4
4
  const __1 = require("..");
5
+ const pTimeout_1 = require("./pTimeout");
5
6
  /**
6
7
  * Returns a Function (!), enhanced with retry capabilities.
7
8
  * Implements "Exponential back-off strategy" by multiplying the delay by `delayMultiplier` with each try.
8
9
  */
9
- // eslint-disable-next-line @typescript-eslint/ban-types
10
- function pRetry(fn, opt = {}) {
11
- const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = console, name = fn.name, } = opt;
10
+ function pRetryFn(fn, opt = {}) {
11
+ return async function pRetryFunction(...args) {
12
+ return await pRetry(() => fn.call(this, ...args), opt);
13
+ };
14
+ }
15
+ exports.pRetryFn = pRetryFn;
16
+ async function pRetry(fn, opt = {}) {
17
+ const { maxAttempts = 4, delay: initialDelay = 1000, delayMultiplier = 2, predicate, logger = console, name, keepStackTrace = true, timeout, } = opt;
18
+ const fakeError = keepStackTrace ? new Error('RetryError') : undefined;
12
19
  let { logFirstAttempt = false, logRetries = true, logFailures = false, logSuccess = false } = opt;
13
20
  if (opt.logAll) {
14
- logFirstAttempt = logRetries = logFailures = true;
21
+ logSuccess = logFirstAttempt = logRetries = logFailures = true;
15
22
  }
16
23
  if (opt.logNone) {
17
24
  logSuccess = logFirstAttempt = logRetries = logFailures = false;
18
25
  }
19
- const fname = ['pRetry', name].filter(Boolean).join('.');
20
- return async function (...args) {
21
- let delay = initialDelay;
22
- let attempt = 0;
23
- return await new Promise((resolve, reject) => {
24
- const next = async () => {
25
- const started = Date.now();
26
- try {
27
- attempt++;
28
- if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
29
- logger.log(`${fname} attempt #${attempt}...`);
30
- }
31
- const r = await fn.apply(this, args);
32
- if (logSuccess) {
33
- logger.log(`${fname} attempt #${attempt} succeeded in ${(0, __1._since)(started)}`);
34
- }
35
- resolve(r);
26
+ const fname = name || fn.name || 'pRetry function';
27
+ let delay = initialDelay;
28
+ let attempt = 0;
29
+ let timer;
30
+ let timedOut = false;
31
+ return await new Promise((resolve, reject) => {
32
+ const rejectWithTimeout = () => {
33
+ timedOut = true; // to prevent more tries
34
+ const err = new pTimeout_1.TimeoutError(`"${fname}" timed out after ${timeout} ms`);
35
+ if (fakeError) {
36
+ // keep original stack
37
+ err.stack = fakeError.stack.replace('Error: RetryError', 'TimeoutError');
38
+ }
39
+ reject(err);
40
+ };
41
+ const next = async () => {
42
+ if (timedOut)
43
+ return;
44
+ if (timeout) {
45
+ timer = setTimeout(rejectWithTimeout, timeout);
46
+ }
47
+ const started = Date.now();
48
+ try {
49
+ attempt++;
50
+ if ((attempt === 1 && logFirstAttempt) || (attempt > 1 && logRetries)) {
51
+ logger.log(`${fname} attempt #${attempt}...`);
36
52
  }
37
- catch (err) {
38
- if (logFailures) {
39
- logger.warn(`${fname} attempt #${attempt} error in ${(0, __1._since)(started)}:`, (0, __1._stringifyAny)(err, {
40
- includeErrorData: true,
41
- }));
42
- }
43
- if (attempt >= maxAttempts || (predicate && !predicate(err, attempt, maxAttempts))) {
44
- // Give up
45
- reject(err);
46
- }
47
- else {
48
- // Retry after delay
49
- delay *= delayMultiplier;
50
- setTimeout(next, delay);
53
+ const r = await fn(attempt);
54
+ clearTimeout(timer);
55
+ if (logSuccess) {
56
+ logger.log(`${fname} attempt #${attempt} succeeded in ${(0, __1._since)(started)}`);
57
+ }
58
+ resolve(r);
59
+ }
60
+ catch (err) {
61
+ clearTimeout(timer);
62
+ if (logFailures) {
63
+ logger.warn(`${fname} attempt #${attempt} error in ${(0, __1._since)(started)}:`, (0, __1._stringifyAny)(err, {
64
+ includeErrorData: true,
65
+ }));
66
+ }
67
+ if (attempt >= maxAttempts ||
68
+ (predicate && !predicate(err, attempt, maxAttempts))) {
69
+ // Give up
70
+ if (fakeError) {
71
+ // Preserve the original call stack
72
+ Object.defineProperty(err, 'stack', {
73
+ value: err.stack +
74
+ '\n --' +
75
+ fakeError.stack.replace('Error: RetryError', ''),
76
+ });
51
77
  }
78
+ reject(err);
52
79
  }
53
- };
54
- void next();
55
- });
56
- };
80
+ else {
81
+ // Retry after delay
82
+ delay *= delayMultiplier;
83
+ setTimeout(next, delay);
84
+ }
85
+ }
86
+ };
87
+ void next();
88
+ });
57
89
  }
58
90
  exports.pRetry = pRetry;
package/dist/types.d.ts CHANGED
@@ -127,8 +127,8 @@ export declare type UnixTimestamp = number;
127
127
  /**
128
128
  * Base interface for any Entity that was saved to DB.
129
129
  */
130
- export interface SavedDBEntity {
131
- id: string;
130
+ export interface SavedDBEntity<ID = string> {
131
+ id: ID;
132
132
  /**
133
133
  * unixTimestamp of when the entity was first created (in the DB).
134
134
  */
@@ -144,9 +144,9 @@ export interface SavedDBEntity {
144
144
  * hence `id`, `created` and `updated` fields CAN BE undefined (yet).
145
145
  * When it's known to be saved - `SavedDBEntity` interface can be used instead.
146
146
  */
147
- export declare type BaseDBEntity = Partial<SavedDBEntity>;
148
- export declare type Saved<E> = Merge<E, SavedDBEntity>;
149
- export declare type Unsaved<E> = Merge<E, BaseDBEntity>;
147
+ export declare type BaseDBEntity<ID = string> = Partial<SavedDBEntity<ID>>;
148
+ export declare type Saved<E, ID = string> = Merge<E, SavedDBEntity<ID>>;
149
+ export declare type Unsaved<E, ID = string> = Merge<E, BaseDBEntity<ID>>;
150
150
  /**
151
151
  * Named type for JSON.parse / JSON.stringify second argument
152
152
  */
@@ -0,0 +1,104 @@
1
+ import { __asyncValues } from "tslib";
2
+ import { _since } from '../time/time.util';
3
+ import { _getArgsSignature, _getMethodSignature, _getTargetMethodSignature } from './decorator.util';
4
+ import { CACHE_DROP } from './memo.decorator';
5
+ import { jsonMemoSerializer } from './memo.util';
6
+ /**
7
+ * Like @_Memo, but allowing async MemoCache implementation.
8
+ *
9
+ * Method CANNOT return `undefined`, as undefined will always be treated as cache MISS and retried.
10
+ * Return `null` instead (it'll be cached).
11
+ */
12
+ // eslint-disable-next-line @typescript-eslint/naming-convention
13
+ export const _AsyncMemo = (opt) => (target, key, descriptor) => {
14
+ if (typeof descriptor.value !== 'function') {
15
+ throw new TypeError('Memoization can be applied only to methods');
16
+ }
17
+ const originalFn = descriptor.value;
18
+ // Map from "instance" of the Class where @_AsyncMemo is applied to AsyncMemoCache instance.
19
+ const cache = new Map();
20
+ const { logHit = false, logMiss = false, noLogArgs = false, logger = console, cacheFactory, cacheKeyFn = jsonMemoSerializer, noCacheRejected = false, noCacheResolved = false, } = opt;
21
+ const keyStr = String(key);
22
+ const methodSignature = _getTargetMethodSignature(target, keyStr);
23
+ descriptor.value = async function (...args) {
24
+ var e_1, _a;
25
+ const ctx = this;
26
+ const cacheKey = cacheKeyFn(args);
27
+ if (!cache.has(ctx)) {
28
+ cache.set(ctx, cacheFactory());
29
+ // here, no need to check the cache. It's definitely a miss, because the cacheLayers is just created
30
+ // UPD: no! AsyncMemo supports "persistent caches" (e.g Database-backed cache)
31
+ }
32
+ if (args.length === 1 && args[0] === CACHE_DROP) {
33
+ // Special event - CACHE_DROP
34
+ // Function will return undefined
35
+ logger.log(`${methodSignature} @_AsyncMemo.dropCache()`);
36
+ try {
37
+ await Promise.all(cache.get(ctx).map(c => c.clear()));
38
+ }
39
+ catch (err) {
40
+ logger.error(err);
41
+ }
42
+ return;
43
+ }
44
+ let value;
45
+ try {
46
+ try {
47
+ for (var _b = __asyncValues(cache.get(ctx)), _c; _c = await _b.next(), !_c.done;) {
48
+ const cacheLayer = _c.value;
49
+ value = await cacheLayer.get(cacheKey);
50
+ if (value !== undefined) {
51
+ // it's a hit!
52
+ break;
53
+ }
54
+ }
55
+ }
56
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
57
+ finally {
58
+ try {
59
+ if (_c && !_c.done && (_a = _b.return)) await _a.call(_b);
60
+ }
61
+ finally { if (e_1) throw e_1.error; }
62
+ }
63
+ }
64
+ catch (err) {
65
+ // log error, but don't throw, treat it as a "miss"
66
+ logger.error(err);
67
+ }
68
+ if (value !== undefined) {
69
+ // hit!
70
+ if (logHit) {
71
+ logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, noLogArgs)}) @_AsyncMemo hit`);
72
+ }
73
+ return value instanceof Error ? Promise.reject(value) : Promise.resolve(value);
74
+ }
75
+ // Here we know it's a MISS, let's execute the real method
76
+ const started = Date.now();
77
+ try {
78
+ value = await originalFn.apply(ctx, args);
79
+ if (!noCacheResolved) {
80
+ Promise.all(cache.get(ctx).map(cacheLayer => cacheLayer.set(cacheKey, value))).catch(err => {
81
+ // log and ignore the error
82
+ logger.error(err);
83
+ });
84
+ }
85
+ return value;
86
+ }
87
+ catch (err) {
88
+ if (!noCacheRejected) {
89
+ // We put it to cache as raw Error, not Promise.reject(err)
90
+ Promise.all(cache.get(ctx).map(cacheLayer => cacheLayer.set(cacheKey, err))).catch(err => {
91
+ // log and ignore the error
92
+ logger.error(err);
93
+ });
94
+ }
95
+ throw err;
96
+ }
97
+ finally {
98
+ if (logMiss) {
99
+ logger.log(`${_getMethodSignature(ctx, keyStr)}(${_getArgsSignature(args, noLogArgs)}) @_AsyncMemo miss (${_since(started)})`);
100
+ }
101
+ }
102
+ };
103
+ return descriptor;
104
+ };
@@ -6,6 +6,10 @@
6
6
  import { _since } from '../time/time.util';
7
7
  import { _getArgsSignature, _getMethodSignature, _getTargetMethodSignature } from './decorator.util';
8
8
  import { jsonMemoSerializer, MapMemoCache } from './memo.util';
9
+ /**
10
+ * Symbol to indicate that the Cache should be dropped.
11
+ */
12
+ export const CACHE_DROP = Symbol('CACHE_DROP');
9
13
  /**
10
14
  * Memoizes the method of the class, so it caches the output and returns the cached version if the "key"
11
15
  * of the cache is the same. Key, by defaul, is calculated as `JSON.stringify(...args)`.
@@ -35,7 +39,14 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
35
39
  const keyStr = String(key);
36
40
  const methodSignature = _getTargetMethodSignature(target, keyStr);
37
41
  descriptor.value = function (...args) {
42
+ var _a;
38
43
  const ctx = this;
44
+ if (args.length === 1 && args[0] === CACHE_DROP) {
45
+ // Special event - CACHE_DROP
46
+ // Function will return undefined
47
+ logger.log(`${methodSignature} @_Memo.CACHE_DROP`);
48
+ return (_a = cache.get(ctx)) === null || _a === void 0 ? void 0 : _a.clear();
49
+ }
39
50
  const cacheKey = cacheKeyFn(args);
40
51
  if (!cache.has(ctx)) {
41
52
  cache.set(ctx, cacheFactory());
@@ -88,10 +99,5 @@ export const _Memo = (opt = {}) => (target, key, descriptor) => {
88
99
  return res;
89
100
  }
90
101
  };
91
- descriptor.value.dropCache = () => {
92
- logger.log(`${methodSignature} @_Memo.dropCache()`);
93
- cache.forEach(memoCache => memoCache.clear());
94
- cache.clear();
95
- };
96
102
  return descriptor;
97
103
  };
@@ -1,6 +1,6 @@
1
1
  import { _isPrimitive } from '../object/object.util';
2
2
  export const jsonMemoSerializer = args => {
3
- if (!args.length)
3
+ if (args.length === 0)
4
4
  return undefined;
5
5
  if (args.length === 1 && _isPrimitive(args[0]))
6
6
  return args[0];
@@ -68,3 +68,17 @@ export class MapMemoCache {
68
68
  this.m.clear();
69
69
  }
70
70
  }
71
+ export class MapAsyncMemoCache {
72
+ constructor() {
73
+ this.m = new Map();
74
+ }
75
+ async get(k) {
76
+ return this.m.get(k);
77
+ }
78
+ async set(k, v) {
79
+ this.m.set(k, v);
80
+ }
81
+ async clear() {
82
+ this.m.clear();
83
+ }
84
+ }
@@ -1,9 +1,9 @@
1
- import { pRetry } from '..';
1
+ import { pRetryFn } from '..';
2
2
  // eslint-disable-next-line @typescript-eslint/naming-convention
3
3
  export function _Retry(opt = {}) {
4
4
  return (target, key, descriptor) => {
5
5
  const originalFn = descriptor.value;
6
- descriptor.value = pRetry(originalFn, opt);
6
+ descriptor.value = pRetryFn(originalFn, opt);
7
7
  return descriptor;
8
8
  };
9
9
  }
package/dist-esm/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './decorators/debounce.decorator';
8
8
  export * from './decorators/decorator.util';
9
9
  export * from './decorators/logMethod.decorator';
10
10
  export * from './decorators/memo.decorator';
11
+ export * from './decorators/asyncMemo.decorator';
11
12
  export * from './decorators/memoFn';
12
13
  export * from './decorators/retry.decorator';
13
14
  export * from './decorators/timeout.decorator';
@@ -31,14 +32,13 @@ export * from './object/object.util';
31
32
  export * from './object/sortObject';
32
33
  export * from './object/sortObjectDeep';
33
34
  import { AggregatedError } from './promise/AggregatedError';
34
- export * from './promise/pBatch';
35
35
  import { pDefer } from './promise/pDefer';
36
36
  export * from './promise/pDelay';
37
37
  export * from './promise/pFilter';
38
38
  export * from './promise/pHang';
39
39
  import { pMap } from './promise/pMap';
40
40
  export * from './promise/pProps';
41
- import { pRetry } from './promise/pRetry';
41
+ import { pRetry, pRetryFn } from './promise/pRetry';
42
42
  export * from './promise/pState';
43
43
  import { pTimeout, pTimeoutFn } from './promise/pTimeout';
44
44
  export * from './promise/pTuple';
@@ -55,4 +55,4 @@ export * from './string/safeJsonStringify';
55
55
  import { PQueue } from './promise/pQueue';
56
56
  export * from './seq/seq';
57
57
  export * from './math/stack.util';
58
- export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
58
+ export { is, _createPromiseDecorator, _stringMapValues, _stringMapEntries, _objectKeys, pMap, _passthroughMapper, _passUndefinedMapper, _passthroughPredicate, _passNothingPredicate, _noop, ErrorMode, pDefer, AggregatedError, pRetry, pRetryFn, pTimeout, pTimeoutFn, _tryCatch, _TryCatch, _stringifyAny, jsonSchema, JsonSchemaAnyBuilder, commonLoggerMinLevel, commonLoggerNoop, commonLogLevelNumber, commonLoggerPipe, commonLoggerPrefix, commonLoggerCreate, PQueue, END, SKIP, };
@@ -301,16 +301,16 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
301
301
  Object.assign(this.schema, { additionalProperties });
302
302
  return this;
303
303
  }
304
- baseDBEntity() {
304
+ baseDBEntity(idType = 'string') {
305
305
  Object.assign(this.schema.properties, {
306
- id: { type: 'string' },
306
+ id: { type: idType },
307
307
  created: { type: 'number', format: 'unixTimestamp' },
308
308
  updated: { type: 'number', format: 'unixTimestamp' },
309
309
  });
310
310
  return this;
311
311
  }
312
- savedDBEntity() {
313
- return this.baseDBEntity().addRequired(['id', 'created', 'updated']);
312
+ savedDBEntity(idType = 'string') {
313
+ return this.baseDBEntity(idType).addRequired(['id', 'created', 'updated']);
314
314
  }
315
315
  extend(s2) {
316
316
  const builder = new JsonSchemaObjectBuilder();
@@ -5,17 +5,12 @@
5
5
  */
6
6
  export class AggregatedError extends Error {
7
7
  constructor(errors, results = []) {
8
- const mappedErrors = errors.map(e => {
9
- if (typeof e === 'string')
10
- return new Error(e);
11
- return e;
12
- });
13
8
  const message = [
14
9
  `${errors.length} errors:`,
15
- ...mappedErrors.map((e, i) => `${i + 1}. ${e.message}`),
10
+ ...errors.map((e, i) => `${i + 1}. ${e.message}`),
16
11
  ].join('\n');
17
12
  super(message);
18
- this.errors = mappedErrors;
13
+ this.errors = errors;
19
14
  this.results = results;
20
15
  Object.defineProperty(this, 'name', {
21
16
  value: this.constructor.name,
@@ -1,5 +1,5 @@
1
- import { pMap } from './pMap';
2
- export async function pFilter(iterable, filterFn, opt) {
3
- const values = await pMap(iterable, async (item, index) => await Promise.all([filterFn(item, index), item]), opt);
4
- return values.filter(value => Boolean(value[0])).map(value => value[1]);
1
+ export async function pFilter(iterable, filterFn) {
2
+ const items = [...iterable];
3
+ const predicates = await Promise.all(items.map((item, i) => filterFn(item, i)));
4
+ return items.filter((item, i) => predicates[i]);
5
5
  }
@@ -6,6 +6,7 @@ Improvements:
6
6
  - Included Typescript typings (no need for @types/p-map)
7
7
  - Compatible with pProps (that had typings issues)
8
8
  */
9
+ import { __asyncValues } from "tslib";
9
10
  import { END, ErrorMode, SKIP } from '..';
10
11
  import { AggregatedError } from './AggregatedError';
11
12
  /**
@@ -35,28 +36,85 @@ import { AggregatedError } from './AggregatedError';
35
36
  * })();
36
37
  */
37
38
  export async function pMap(iterable, mapper, opt = {}) {
39
+ var e_1, _a;
40
+ const ret = [];
41
+ // const iterator = iterable[Symbol.iterator]()
42
+ const items = [...iterable];
43
+ const itemsLength = items.length;
44
+ if (itemsLength === 0)
45
+ return []; // short circuit
46
+ const { concurrency = itemsLength, errorMode = ErrorMode.THROW_IMMEDIATELY } = opt;
47
+ const errors = [];
48
+ let isSettled = false;
49
+ let resolvingCount = 0;
50
+ let currentIndex = 0;
51
+ // Special cases that are able to preserve async stack traces
52
+ if (concurrency === 1) {
53
+ try {
54
+ // Special case for concurrency == 1
55
+ for (var items_1 = __asyncValues(items), items_1_1; items_1_1 = await items_1.next(), !items_1_1.done;) {
56
+ const item = items_1_1.value;
57
+ try {
58
+ const r = await mapper(item, currentIndex++);
59
+ if (r === END)
60
+ break;
61
+ if (r !== SKIP)
62
+ ret.push(r);
63
+ }
64
+ catch (err) {
65
+ if (errorMode === ErrorMode.THROW_IMMEDIATELY)
66
+ throw err;
67
+ if (errorMode === ErrorMode.THROW_AGGREGATED) {
68
+ errors.push(err);
69
+ }
70
+ // otherwise, suppress completely
71
+ }
72
+ }
73
+ }
74
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
75
+ finally {
76
+ try {
77
+ if (items_1_1 && !items_1_1.done && (_a = items_1.return)) await _a.call(items_1);
78
+ }
79
+ finally { if (e_1) throw e_1.error; }
80
+ }
81
+ if (errors.length) {
82
+ throw new AggregatedError(errors, ret);
83
+ }
84
+ return ret;
85
+ }
86
+ else if (!opt.concurrency || items.length <= opt.concurrency) {
87
+ // Special case for concurrency == infinity or iterable.length < concurrency
88
+ if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
89
+ return (await Promise.all(items.map((item, i) => mapper(item, i)))).filter(r => r !== SKIP && r !== END);
90
+ }
91
+ ;
92
+ (await Promise.allSettled(items.map((item, i) => mapper(item, i)))).forEach(r => {
93
+ if (r.status === 'fulfilled') {
94
+ if (r.value !== SKIP && r.value !== END)
95
+ ret.push(r.value);
96
+ }
97
+ else if (errorMode === ErrorMode.THROW_AGGREGATED) {
98
+ errors.push(r.reason);
99
+ }
100
+ });
101
+ if (errors.length) {
102
+ throw new AggregatedError(errors, ret);
103
+ }
104
+ return ret;
105
+ }
38
106
  return new Promise((resolve, reject) => {
39
- const { concurrency = Number.POSITIVE_INFINITY, errorMode = ErrorMode.THROW_IMMEDIATELY } = opt;
40
- const ret = [];
41
- const iterator = iterable[Symbol.iterator]();
42
- const errors = [];
43
- let isSettled = false;
44
- let isIterableDone = false;
45
- let resolvingCount = 0;
46
- let currentIndex = 0;
47
- const next = (skipped = false) => {
107
+ const next = () => {
48
108
  if (isSettled) {
49
109
  return;
50
110
  }
51
- const nextItem = iterator.next();
52
- const i = currentIndex;
53
- if (!skipped)
54
- currentIndex++;
55
- if (nextItem.done) {
56
- isIterableDone = true;
111
+ const nextItem = items[currentIndex];
112
+ const i = currentIndex++;
113
+ if (currentIndex > itemsLength) {
57
114
  if (resolvingCount === 0) {
115
+ isSettled = true;
58
116
  const r = ret.filter(r => r !== SKIP);
59
- if (errors.length && errorMode === ErrorMode.THROW_AGGREGATED) {
117
+ if (errors.length) {
60
118
  reject(new AggregatedError(errors, r));
61
119
  }
62
120
  else {
@@ -66,7 +124,7 @@ export async function pMap(iterable, mapper, opt = {}) {
66
124
  return;
67
125
  }
68
126
  resolvingCount++;
69
- Promise.resolve(nextItem.value)
127
+ Promise.resolve(nextItem)
70
128
  .then(async (element) => await mapper(element, i))
71
129
  .then(value => {
72
130
  if (value === END) {
@@ -82,7 +140,9 @@ export async function pMap(iterable, mapper, opt = {}) {
82
140
  reject(err);
83
141
  }
84
142
  else {
85
- errors.push(err);
143
+ if (errorMode === ErrorMode.THROW_AGGREGATED) {
144
+ errors.push(err);
145
+ }
86
146
  resolvingCount--;
87
147
  next();
88
148
  }
@@ -90,7 +150,7 @@ export async function pMap(iterable, mapper, opt = {}) {
90
150
  };
91
151
  for (let i = 0; i < concurrency; i++) {
92
152
  next();
93
- if (isIterableDone) {
153
+ if (isSettled) {
94
154
  break;
95
155
  }
96
156
  }