@naturalcycles/nodejs-lib 13.15.0 → 13.17.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/fs/json2env.d.ts +1 -1
- package/dist/fs/json2env.js +3 -2
- package/dist/stream/progressLogger.d.ts +5 -0
- package/dist/stream/progressLogger.js +1 -0
- package/dist/stream/transform/transformMap.d.ts +30 -1
- package/dist/stream/transform/transformMap.js +41 -2
- package/dist/stream/transform/transformMapSync.d.ts +12 -0
- package/dist/stream/transform/transformMapSync.js +29 -2
- package/package.json +1 -1
- package/src/fs/json2env.ts +3 -2
- package/src/stream/progressLogger.ts +8 -0
- package/src/stream/transform/transformMap.ts +79 -0
- package/src/stream/transform/transformMapSync.ts +46 -2
package/dist/fs/json2env.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare function appendToGithubOutput(obj: AnyObject, prefix?: string): v
|
|
|
30
30
|
/**
|
|
31
31
|
* https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary
|
|
32
32
|
*/
|
|
33
|
-
export declare function appendToGithubSummary(
|
|
33
|
+
export declare function appendToGithubSummary(...lines: string[]): void;
|
|
34
34
|
/**
|
|
35
35
|
* Turns Object with keys/values into a *.sh script that exports all keys as values.
|
|
36
36
|
*
|
package/dist/fs/json2env.js
CHANGED
|
@@ -81,10 +81,11 @@ exports.appendToGithubOutput = appendToGithubOutput;
|
|
|
81
81
|
/**
|
|
82
82
|
* https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary
|
|
83
83
|
*/
|
|
84
|
-
function appendToGithubSummary(
|
|
84
|
+
function appendToGithubSummary(...lines) {
|
|
85
85
|
const { GITHUB_STEP_SUMMARY } = process.env;
|
|
86
86
|
if (GITHUB_STEP_SUMMARY) {
|
|
87
|
-
|
|
87
|
+
const str = lines.join('\n') + '\n';
|
|
88
|
+
node_fs_1.default.appendFileSync(GITHUB_STEP_SUMMARY, str);
|
|
88
89
|
console.log(`GITHUB_STEP_SUMMARY appended:\n${str}`);
|
|
89
90
|
}
|
|
90
91
|
}
|
|
@@ -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
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbortableAsyncMapper, AsyncPredicate, CommonLogger, END, ErrorMode, SKIP } from '@naturalcycles/js-lib';
|
|
1
|
+
import { AbortableAsyncMapper, AsyncPredicate, CommonLogger, END, ErrorMode, SKIP, StringMap, UnixTimestampMillisNumber } from '@naturalcycles/js-lib';
|
|
2
2
|
import { TransformTyped } from '../stream.model';
|
|
3
3
|
export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
4
4
|
/**
|
|
@@ -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,20 @@ 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
|
+
started: UnixTimestampMillisNumber;
|
|
65
|
+
}
|
|
41
66
|
/**
|
|
42
67
|
* Like pMap, but for streams.
|
|
43
68
|
* Inspired by `through2`.
|
|
@@ -51,3 +76,7 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
51
76
|
* If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
|
|
52
77
|
*/
|
|
53
78
|
export declare function transformMap<IN = any, OUT = IN>(mapper: AbortableAsyncMapper<IN, OUT | typeof SKIP | typeof END>, opt?: TransformMapOptions<IN, OUT>): TransformTyped<IN, OUT>;
|
|
79
|
+
export declare function appendTransformMapStatsToGithubSummary(stats: TransformMapStats & {
|
|
80
|
+
name?: string;
|
|
81
|
+
extra?: StringMap<any>;
|
|
82
|
+
}): void;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.transformMap = void 0;
|
|
3
|
+
exports.appendTransformMapStatsToGithubSummary = exports.transformMap = void 0;
|
|
4
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const through2Concurrent = require("through2-concurrent");
|
|
6
6
|
const colors_1 = require("../../colors/colors");
|
|
7
|
+
const json2env_1 = require("../../fs/json2env");
|
|
7
8
|
const stream_util_1 = require("../stream.util");
|
|
8
9
|
// doesn't work, cause here we don't construct our Transform instance ourselves
|
|
9
10
|
// export class TransformMap extends AbortableTransform {}
|
|
@@ -21,8 +22,10 @@ const stream_util_1 = require("../stream.util");
|
|
|
21
22
|
*/
|
|
22
23
|
function transformMap(mapper, opt = {}) {
|
|
23
24
|
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;
|
|
25
|
+
errorMode = js_lib_1.ErrorMode.THROW_IMMEDIATELY, flattenArrayOutput, onError, onDone, metric = 'stream', logger = console, } = opt;
|
|
26
|
+
const started = Date.now();
|
|
25
27
|
let index = -1;
|
|
28
|
+
let countOut = 0;
|
|
26
29
|
let isSettled = false;
|
|
27
30
|
let errors = 0;
|
|
28
31
|
const collectedErrors = []; // only used if errorMode == THROW_AGGREGATED
|
|
@@ -32,11 +35,27 @@ function transformMap(mapper, opt = {}) {
|
|
|
32
35
|
// console.log('transformMap final')
|
|
33
36
|
logErrorStats(true);
|
|
34
37
|
if (collectedErrors.length) {
|
|
38
|
+
onDone?.({
|
|
39
|
+
ok: false,
|
|
40
|
+
collectedErrors,
|
|
41
|
+
countErrors: errors,
|
|
42
|
+
countIn: index + 1,
|
|
43
|
+
countOut,
|
|
44
|
+
started,
|
|
45
|
+
});
|
|
35
46
|
// emit Aggregated error
|
|
36
47
|
cb(new AggregateError(collectedErrors, `transformMap resulted in ${collectedErrors.length} error(s)`));
|
|
37
48
|
}
|
|
38
49
|
else {
|
|
39
50
|
// emit no error
|
|
51
|
+
onDone?.({
|
|
52
|
+
ok: true,
|
|
53
|
+
collectedErrors,
|
|
54
|
+
countErrors: errors,
|
|
55
|
+
countIn: index + 1,
|
|
56
|
+
countOut,
|
|
57
|
+
started,
|
|
58
|
+
});
|
|
40
59
|
cb();
|
|
41
60
|
}
|
|
42
61
|
},
|
|
@@ -54,6 +73,7 @@ function transformMap(mapper, opt = {}) {
|
|
|
54
73
|
}
|
|
55
74
|
return r !== js_lib_1.SKIP && (!predicate || (await predicate(r, currentIndex)));
|
|
56
75
|
});
|
|
76
|
+
countOut += passedResults.length;
|
|
57
77
|
passedResults.forEach(r => this.push(r));
|
|
58
78
|
if (isSettled) {
|
|
59
79
|
logger.log(`transformMap END received at index ${currentIndex}`);
|
|
@@ -73,6 +93,14 @@ function transformMap(mapper, opt = {}) {
|
|
|
73
93
|
}
|
|
74
94
|
if (errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY) {
|
|
75
95
|
isSettled = true;
|
|
96
|
+
onDone?.({
|
|
97
|
+
ok: false,
|
|
98
|
+
collectedErrors,
|
|
99
|
+
countErrors: errors,
|
|
100
|
+
countIn: index + 1,
|
|
101
|
+
countOut,
|
|
102
|
+
started,
|
|
103
|
+
});
|
|
76
104
|
return cb(err); // Emit error immediately
|
|
77
105
|
}
|
|
78
106
|
if (errorMode === js_lib_1.ErrorMode.THROW_AGGREGATED) {
|
|
@@ -89,3 +117,14 @@ function transformMap(mapper, opt = {}) {
|
|
|
89
117
|
}
|
|
90
118
|
}
|
|
91
119
|
exports.transformMap = transformMap;
|
|
120
|
+
function appendTransformMapStatsToGithubSummary(stats) {
|
|
121
|
+
const { countIn, countOut, countErrors, started, name = 'Transform', extra = {} } = stats;
|
|
122
|
+
(0, json2env_1.appendToGithubSummary)(...[
|
|
123
|
+
`### ${name} summary\n`,
|
|
124
|
+
`${(0, js_lib_1._since)(started)} spent`,
|
|
125
|
+
`${(0, js_lib_1._hc)(countIn)} / ${(0, js_lib_1._hc)(countOut)} rows in / out`,
|
|
126
|
+
countErrors ? `${countErrors} errors` : '',
|
|
127
|
+
...Object.entries(extra).map(([k, v]) => `${k}: ${v}`),
|
|
128
|
+
].filter(Boolean));
|
|
129
|
+
}
|
|
130
|
+
exports.appendTransformMapStatsToGithubSummary = appendTransformMapStatsToGithubSummary;
|
|
@@ -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,11 @@ 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
|
+
const started = Date.now();
|
|
19
|
+
let index = -1;
|
|
20
|
+
let countOut = 0;
|
|
19
21
|
let isSettled = false;
|
|
20
22
|
let errors = 0;
|
|
21
23
|
const collectedErrors = []; // only used if errorMode == THROW_AGGREGATED
|
|
@@ -37,6 +39,7 @@ function transformMapSync(mapper, opt = {}) {
|
|
|
37
39
|
}
|
|
38
40
|
return r !== js_lib_1.SKIP && (!predicate || predicate(r, currentIndex));
|
|
39
41
|
});
|
|
42
|
+
countOut += passedResults.length;
|
|
40
43
|
passedResults.forEach(r => this.push(r));
|
|
41
44
|
if (isSettled) {
|
|
42
45
|
logger.log(`transformMapSync END received at index ${currentIndex}`);
|
|
@@ -56,6 +59,14 @@ function transformMapSync(mapper, opt = {}) {
|
|
|
56
59
|
}
|
|
57
60
|
if (errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY) {
|
|
58
61
|
isSettled = true;
|
|
62
|
+
onDone?.({
|
|
63
|
+
ok: false,
|
|
64
|
+
collectedErrors,
|
|
65
|
+
countErrors: errors,
|
|
66
|
+
countIn: index + 1,
|
|
67
|
+
countOut,
|
|
68
|
+
started,
|
|
69
|
+
});
|
|
59
70
|
// Emit error immediately
|
|
60
71
|
return cb(err);
|
|
61
72
|
}
|
|
@@ -69,11 +80,27 @@ function transformMapSync(mapper, opt = {}) {
|
|
|
69
80
|
// console.log('transformMap final')
|
|
70
81
|
logErrorStats(true);
|
|
71
82
|
if (collectedErrors.length) {
|
|
83
|
+
onDone?.({
|
|
84
|
+
ok: false,
|
|
85
|
+
collectedErrors,
|
|
86
|
+
countErrors: errors,
|
|
87
|
+
countIn: index + 1,
|
|
88
|
+
countOut,
|
|
89
|
+
started,
|
|
90
|
+
});
|
|
72
91
|
// emit Aggregated error
|
|
73
92
|
cb(new AggregateError(collectedErrors, `transformMapSync resulted in ${collectedErrors.length} error(s)`));
|
|
74
93
|
}
|
|
75
94
|
else {
|
|
76
95
|
// emit no error
|
|
96
|
+
onDone?.({
|
|
97
|
+
ok: true,
|
|
98
|
+
collectedErrors,
|
|
99
|
+
countErrors: errors,
|
|
100
|
+
countIn: index + 1,
|
|
101
|
+
countOut,
|
|
102
|
+
started,
|
|
103
|
+
});
|
|
77
104
|
cb();
|
|
78
105
|
}
|
|
79
106
|
},
|
package/package.json
CHANGED
package/src/fs/json2env.ts
CHANGED
|
@@ -120,10 +120,11 @@ export function appendToGithubOutput(obj: AnyObject, prefix = ''): void {
|
|
|
120
120
|
/**
|
|
121
121
|
* https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary
|
|
122
122
|
*/
|
|
123
|
-
export function appendToGithubSummary(
|
|
123
|
+
export function appendToGithubSummary(...lines: string[]): void {
|
|
124
124
|
const { GITHUB_STEP_SUMMARY } = process.env
|
|
125
125
|
if (GITHUB_STEP_SUMMARY) {
|
|
126
|
-
|
|
126
|
+
const str = lines.join('\n') + '\n'
|
|
127
|
+
fs.appendFileSync(GITHUB_STEP_SUMMARY, str)
|
|
127
128
|
console.log(`GITHUB_STEP_SUMMARY appended:\n${str}`)
|
|
128
129
|
}
|
|
129
130
|
}
|
|
@@ -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
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
_anyToError,
|
|
3
|
+
_hc,
|
|
4
|
+
_since,
|
|
3
5
|
AbortableAsyncMapper,
|
|
4
6
|
AsyncPredicate,
|
|
5
7
|
CommonLogger,
|
|
@@ -7,9 +9,12 @@ import {
|
|
|
7
9
|
ErrorMode,
|
|
8
10
|
pFilter,
|
|
9
11
|
SKIP,
|
|
12
|
+
StringMap,
|
|
13
|
+
UnixTimestampMillisNumber,
|
|
10
14
|
} from '@naturalcycles/js-lib'
|
|
11
15
|
import through2Concurrent = require('through2-concurrent')
|
|
12
16
|
import { yellow } from '../../colors/colors'
|
|
17
|
+
import { appendToGithubSummary } from '../../fs/json2env'
|
|
13
18
|
import { AbortableTransform } from '../pipeline/pipeline'
|
|
14
19
|
import { TransformTyped } from '../stream.model'
|
|
15
20
|
import { pipelineClose } from '../stream.util'
|
|
@@ -49,6 +54,18 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
49
54
|
*/
|
|
50
55
|
onError?: (err: Error, input: IN) => any
|
|
51
56
|
|
|
57
|
+
/**
|
|
58
|
+
* A hook that is called when the last item is finished processing.
|
|
59
|
+
* stats object is passed, containing countIn and countOut -
|
|
60
|
+
* number of items that entered the transform and number of items that left it.
|
|
61
|
+
*
|
|
62
|
+
* Callback is called **before** [possible] Aggregated error is thrown,
|
|
63
|
+
* and before [possible] THROW_IMMEDIATELY error.
|
|
64
|
+
*
|
|
65
|
+
* onDone callback will be called before Error is thrown.
|
|
66
|
+
*/
|
|
67
|
+
onDone?: (stats: TransformMapStats) => any
|
|
68
|
+
|
|
52
69
|
/**
|
|
53
70
|
* Progress metric
|
|
54
71
|
*
|
|
@@ -59,6 +76,21 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
59
76
|
logger?: CommonLogger
|
|
60
77
|
}
|
|
61
78
|
|
|
79
|
+
export interface TransformMapStats {
|
|
80
|
+
/**
|
|
81
|
+
* True if transform was successful (didn't throw Immediate or Aggregated error).
|
|
82
|
+
*/
|
|
83
|
+
ok: boolean
|
|
84
|
+
/**
|
|
85
|
+
* Only used (and returned) for ErrorMode.Aggregated
|
|
86
|
+
*/
|
|
87
|
+
collectedErrors: Error[]
|
|
88
|
+
countErrors: number
|
|
89
|
+
countIn: number
|
|
90
|
+
countOut: number
|
|
91
|
+
started: UnixTimestampMillisNumber
|
|
92
|
+
}
|
|
93
|
+
|
|
62
94
|
// doesn't work, cause here we don't construct our Transform instance ourselves
|
|
63
95
|
// export class TransformMap extends AbortableTransform {}
|
|
64
96
|
|
|
@@ -84,11 +116,14 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
84
116
|
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
85
117
|
flattenArrayOutput,
|
|
86
118
|
onError,
|
|
119
|
+
onDone,
|
|
87
120
|
metric = 'stream',
|
|
88
121
|
logger = console,
|
|
89
122
|
} = opt
|
|
90
123
|
|
|
124
|
+
const started = Date.now()
|
|
91
125
|
let index = -1
|
|
126
|
+
let countOut = 0
|
|
92
127
|
let isSettled = false
|
|
93
128
|
let errors = 0
|
|
94
129
|
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
@@ -102,6 +137,15 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
102
137
|
logErrorStats(true)
|
|
103
138
|
|
|
104
139
|
if (collectedErrors.length) {
|
|
140
|
+
onDone?.({
|
|
141
|
+
ok: false,
|
|
142
|
+
collectedErrors,
|
|
143
|
+
countErrors: errors,
|
|
144
|
+
countIn: index + 1,
|
|
145
|
+
countOut,
|
|
146
|
+
started,
|
|
147
|
+
})
|
|
148
|
+
|
|
105
149
|
// emit Aggregated error
|
|
106
150
|
cb(
|
|
107
151
|
new AggregateError(
|
|
@@ -111,6 +155,16 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
111
155
|
)
|
|
112
156
|
} else {
|
|
113
157
|
// emit no error
|
|
158
|
+
|
|
159
|
+
onDone?.({
|
|
160
|
+
ok: true,
|
|
161
|
+
collectedErrors,
|
|
162
|
+
countErrors: errors,
|
|
163
|
+
countIn: index + 1,
|
|
164
|
+
countOut,
|
|
165
|
+
started,
|
|
166
|
+
})
|
|
167
|
+
|
|
114
168
|
cb()
|
|
115
169
|
}
|
|
116
170
|
},
|
|
@@ -134,6 +188,7 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
134
188
|
},
|
|
135
189
|
)
|
|
136
190
|
|
|
191
|
+
countOut += passedResults.length
|
|
137
192
|
passedResults.forEach(r => this.push(r))
|
|
138
193
|
|
|
139
194
|
if (isSettled) {
|
|
@@ -155,6 +210,14 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
155
210
|
|
|
156
211
|
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
157
212
|
isSettled = true
|
|
213
|
+
onDone?.({
|
|
214
|
+
ok: false,
|
|
215
|
+
collectedErrors,
|
|
216
|
+
countErrors: errors,
|
|
217
|
+
countIn: index + 1,
|
|
218
|
+
countOut,
|
|
219
|
+
started,
|
|
220
|
+
})
|
|
158
221
|
return cb(err) // Emit error immediately
|
|
159
222
|
}
|
|
160
223
|
|
|
@@ -173,3 +236,19 @@ export function transformMap<IN = any, OUT = IN>(
|
|
|
173
236
|
logger.log(`${metric} ${final ? 'final ' : ''}errors: ${yellow(errors)}`)
|
|
174
237
|
}
|
|
175
238
|
}
|
|
239
|
+
|
|
240
|
+
export function appendTransformMapStatsToGithubSummary(
|
|
241
|
+
stats: TransformMapStats & { name?: string; extra?: StringMap<any> },
|
|
242
|
+
): void {
|
|
243
|
+
const { countIn, countOut, countErrors, started, name = 'Transform', extra = {} } = stats
|
|
244
|
+
|
|
245
|
+
appendToGithubSummary(
|
|
246
|
+
...[
|
|
247
|
+
`### ${name} summary\n`,
|
|
248
|
+
`${_since(started)} spent`,
|
|
249
|
+
`${_hc(countIn)} / ${_hc(countOut)} rows in / out`,
|
|
250
|
+
countErrors ? `${countErrors} errors` : '',
|
|
251
|
+
...Object.entries(extra).map(([k, v]) => `${k}: ${v}`),
|
|
252
|
+
].filter(Boolean),
|
|
253
|
+
)
|
|
254
|
+
}
|
|
@@ -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,20 @@ 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
|
+
const started = Date.now()
|
|
92
|
+
let index = -1
|
|
93
|
+
let countOut = 0
|
|
78
94
|
let isSettled = false
|
|
79
95
|
let errors = 0
|
|
80
96
|
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
@@ -100,6 +116,7 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
100
116
|
return r !== SKIP && (!predicate || predicate(r, currentIndex))
|
|
101
117
|
})
|
|
102
118
|
|
|
119
|
+
countOut += passedResults.length
|
|
103
120
|
passedResults.forEach(r => this.push(r))
|
|
104
121
|
|
|
105
122
|
if (isSettled) {
|
|
@@ -122,6 +139,14 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
122
139
|
|
|
123
140
|
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
124
141
|
isSettled = true
|
|
142
|
+
onDone?.({
|
|
143
|
+
ok: false,
|
|
144
|
+
collectedErrors,
|
|
145
|
+
countErrors: errors,
|
|
146
|
+
countIn: index + 1,
|
|
147
|
+
countOut,
|
|
148
|
+
started,
|
|
149
|
+
})
|
|
125
150
|
// Emit error immediately
|
|
126
151
|
return cb(err as Error)
|
|
127
152
|
}
|
|
@@ -139,6 +164,15 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
139
164
|
logErrorStats(true)
|
|
140
165
|
|
|
141
166
|
if (collectedErrors.length) {
|
|
167
|
+
onDone?.({
|
|
168
|
+
ok: false,
|
|
169
|
+
collectedErrors,
|
|
170
|
+
countErrors: errors,
|
|
171
|
+
countIn: index + 1,
|
|
172
|
+
countOut,
|
|
173
|
+
started,
|
|
174
|
+
})
|
|
175
|
+
|
|
142
176
|
// emit Aggregated error
|
|
143
177
|
cb(
|
|
144
178
|
new AggregateError(
|
|
@@ -148,6 +182,16 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
148
182
|
)
|
|
149
183
|
} else {
|
|
150
184
|
// emit no error
|
|
185
|
+
|
|
186
|
+
onDone?.({
|
|
187
|
+
ok: true,
|
|
188
|
+
collectedErrors,
|
|
189
|
+
countErrors: errors,
|
|
190
|
+
countIn: index + 1,
|
|
191
|
+
countOut,
|
|
192
|
+
started,
|
|
193
|
+
})
|
|
194
|
+
|
|
151
195
|
cb()
|
|
152
196
|
}
|
|
153
197
|
},
|