@naturalcycles/nodejs-lib 13.15.0 → 13.16.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/stream/progressLogger.d.ts +5 -0
- package/dist/stream/progressLogger.js +1 -0
- package/dist/stream/transform/transformMap.d.ts +24 -0
- package/dist/stream/transform/transformMap.js +24 -1
- package/dist/stream/transform/transformMapSync.d.ts +12 -0
- package/dist/stream/transform/transformMapSync.js +25 -2
- package/package.json +1 -1
- package/src/stream/progressLogger.ts +8 -0
- package/src/stream/transform/transformMap.ts +53 -0
- package/src/stream/transform/transformMapSync.ts +42 -2
|
@@ -76,6 +76,11 @@ export interface ProgressLoggerCfg<T = any> {
|
|
|
76
76
|
* chunk is undefined for "final" stats, otherwise is defined.
|
|
77
77
|
*/
|
|
78
78
|
extra?: (chunk: T | undefined, index: number) => AnyObject;
|
|
79
|
+
/**
|
|
80
|
+
* Hook that is called when the last item is passed through.
|
|
81
|
+
* Passes the final stats as `ProgressLogItem`.
|
|
82
|
+
*/
|
|
83
|
+
onProgressDone?: (stats: ProgressLogItem) => any;
|
|
79
84
|
/**
|
|
80
85
|
* If specified - will multiply the counter by this number.
|
|
81
86
|
* Useful e.g when using `transformChunk({ chunkSize: 500 })`, so
|
|
@@ -107,6 +107,7 @@ class ProgressLogger {
|
|
|
107
107
|
}
|
|
108
108
|
else if (final) {
|
|
109
109
|
logger.log(`${(0, colors_1.boldWhite)(metric)} took ${(0, colors_1.yellow)((0, js_lib_1._since)(this.started))} to process ${(0, colors_1.yellow)(batchedProgress)} rows with total RPS of ${(0, colors_1.yellow)(rpsTotal)}`);
|
|
110
|
+
this.cfg.onProgressDone?.(o);
|
|
110
111
|
}
|
|
111
112
|
}
|
|
112
113
|
}
|
|
@@ -30,6 +30,17 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
30
30
|
* Called BEFORE observable will emit error (unless skipErrors is set to true).
|
|
31
31
|
*/
|
|
32
32
|
onError?: (err: Error, input: IN) => any;
|
|
33
|
+
/**
|
|
34
|
+
* A hook that is called when the last item is finished processing.
|
|
35
|
+
* stats object is passed, containing countIn and countOut -
|
|
36
|
+
* number of items that entered the transform and number of items that left it.
|
|
37
|
+
*
|
|
38
|
+
* Callback is called **before** [possible] Aggregated error is thrown,
|
|
39
|
+
* and before [possible] THROW_IMMEDIATELY error.
|
|
40
|
+
*
|
|
41
|
+
* onDone callback will be called before Error is thrown.
|
|
42
|
+
*/
|
|
43
|
+
onDone?: (stats: TransformMapStats) => any;
|
|
33
44
|
/**
|
|
34
45
|
* Progress metric
|
|
35
46
|
*
|
|
@@ -38,6 +49,19 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
38
49
|
metric?: string;
|
|
39
50
|
logger?: CommonLogger;
|
|
40
51
|
}
|
|
52
|
+
export interface TransformMapStats {
|
|
53
|
+
/**
|
|
54
|
+
* True if transform was successful (didn't throw Immediate or Aggregated error).
|
|
55
|
+
*/
|
|
56
|
+
ok: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Only used (and returned) for ErrorMode.Aggregated
|
|
59
|
+
*/
|
|
60
|
+
collectedErrors: Error[];
|
|
61
|
+
countErrors: number;
|
|
62
|
+
countIn: number;
|
|
63
|
+
countOut: number;
|
|
64
|
+
}
|
|
41
65
|
/**
|
|
42
66
|
* Like pMap, but for streams.
|
|
43
67
|
* Inspired by `through2`.
|
|
@@ -21,8 +21,9 @@ const stream_util_1 = require("../stream.util");
|
|
|
21
21
|
*/
|
|
22
22
|
function transformMap(mapper, opt = {}) {
|
|
23
23
|
const { concurrency = 16, predicate, // we now default to "no predicate" (meaning pass-everything)
|
|
24
|
-
errorMode = js_lib_1.ErrorMode.THROW_IMMEDIATELY, flattenArrayOutput, onError, metric = 'stream', logger = console, } = opt;
|
|
24
|
+
errorMode = js_lib_1.ErrorMode.THROW_IMMEDIATELY, flattenArrayOutput, onError, onDone, metric = 'stream', logger = console, } = opt;
|
|
25
25
|
let index = -1;
|
|
26
|
+
let countOut = 0;
|
|
26
27
|
let isSettled = false;
|
|
27
28
|
let errors = 0;
|
|
28
29
|
const collectedErrors = []; // only used if errorMode == THROW_AGGREGATED
|
|
@@ -32,11 +33,25 @@ function transformMap(mapper, opt = {}) {
|
|
|
32
33
|
// console.log('transformMap final')
|
|
33
34
|
logErrorStats(true);
|
|
34
35
|
if (collectedErrors.length) {
|
|
36
|
+
onDone?.({
|
|
37
|
+
ok: false,
|
|
38
|
+
collectedErrors,
|
|
39
|
+
countErrors: errors,
|
|
40
|
+
countIn: index + 1,
|
|
41
|
+
countOut,
|
|
42
|
+
});
|
|
35
43
|
// emit Aggregated error
|
|
36
44
|
cb(new AggregateError(collectedErrors, `transformMap resulted in ${collectedErrors.length} error(s)`));
|
|
37
45
|
}
|
|
38
46
|
else {
|
|
39
47
|
// emit no error
|
|
48
|
+
onDone?.({
|
|
49
|
+
ok: true,
|
|
50
|
+
collectedErrors,
|
|
51
|
+
countErrors: errors,
|
|
52
|
+
countIn: index + 1,
|
|
53
|
+
countOut,
|
|
54
|
+
});
|
|
40
55
|
cb();
|
|
41
56
|
}
|
|
42
57
|
},
|
|
@@ -54,6 +69,7 @@ function transformMap(mapper, opt = {}) {
|
|
|
54
69
|
}
|
|
55
70
|
return r !== js_lib_1.SKIP && (!predicate || (await predicate(r, currentIndex)));
|
|
56
71
|
});
|
|
72
|
+
countOut += passedResults.length;
|
|
57
73
|
passedResults.forEach(r => this.push(r));
|
|
58
74
|
if (isSettled) {
|
|
59
75
|
logger.log(`transformMap END received at index ${currentIndex}`);
|
|
@@ -73,6 +89,13 @@ function transformMap(mapper, opt = {}) {
|
|
|
73
89
|
}
|
|
74
90
|
if (errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY) {
|
|
75
91
|
isSettled = true;
|
|
92
|
+
onDone?.({
|
|
93
|
+
ok: false,
|
|
94
|
+
collectedErrors,
|
|
95
|
+
countErrors: errors,
|
|
96
|
+
countIn: index + 1,
|
|
97
|
+
countOut,
|
|
98
|
+
});
|
|
76
99
|
return cb(err); // Emit error immediately
|
|
77
100
|
}
|
|
78
101
|
if (errorMode === js_lib_1.ErrorMode.THROW_AGGREGATED) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CommonLogger, END, ErrorMode, Mapper, Predicate, SKIP } from '@naturalcycles/js-lib';
|
|
2
2
|
import { AbortableTransform } from '../pipeline/pipeline';
|
|
3
3
|
import { TransformTyped } from '../stream.model';
|
|
4
|
+
import { TransformMapStats } from './transformMap';
|
|
4
5
|
export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
5
6
|
/**
|
|
6
7
|
* @default true
|
|
@@ -28,6 +29,17 @@ export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
|
28
29
|
* Called BEFORE observable will emit error (unless skipErrors is set to true).
|
|
29
30
|
*/
|
|
30
31
|
onError?: (err: Error, input: IN) => any;
|
|
32
|
+
/**
|
|
33
|
+
* A hook that is called when the last item is finished processing.
|
|
34
|
+
* stats object is passed, containing countIn and countOut -
|
|
35
|
+
* number of items that entered the transform and number of items that left it.
|
|
36
|
+
*
|
|
37
|
+
* Callback is called **before** [possible] Aggregated error is thrown,
|
|
38
|
+
* and before [possible] THROW_IMMEDIATELY error.
|
|
39
|
+
*
|
|
40
|
+
* onDone callback will be called before Error is thrown.
|
|
41
|
+
*/
|
|
42
|
+
onDone?: (stats: TransformMapStats) => any;
|
|
31
43
|
/**
|
|
32
44
|
* Progress metric
|
|
33
45
|
*
|
|
@@ -13,9 +13,10 @@ exports.TransformMapSync = TransformMapSync;
|
|
|
13
13
|
* Supposedly faster, for cases when async is not needed.
|
|
14
14
|
*/
|
|
15
15
|
function transformMapSync(mapper, opt = {}) {
|
|
16
|
-
let index = -1;
|
|
17
16
|
const { predicate, // defaults to "no predicate" (pass everything)
|
|
18
|
-
errorMode = js_lib_1.ErrorMode.THROW_IMMEDIATELY, flattenArrayOutput = false, onError, metric = 'stream', objectMode = true, logger = console, } = opt;
|
|
17
|
+
errorMode = js_lib_1.ErrorMode.THROW_IMMEDIATELY, flattenArrayOutput = false, onError, onDone, metric = 'stream', objectMode = true, logger = console, } = opt;
|
|
18
|
+
let index = -1;
|
|
19
|
+
let countOut = 0;
|
|
19
20
|
let isSettled = false;
|
|
20
21
|
let errors = 0;
|
|
21
22
|
const collectedErrors = []; // only used if errorMode == THROW_AGGREGATED
|
|
@@ -37,6 +38,7 @@ function transformMapSync(mapper, opt = {}) {
|
|
|
37
38
|
}
|
|
38
39
|
return r !== js_lib_1.SKIP && (!predicate || predicate(r, currentIndex));
|
|
39
40
|
});
|
|
41
|
+
countOut += passedResults.length;
|
|
40
42
|
passedResults.forEach(r => this.push(r));
|
|
41
43
|
if (isSettled) {
|
|
42
44
|
logger.log(`transformMapSync END received at index ${currentIndex}`);
|
|
@@ -56,6 +58,13 @@ function transformMapSync(mapper, opt = {}) {
|
|
|
56
58
|
}
|
|
57
59
|
if (errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY) {
|
|
58
60
|
isSettled = true;
|
|
61
|
+
onDone?.({
|
|
62
|
+
ok: false,
|
|
63
|
+
collectedErrors,
|
|
64
|
+
countErrors: errors,
|
|
65
|
+
countIn: index + 1,
|
|
66
|
+
countOut,
|
|
67
|
+
});
|
|
59
68
|
// Emit error immediately
|
|
60
69
|
return cb(err);
|
|
61
70
|
}
|
|
@@ -69,11 +78,25 @@ function transformMapSync(mapper, opt = {}) {
|
|
|
69
78
|
// console.log('transformMap final')
|
|
70
79
|
logErrorStats(true);
|
|
71
80
|
if (collectedErrors.length) {
|
|
81
|
+
onDone?.({
|
|
82
|
+
ok: false,
|
|
83
|
+
collectedErrors,
|
|
84
|
+
countErrors: errors,
|
|
85
|
+
countIn: index + 1,
|
|
86
|
+
countOut,
|
|
87
|
+
});
|
|
72
88
|
// emit Aggregated error
|
|
73
89
|
cb(new AggregateError(collectedErrors, `transformMapSync resulted in ${collectedErrors.length} error(s)`));
|
|
74
90
|
}
|
|
75
91
|
else {
|
|
76
92
|
// emit no error
|
|
93
|
+
onDone?.({
|
|
94
|
+
ok: true,
|
|
95
|
+
collectedErrors,
|
|
96
|
+
countErrors: errors,
|
|
97
|
+
countIn: index + 1,
|
|
98
|
+
countOut,
|
|
99
|
+
});
|
|
77
100
|
cb();
|
|
78
101
|
}
|
|
79
102
|
},
|
package/package.json
CHANGED
|
@@ -101,6 +101,12 @@ export interface ProgressLoggerCfg<T = any> {
|
|
|
101
101
|
*/
|
|
102
102
|
extra?: (chunk: T | undefined, index: number) => AnyObject
|
|
103
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Hook that is called when the last item is passed through.
|
|
106
|
+
* Passes the final stats as `ProgressLogItem`.
|
|
107
|
+
*/
|
|
108
|
+
onProgressDone?: (stats: ProgressLogItem) => any
|
|
109
|
+
|
|
104
110
|
/**
|
|
105
111
|
* If specified - will multiply the counter by this number.
|
|
106
112
|
* Useful e.g when using `transformChunk({ chunkSize: 500 })`, so
|
|
@@ -298,6 +304,8 @@ export class ProgressLogger<T> implements Disposable {
|
|
|
298
304
|
batchedProgress,
|
|
299
305
|
)} rows with total RPS of ${yellow(rpsTotal)}`,
|
|
300
306
|
)
|
|
307
|
+
|
|
308
|
+
this.cfg.onProgressDone?.(o)
|
|
301
309
|
}
|
|
302
310
|
}
|
|
303
311
|
}
|
|
@@ -49,6 +49,18 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
49
49
|
*/
|
|
50
50
|
onError?: (err: Error, input: IN) => any
|
|
51
51
|
|
|
52
|
+
/**
|
|
53
|
+
* A hook that is called when the last item is finished processing.
|
|
54
|
+
* stats object is passed, containing countIn and countOut -
|
|
55
|
+
* number of items that entered the transform and number of items that left it.
|
|
56
|
+
*
|
|
57
|
+
* Callback is called **before** [possible] Aggregated error is thrown,
|
|
58
|
+
* and before [possible] THROW_IMMEDIATELY error.
|
|
59
|
+
*
|
|
60
|
+
* onDone callback will be called before Error is thrown.
|
|
61
|
+
*/
|
|
62
|
+
onDone?: (stats: TransformMapStats) => any
|
|
63
|
+
|
|
52
64
|
/**
|
|
53
65
|
* Progress metric
|
|
54
66
|
*
|
|
@@ -59,6 +71,20 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
59
71
|
logger?: CommonLogger
|
|
60
72
|
}
|
|
61
73
|
|
|
74
|
+
export interface TransformMapStats {
|
|
75
|
+
/**
|
|
76
|
+
* True if transform was successful (didn't throw Immediate or Aggregated error).
|
|
77
|
+
*/
|
|
78
|
+
ok: boolean
|
|
79
|
+
/**
|
|
80
|
+
* Only used (and returned) for ErrorMode.Aggregated
|
|
81
|
+
*/
|
|
82
|
+
collectedErrors: Error[]
|
|
83
|
+
countErrors: number
|
|
84
|
+
countIn: number
|
|
85
|
+
countOut: number
|
|
86
|
+
}
|
|
87
|
+
|
|
62
88
|
// doesn't work, cause here we don't construct our Transform instance ourselves
|
|
63
89
|
// export class TransformMap extends AbortableTransform {}
|
|
64
90
|
|
|
@@ -84,11 +110,13 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
84
110
|
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
85
111
|
flattenArrayOutput,
|
|
86
112
|
onError,
|
|
113
|
+
onDone,
|
|
87
114
|
metric = 'stream',
|
|
88
115
|
logger = console,
|
|
89
116
|
} = opt
|
|
90
117
|
|
|
91
118
|
let index = -1
|
|
119
|
+
let countOut = 0
|
|
92
120
|
let isSettled = false
|
|
93
121
|
let errors = 0
|
|
94
122
|
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
@@ -102,6 +130,14 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
102
130
|
logErrorStats(true)
|
|
103
131
|
|
|
104
132
|
if (collectedErrors.length) {
|
|
133
|
+
onDone?.({
|
|
134
|
+
ok: false,
|
|
135
|
+
collectedErrors,
|
|
136
|
+
countErrors: errors,
|
|
137
|
+
countIn: index + 1,
|
|
138
|
+
countOut,
|
|
139
|
+
})
|
|
140
|
+
|
|
105
141
|
// emit Aggregated error
|
|
106
142
|
cb(
|
|
107
143
|
new AggregateError(
|
|
@@ -111,6 +147,15 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
111
147
|
)
|
|
112
148
|
} else {
|
|
113
149
|
// emit no error
|
|
150
|
+
|
|
151
|
+
onDone?.({
|
|
152
|
+
ok: true,
|
|
153
|
+
collectedErrors,
|
|
154
|
+
countErrors: errors,
|
|
155
|
+
countIn: index + 1,
|
|
156
|
+
countOut,
|
|
157
|
+
})
|
|
158
|
+
|
|
114
159
|
cb()
|
|
115
160
|
}
|
|
116
161
|
},
|
|
@@ -134,6 +179,7 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
134
179
|
},
|
|
135
180
|
)
|
|
136
181
|
|
|
182
|
+
countOut += passedResults.length
|
|
137
183
|
passedResults.forEach(r => this.push(r))
|
|
138
184
|
|
|
139
185
|
if (isSettled) {
|
|
@@ -155,6 +201,13 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
155
201
|
|
|
156
202
|
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
157
203
|
isSettled = true
|
|
204
|
+
onDone?.({
|
|
205
|
+
ok: false,
|
|
206
|
+
collectedErrors,
|
|
207
|
+
countErrors: errors,
|
|
208
|
+
countIn: index + 1,
|
|
209
|
+
countOut,
|
|
210
|
+
})
|
|
158
211
|
return cb(err) // Emit error immediately
|
|
159
212
|
}
|
|
160
213
|
|
|
@@ -11,6 +11,7 @@ import { yellow } from '../../colors/colors'
|
|
|
11
11
|
import { AbortableTransform } from '../pipeline/pipeline'
|
|
12
12
|
import { TransformTyped } from '../stream.model'
|
|
13
13
|
import { pipelineClose } from '../stream.util'
|
|
14
|
+
import { TransformMapStats } from './transformMap'
|
|
14
15
|
|
|
15
16
|
export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
16
17
|
/**
|
|
@@ -44,6 +45,18 @@ export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
|
44
45
|
*/
|
|
45
46
|
onError?: (err: Error, input: IN) => any
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* A hook that is called when the last item is finished processing.
|
|
50
|
+
* stats object is passed, containing countIn and countOut -
|
|
51
|
+
* number of items that entered the transform and number of items that left it.
|
|
52
|
+
*
|
|
53
|
+
* Callback is called **before** [possible] Aggregated error is thrown,
|
|
54
|
+
* and before [possible] THROW_IMMEDIATELY error.
|
|
55
|
+
*
|
|
56
|
+
* onDone callback will be called before Error is thrown.
|
|
57
|
+
*/
|
|
58
|
+
onDone?: (stats: TransformMapStats) => any
|
|
59
|
+
|
|
47
60
|
/**
|
|
48
61
|
* Progress metric
|
|
49
62
|
*
|
|
@@ -64,17 +77,19 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
64
77
|
mapper: Mapper<IN, OUT | typeof SKIP | typeof END>,
|
|
65
78
|
opt: TransformMapSyncOptions = {},
|
|
66
79
|
): TransformTyped<IN, OUT> {
|
|
67
|
-
let index = -1
|
|
68
|
-
|
|
69
80
|
const {
|
|
70
81
|
predicate, // defaults to "no predicate" (pass everything)
|
|
71
82
|
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
72
83
|
flattenArrayOutput = false,
|
|
73
84
|
onError,
|
|
85
|
+
onDone,
|
|
74
86
|
metric = 'stream',
|
|
75
87
|
objectMode = true,
|
|
76
88
|
logger = console,
|
|
77
89
|
} = opt
|
|
90
|
+
|
|
91
|
+
let index = -1
|
|
92
|
+
let countOut = 0
|
|
78
93
|
let isSettled = false
|
|
79
94
|
let errors = 0
|
|
80
95
|
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
@@ -100,6 +115,7 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
100
115
|
return r !== SKIP && (!predicate || predicate(r, currentIndex))
|
|
101
116
|
})
|
|
102
117
|
|
|
118
|
+
countOut += passedResults.length
|
|
103
119
|
passedResults.forEach(r => this.push(r))
|
|
104
120
|
|
|
105
121
|
if (isSettled) {
|
|
@@ -122,6 +138,13 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
122
138
|
|
|
123
139
|
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
124
140
|
isSettled = true
|
|
141
|
+
onDone?.({
|
|
142
|
+
ok: false,
|
|
143
|
+
collectedErrors,
|
|
144
|
+
countErrors: errors,
|
|
145
|
+
countIn: index + 1,
|
|
146
|
+
countOut,
|
|
147
|
+
})
|
|
125
148
|
// Emit error immediately
|
|
126
149
|
return cb(err as Error)
|
|
127
150
|
}
|
|
@@ -139,6 +162,14 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
139
162
|
logErrorStats(true)
|
|
140
163
|
|
|
141
164
|
if (collectedErrors.length) {
|
|
165
|
+
onDone?.({
|
|
166
|
+
ok: false,
|
|
167
|
+
collectedErrors,
|
|
168
|
+
countErrors: errors,
|
|
169
|
+
countIn: index + 1,
|
|
170
|
+
countOut,
|
|
171
|
+
})
|
|
172
|
+
|
|
142
173
|
// emit Aggregated error
|
|
143
174
|
cb(
|
|
144
175
|
new AggregateError(
|
|
@@ -148,6 +179,15 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
148
179
|
)
|
|
149
180
|
} else {
|
|
150
181
|
// emit no error
|
|
182
|
+
|
|
183
|
+
onDone?.({
|
|
184
|
+
ok: true,
|
|
185
|
+
collectedErrors,
|
|
186
|
+
countErrors: errors,
|
|
187
|
+
countIn: index + 1,
|
|
188
|
+
countOut,
|
|
189
|
+
})
|
|
190
|
+
|
|
151
191
|
cb()
|
|
152
192
|
}
|
|
153
193
|
},
|