@naturalcycles/nodejs-lib 12.56.1 → 12.60.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/index.d.ts +19 -18
- package/dist/index.js +19 -92
- package/dist/log/log.util.d.ts +4 -0
- package/dist/log/log.util.js +11 -0
- package/dist/stream/ndjson/ndjsonMap.d.ts +2 -2
- package/dist/stream/ndjson/ndjsonMap.js +4 -3
- package/dist/stream/ndjson/ndjsonStreamForEach.d.ts +2 -2
- package/dist/stream/ndjson/transformJsonParse.js +3 -3
- package/dist/stream/ndjson/transformToNDJson.js +2 -2
- package/dist/stream/pipeline/pipeline.d.ts +25 -3
- package/dist/stream/pipeline/pipeline.js +76 -9
- package/dist/stream/readable/readableCreate.d.ts +8 -0
- package/dist/stream/readable/readableCreate.js +9 -1
- package/dist/stream/readable/readableForEach.d.ts +2 -2
- package/dist/stream/readable/readableFromArray.d.ts +2 -2
- package/dist/stream/readable/readableFromArray.js +17 -13
- package/dist/stream/readable/readableMap.d.ts +2 -2
- package/dist/stream/readable/readableMap.js +22 -17
- package/dist/stream/sizeStack.d.ts +9 -0
- package/dist/stream/sizeStack.js +48 -0
- package/dist/stream/stream.util.d.ts +4 -0
- package/dist/stream/stream.util.js +24 -0
- package/dist/stream/transform/transformBuffer.js +1 -1
- package/dist/stream/transform/transformFilter.d.ts +3 -4
- package/dist/stream/transform/transformFilter.js +5 -20
- package/dist/stream/transform/transformLimit.d.ts +36 -1
- package/dist/stream/transform/transformLimit.js +33 -15
- package/dist/stream/transform/transformLogProgress.d.ts +22 -1
- package/dist/stream/transform/transformLogProgress.js +38 -20
- package/dist/stream/transform/transformMap.d.ts +4 -10
- package/dist/stream/transform/transformMap.js +52 -64
- package/dist/stream/transform/transformMapSimple.d.ts +2 -1
- package/dist/stream/transform/transformMapSimple.js +3 -3
- package/dist/stream/transform/transformMapSync.d.ts +7 -4
- package/dist/stream/transform/transformMapSync.js +30 -24
- package/dist/stream/transform/transformNoOp.js +1 -1
- package/dist/stream/transform/transformTap.d.ts +5 -2
- package/dist/stream/transform/transformTap.js +5 -4
- package/dist/stream/transform/transformToArray.js +1 -1
- package/dist/stream/transform/transformToString.js +2 -2
- package/dist/stream/transform/worker/transformMultiThreaded.js +1 -1
- package/dist/stream/transform/worker/workerClassProxy.js +1 -0
- package/dist/stream/writable/writableFork.d.ts +2 -0
- package/dist/stream/writable/writableFork.js +3 -1
- package/dist/stream/writable/writableLimit.d.ts +9 -0
- package/dist/stream/writable/writableLimit.js +29 -0
- package/dist/stream/writable/writablePushToArray.js +1 -1
- package/dist/stream/writable/writableVoid.d.ts +8 -1
- package/dist/stream/writable/writableVoid.js +6 -2
- package/dist/util/zip.util.d.ts +10 -2
- package/dist/util/zip.util.js +10 -3
- package/package.json +1 -1
- package/src/index.ts +17 -156
- package/src/log/log.util.ts +9 -0
- package/src/stream/ndjson/ndjsonMap.ts +7 -5
- package/src/stream/ndjson/ndjsonStreamForEach.ts +2 -2
- package/src/stream/ndjson/transformJsonParse.ts +3 -3
- package/src/stream/ndjson/transformToNDJson.ts +2 -2
- package/src/stream/pipeline/pipeline.ts +102 -9
- package/src/stream/readable/readableCreate.ts +9 -1
- package/src/stream/readable/readableForEach.ts +2 -2
- package/src/stream/readable/readableFromArray.ts +18 -21
- package/src/stream/readable/readableMap.ts +24 -21
- package/src/stream/sizeStack.ts +56 -0
- package/src/stream/stream.util.ts +29 -0
- package/src/stream/transform/transformBuffer.ts +1 -1
- package/src/stream/transform/transformFilter.ts +6 -20
- package/src/stream/transform/transformLimit.ts +71 -19
- package/src/stream/transform/transformLogProgress.ts +78 -26
- package/src/stream/transform/transformMap.ts +74 -94
- package/src/stream/transform/transformMapSimple.ts +6 -4
- package/src/stream/transform/transformMapSync.ts +45 -28
- package/src/stream/transform/transformNoOp.ts +1 -1
- package/src/stream/transform/transformTap.ts +11 -6
- package/src/stream/transform/transformToArray.ts +1 -1
- package/src/stream/transform/transformToString.ts +2 -2
- package/src/stream/transform/worker/transformMultiThreaded.ts +1 -1
- package/src/stream/transform/worker/workerClassProxy.js +1 -0
- package/src/stream/writable/writableFork.ts +3 -1
- package/src/stream/writable/writableLimit.ts +28 -0
- package/src/stream/writable/writablePushToArray.ts +1 -1
- package/src/stream/writable/writableVoid.ts +14 -2
- package/src/util/zip.util.ts +11 -3
- package/dist/stream/transform/legacy/transformMap.d.ts +0 -17
- package/dist/stream/transform/legacy/transformMap.js +0 -94
- package/src/stream/transform/legacy/transformMap.ts +0 -133
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.readableMap = void 0;
|
|
4
|
-
const
|
|
4
|
+
const stream_1 = require("stream");
|
|
5
|
+
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
6
|
function readableMap(readable, mapper) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
let i = -1;
|
|
8
|
+
// todo: check if we need to handle errors somehow specifically
|
|
9
|
+
return readable.pipe(new stream_1.Transform({
|
|
10
|
+
objectMode: true,
|
|
11
|
+
async transform(chunk, _enc, cb) {
|
|
12
|
+
try {
|
|
13
|
+
const r = await mapper(chunk, ++i);
|
|
14
|
+
if (r === js_lib_1.SKIP) {
|
|
15
|
+
cb();
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// _assert(r !== END, `readableMap END not supported`)
|
|
19
|
+
cb(null, r);
|
|
20
|
+
}
|
|
13
21
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
})();
|
|
22
|
-
return out;
|
|
22
|
+
catch (err) {
|
|
23
|
+
console.error(err);
|
|
24
|
+
cb(err);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
23
28
|
}
|
|
24
29
|
exports.readableMap = readableMap;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CommonLogger, NumberStack } from '@naturalcycles/js-lib';
|
|
2
|
+
export declare class SizeStack extends NumberStack {
|
|
3
|
+
name: string;
|
|
4
|
+
constructor(name: string, size: number);
|
|
5
|
+
total: number;
|
|
6
|
+
push(item: any): this;
|
|
7
|
+
getStats(): string;
|
|
8
|
+
static countItem(item: any, logger: CommonLogger, sizes?: SizeStack, sizesZipped?: SizeStack): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SizeStack = void 0;
|
|
4
|
+
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
|
+
const colors_1 = require("../colors");
|
|
6
|
+
const zip_util_1 = require("../util/zip.util");
|
|
7
|
+
class SizeStack extends js_lib_1.NumberStack {
|
|
8
|
+
constructor(name, size) {
|
|
9
|
+
super(size);
|
|
10
|
+
this.name = name;
|
|
11
|
+
this.total = 0;
|
|
12
|
+
}
|
|
13
|
+
push(item) {
|
|
14
|
+
this.total += item;
|
|
15
|
+
return super.push(item);
|
|
16
|
+
}
|
|
17
|
+
getStats() {
|
|
18
|
+
const pcs = this.percentiles([50, 90]);
|
|
19
|
+
return [
|
|
20
|
+
this.name,
|
|
21
|
+
'avg',
|
|
22
|
+
(0, colors_1.yellow)((0, js_lib_1._hb)(this.avg())),
|
|
23
|
+
'p50',
|
|
24
|
+
(0, colors_1.yellow)((0, js_lib_1._hb)(pcs[50])),
|
|
25
|
+
'p90',
|
|
26
|
+
(0, colors_1.yellow)((0, js_lib_1._hb)(pcs[90])),
|
|
27
|
+
'total',
|
|
28
|
+
(0, colors_1.yellow)((0, js_lib_1._hb)(this.total)),
|
|
29
|
+
].join(' ');
|
|
30
|
+
}
|
|
31
|
+
static async countItem(item, logger, sizes, sizesZipped) {
|
|
32
|
+
if (!sizes)
|
|
33
|
+
return;
|
|
34
|
+
// try-catch, because we don't want to fail the pipeline on logProgress
|
|
35
|
+
try {
|
|
36
|
+
const buf = Buffer.from(JSON.stringify(item));
|
|
37
|
+
sizes.push(buf.byteLength);
|
|
38
|
+
if (sizesZipped) {
|
|
39
|
+
const { byteLength } = await (0, zip_util_1.gzipBuffer)(buf);
|
|
40
|
+
sizesZipped.push(byteLength);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
logger.warn(`transformLogProgress failed to JSON.stringify the chunk: ${err.message}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.SizeStack = SizeStack;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import { CommonLogger } from '@naturalcycles/js-lib';
|
|
4
|
+
export declare function pipelineClose(name: string, readableDownstream: Readable, sourceReadable: Readable | undefined, streamDone: Promise<void> | undefined, logger: CommonLogger): void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pipelineClose = void 0;
|
|
4
|
+
function pipelineClose(name, readableDownstream, sourceReadable, streamDone, logger) {
|
|
5
|
+
readableDownstream.push(null); // this closes the stream, so downstream Readable will receive `end` and won't write anything
|
|
6
|
+
if (!sourceReadable) {
|
|
7
|
+
logger.warn(`${name} sourceReadable is not provided, readable stream will not be stopped`);
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
logger.log(`${name} is calling readable.unpipe() to pause the stream`);
|
|
11
|
+
sourceReadable.unpipe(); // it is expected to pause the stream
|
|
12
|
+
if (!streamDone) {
|
|
13
|
+
logger.log(`${name} streamDone is not provided, will do readable.destroy right away`);
|
|
14
|
+
sourceReadable.destroy();
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
void streamDone.then(() => {
|
|
18
|
+
logger.log(`${name} streamDone, calling readable.destroy()`);
|
|
19
|
+
sourceReadable.destroy(); // this throws ERR_STREAM_PREMATURE_CLOSE
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.pipelineClose = pipelineClose;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { AsyncPredicate, Predicate } from '@naturalcycles/js-lib';
|
|
2
2
|
import { TransformOptions, TransformTyped } from '../stream.model';
|
|
3
|
+
import { TransformMapOptions } from './transformMap';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
* So, it's recommended to use transformMap instead, that is both concurrent and has
|
|
6
|
-
* filtering feature by default.
|
|
5
|
+
* Just a convenience wrapper around `transformMap` that has built-in predicate filtering support.
|
|
7
6
|
*/
|
|
8
|
-
export declare function transformFilter<IN = any>(predicate: AsyncPredicate<IN>, opt?:
|
|
7
|
+
export declare function transformFilter<IN = any>(predicate: AsyncPredicate<IN>, opt?: TransformMapOptions): TransformTyped<IN, IN>;
|
|
9
8
|
/**
|
|
10
9
|
* Sync version of `transformFilter`
|
|
11
10
|
*/
|
|
@@ -2,29 +2,14 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.transformFilterSync = exports.transformFilter = void 0;
|
|
4
4
|
const stream_1 = require("stream");
|
|
5
|
+
const transformMap_1 = require("./transformMap");
|
|
5
6
|
/**
|
|
6
|
-
*
|
|
7
|
-
* So, it's recommended to use transformMap instead, that is both concurrent and has
|
|
8
|
-
* filtering feature by default.
|
|
7
|
+
* Just a convenience wrapper around `transformMap` that has built-in predicate filtering support.
|
|
9
8
|
*/
|
|
10
9
|
function transformFilter(predicate, opt = {}) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
objectMode: true,
|
|
10
|
+
return (0, transformMap_1.transformMap)(v => v, {
|
|
11
|
+
predicate,
|
|
14
12
|
...opt,
|
|
15
|
-
async transform(chunk, _encoding, cb) {
|
|
16
|
-
try {
|
|
17
|
-
if (await predicate(chunk, index++)) {
|
|
18
|
-
cb(null, chunk); // pass through
|
|
19
|
-
}
|
|
20
|
-
else {
|
|
21
|
-
cb(); // signal that we've finished processing, but emit no output here
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
catch (err) {
|
|
25
|
-
cb(err);
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
13
|
});
|
|
29
14
|
}
|
|
30
15
|
exports.transformFilter = transformFilter;
|
|
@@ -36,7 +21,7 @@ function transformFilterSync(predicate, opt = {}) {
|
|
|
36
21
|
return new stream_1.Transform({
|
|
37
22
|
objectMode: true,
|
|
38
23
|
...opt,
|
|
39
|
-
|
|
24
|
+
transform(chunk, _, cb) {
|
|
40
25
|
try {
|
|
41
26
|
if (predicate(chunk, index++)) {
|
|
42
27
|
cb(null, chunk); // pass through
|
|
@@ -1,5 +1,40 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Readable } from 'stream';
|
|
3
|
+
import { CommonLogger } from '@naturalcycles/js-lib';
|
|
4
|
+
import { AbortableTransform } from '../../index';
|
|
1
5
|
import { TransformOptions, TransformTyped } from '../stream.model';
|
|
6
|
+
export interface TransformLimitOptions extends TransformOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Nullish value (e.g 0 or undefined) would mean "no limit"
|
|
9
|
+
*/
|
|
10
|
+
limit?: number;
|
|
11
|
+
/**
|
|
12
|
+
* If provided (recommended!) - it will call readable.destroy() on limit.
|
|
13
|
+
* Without it - it will only stop the downstream consumers, but won't stop
|
|
14
|
+
* the Readable ("source" of the stream).
|
|
15
|
+
* It is almost always crucial to stop the Source too, so, please provide the Readable here!
|
|
16
|
+
*/
|
|
17
|
+
sourceReadable?: Readable;
|
|
18
|
+
/**
|
|
19
|
+
* Please provide it (a Promise that resolves when the Stream is done, e.g finished consuming things)
|
|
20
|
+
* to be able to wait for Consumers before calling `readable.destroy`.
|
|
21
|
+
* Has no effect if `readable` is not provided.
|
|
22
|
+
*/
|
|
23
|
+
streamDone?: Promise<void>;
|
|
24
|
+
logger?: CommonLogger;
|
|
25
|
+
/**
|
|
26
|
+
* Set to true to enable additional debug messages, e.g it'll log
|
|
27
|
+
* when readable still emits values after the limit is reached.
|
|
28
|
+
*/
|
|
29
|
+
debug?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Class only exists to be able to do `instanceof TransformLimit`
|
|
33
|
+
* and to set sourceReadable+streamDone to it in `_pipeline`.
|
|
34
|
+
*/
|
|
35
|
+
export declare class TransformLimit extends AbortableTransform {
|
|
36
|
+
}
|
|
2
37
|
/**
|
|
3
38
|
* 0 or falsy value means "no limit"
|
|
4
39
|
*/
|
|
5
|
-
export declare function transformLimit<IN>(
|
|
40
|
+
export declare function transformLimit<IN>(opt?: TransformLimitOptions): TransformTyped<IN, IN>;
|
|
@@ -1,28 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.transformLimit = void 0;
|
|
4
|
-
const
|
|
3
|
+
exports.transformLimit = exports.TransformLimit = void 0;
|
|
4
|
+
const index_1 = require("../../index");
|
|
5
|
+
const stream_util_1 = require("../stream.util");
|
|
6
|
+
/**
|
|
7
|
+
* Class only exists to be able to do `instanceof TransformLimit`
|
|
8
|
+
* and to set sourceReadable+streamDone to it in `_pipeline`.
|
|
9
|
+
*/
|
|
10
|
+
class TransformLimit extends index_1.AbortableTransform {
|
|
11
|
+
}
|
|
12
|
+
exports.TransformLimit = TransformLimit;
|
|
5
13
|
/**
|
|
6
14
|
* 0 or falsy value means "no limit"
|
|
7
15
|
*/
|
|
8
|
-
function transformLimit(
|
|
9
|
-
|
|
16
|
+
function transformLimit(opt = {}) {
|
|
17
|
+
const { logger = console, limit, debug } = opt;
|
|
18
|
+
if (!limit) {
|
|
19
|
+
// No limit - returning pass-through transform
|
|
20
|
+
return (0, index_1.transformNoOp)();
|
|
21
|
+
}
|
|
22
|
+
let i = 0; // so we start first chunk with 1
|
|
10
23
|
let ended = false;
|
|
11
|
-
return new
|
|
24
|
+
return new TransformLimit({
|
|
12
25
|
objectMode: true,
|
|
13
26
|
...opt,
|
|
14
|
-
transform(chunk,
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
|
|
27
|
+
transform(chunk, _, cb) {
|
|
28
|
+
i++;
|
|
29
|
+
if (i === limit) {
|
|
30
|
+
ended = true;
|
|
31
|
+
logger.log(`transformLimit of ${limit} reached`);
|
|
32
|
+
this.push(chunk);
|
|
33
|
+
(0, stream_util_1.pipelineClose)('transformLimit', this, opt.sourceReadable || this.sourceReadable, opt.streamDone || this.streamDone, logger);
|
|
34
|
+
cb(); // after pause
|
|
18
35
|
}
|
|
19
|
-
else {
|
|
20
|
-
cb(null);
|
|
36
|
+
else if (!ended) {
|
|
37
|
+
cb(null, chunk);
|
|
21
38
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//
|
|
39
|
+
else {
|
|
40
|
+
if (debug)
|
|
41
|
+
logger.log(`transformLimit.transform after limit`, i);
|
|
42
|
+
// If we ever HANG (don't call cb) - Node will do process.exit(0) to us
|
|
43
|
+
cb(); // ended, don't emit anything
|
|
26
44
|
}
|
|
27
45
|
},
|
|
28
46
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnyObject } from '@naturalcycles/js-lib';
|
|
1
|
+
import { AnyObject, CommonLogger } from '@naturalcycles/js-lib';
|
|
2
2
|
import { TransformOptions, TransformTyped } from '../stream.model';
|
|
3
3
|
export interface TransformLogProgressOptions<IN = any> extends TransformOptions {
|
|
4
4
|
/**
|
|
@@ -69,6 +69,7 @@ export interface TransformLogProgressOptions<IN = any> extends TransformOptions
|
|
|
69
69
|
* @default 1000
|
|
70
70
|
*/
|
|
71
71
|
logEvery?: number;
|
|
72
|
+
logger?: CommonLogger;
|
|
72
73
|
/**
|
|
73
74
|
* Function to return extra properties to the "progress object".
|
|
74
75
|
*
|
|
@@ -83,6 +84,26 @@ export interface TransformLogProgressOptions<IN = any> extends TransformOptions
|
|
|
83
84
|
* Defaults to 1.
|
|
84
85
|
*/
|
|
85
86
|
batchSize?: number;
|
|
87
|
+
/**
|
|
88
|
+
* Experimental logging of item (shunk) sizes, when json-stringified.
|
|
89
|
+
*
|
|
90
|
+
* Defaults to false.
|
|
91
|
+
*
|
|
92
|
+
* @experimental
|
|
93
|
+
*/
|
|
94
|
+
logSizes?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* How many last item sizes to keep in a buffer, to calculate stats (p50, p90, avg, etc).
|
|
97
|
+
* Defaults to 100_000.
|
|
98
|
+
* Cannot be Infinity.
|
|
99
|
+
*/
|
|
100
|
+
logSizesBuffer?: number;
|
|
101
|
+
/**
|
|
102
|
+
* Works in addition to `logSizes`. Adds "zipped sizes".
|
|
103
|
+
*
|
|
104
|
+
* @experimental
|
|
105
|
+
*/
|
|
106
|
+
logZippedSizes?: boolean;
|
|
86
107
|
}
|
|
87
108
|
/**
|
|
88
109
|
* Pass-through transform that optionally logs progress.
|
|
@@ -7,6 +7,7 @@ const js_lib_1 = require("@naturalcycles/js-lib");
|
|
|
7
7
|
const time_lib_1 = require("@naturalcycles/time-lib");
|
|
8
8
|
const colors_1 = require("../../colors");
|
|
9
9
|
const colors_2 = require("../../colors/colors");
|
|
10
|
+
const sizeStack_1 = require("../sizeStack");
|
|
10
11
|
const inspectOpt = {
|
|
11
12
|
colors: colors_2.hasColors,
|
|
12
13
|
breakLength: 300,
|
|
@@ -15,7 +16,7 @@ const inspectOpt = {
|
|
|
15
16
|
* Pass-through transform that optionally logs progress.
|
|
16
17
|
*/
|
|
17
18
|
function transformLogProgress(opt = {}) {
|
|
18
|
-
const { metric = 'progress', heapTotal: logHeapTotal = false, heapUsed: logHeapUsed = false, rss: logRss = true, peakRSS: logPeakRSS = true, logRPS = true, logEvery = 1000, batchSize = 1, extra, } = opt;
|
|
19
|
+
const { metric = 'progress', heapTotal: logHeapTotal = false, heapUsed: logHeapUsed = false, rss: logRss = true, peakRSS: logPeakRSS = true, logRPS = true, logEvery = 1000, logSizes = false, logSizesBuffer = 100000, logZippedSizes = false, batchSize = 1, extra, logger = console, } = opt;
|
|
19
20
|
const logProgress = opt.logProgress !== false && logEvery !== 0; // true by default
|
|
20
21
|
const logEvery10 = logEvery * 10;
|
|
21
22
|
const started = Date.now();
|
|
@@ -24,13 +25,19 @@ function transformLogProgress(opt = {}) {
|
|
|
24
25
|
let processedLastSecond = 0;
|
|
25
26
|
let progress = 0;
|
|
26
27
|
let peakRSS = 0;
|
|
28
|
+
const sizes = logSizes ? new sizeStack_1.SizeStack('json', logSizesBuffer) : undefined;
|
|
29
|
+
const sizesZipped = logZippedSizes ? new sizeStack_1.SizeStack('json.gz', logSizesBuffer) : undefined;
|
|
27
30
|
logStats(); // initial
|
|
28
31
|
return new stream_1.Transform({
|
|
29
32
|
objectMode: true,
|
|
30
33
|
...opt,
|
|
31
|
-
transform(chunk,
|
|
34
|
+
transform(chunk, _, cb) {
|
|
32
35
|
progress++;
|
|
33
36
|
processedLastSecond++;
|
|
37
|
+
if (sizes) {
|
|
38
|
+
// Check it, cause gzipping might be delayed here..
|
|
39
|
+
void sizeStack_1.SizeStack.countItem(chunk, logger, sizes, sizesZipped);
|
|
40
|
+
}
|
|
34
41
|
if (logProgress && progress % logEvery === 0) {
|
|
35
42
|
logStats(chunk, false, progress % logEvery10 === 0);
|
|
36
43
|
}
|
|
@@ -54,32 +61,43 @@ function transformLogProgress(opt = {}) {
|
|
|
54
61
|
const rps10 = Math.round(sma.push(lastRPS));
|
|
55
62
|
if (mem.rss > peakRSS)
|
|
56
63
|
peakRSS = mem.rss;
|
|
57
|
-
|
|
64
|
+
const o = {
|
|
58
65
|
[final ? `${metric}_final` : metric]: batchedProgress,
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
66
|
+
};
|
|
67
|
+
if (extra)
|
|
68
|
+
Object.assign(o, extra(chunk, progress));
|
|
69
|
+
if (logHeapUsed)
|
|
70
|
+
o.heapUsed = (0, js_lib_1._mb)(mem.heapUsed);
|
|
71
|
+
if (logHeapTotal)
|
|
72
|
+
o.heapTotal = (0, js_lib_1._mb)(mem.heapTotal);
|
|
73
|
+
if (logRss)
|
|
74
|
+
o.rss = (0, js_lib_1._mb)(mem.rss);
|
|
75
|
+
if (logPeakRSS)
|
|
76
|
+
o.peakRSS = (0, js_lib_1._mb)(peakRSS);
|
|
77
|
+
if (opt.rssMinusHeap)
|
|
78
|
+
o.rssMinusHeap = (0, js_lib_1._mb)(mem.rss - mem.heapTotal);
|
|
79
|
+
if (opt.external)
|
|
80
|
+
o.external = (0, js_lib_1._mb)(mem.external);
|
|
81
|
+
if (opt.arrayBuffers)
|
|
82
|
+
o.arrayBuffers = (0, js_lib_1._mb)(mem.arrayBuffers || 0);
|
|
83
|
+
if (logRPS)
|
|
84
|
+
Object.assign(o, { rps10, rpsTotal });
|
|
85
|
+
logger.log((0, util_1.inspect)(o, inspectOpt));
|
|
86
|
+
if (sizes?.items.length) {
|
|
87
|
+
logger.log(sizes.getStats());
|
|
88
|
+
if (sizesZipped?.items.length) {
|
|
89
|
+
logger.log(sizesZipped.getStats());
|
|
90
|
+
}
|
|
91
|
+
}
|
|
74
92
|
if (tenx) {
|
|
75
93
|
let perHour = Math.round((batchedProgress * 1000 * 60 * 60) / (now - started)) || 0;
|
|
76
94
|
if (perHour > 900) {
|
|
77
95
|
perHour = Math.round(perHour / 1000) + 'K';
|
|
78
96
|
}
|
|
79
|
-
|
|
97
|
+
logger.log(`${(0, colors_1.dimGrey)((0, time_lib_1.dayjs)().toPretty())} ${(0, colors_1.white)(metric)} took ${(0, colors_1.yellow)((0, js_lib_1._since)(started))} so far to process ${(0, colors_1.yellow)(batchedProgress)} rows, ~${(0, colors_1.yellow)(perHour)}/hour`);
|
|
80
98
|
}
|
|
81
99
|
else if (final) {
|
|
82
|
-
|
|
100
|
+
logger.log(`${(0, colors_1.boldWhite)(metric)} took ${(0, colors_1.yellow)((0, js_lib_1._since)(started))} to process ${(0, colors_1.yellow)(batchedProgress)} rows with total RPS of ${(0, colors_1.yellow)(rpsTotal)}`);
|
|
83
101
|
}
|
|
84
102
|
}
|
|
85
103
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AbortableAsyncMapper, AsyncPredicate, CommonLogger, ErrorMode } from '@naturalcycles/js-lib';
|
|
2
2
|
import { TransformTyped } from '../stream.model';
|
|
3
3
|
export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
4
4
|
/**
|
|
@@ -11,9 +11,8 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
11
11
|
* Predicate to filter outgoing results (after mapper).
|
|
12
12
|
* Allows to not emit all results.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* @default to filter out undefined/null values, but pass anything else
|
|
14
|
+
* Defaults to "pass everything" (including null, undefined, etc).
|
|
15
|
+
* Simpler way to exclude certain cases is to return SKIP symbol from the mapper.
|
|
17
16
|
*/
|
|
18
17
|
predicate?: AsyncPredicate<OUT>;
|
|
19
18
|
/**
|
|
@@ -37,13 +36,8 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
37
36
|
* @default `stream`
|
|
38
37
|
*/
|
|
39
38
|
metric?: string;
|
|
40
|
-
/**
|
|
41
|
-
* If defined - called BEFORE `final()` callback is called.
|
|
42
|
-
*/
|
|
43
|
-
beforeFinal?: () => any;
|
|
44
39
|
logger?: CommonLogger;
|
|
45
40
|
}
|
|
46
|
-
export declare function notNullishPredicate(item: any): boolean;
|
|
47
41
|
/**
|
|
48
42
|
* Like pMap, but for streams.
|
|
49
43
|
* Inspired by `through2`.
|
|
@@ -56,4 +50,4 @@ export declare function notNullishPredicate(item: any): boolean;
|
|
|
56
50
|
*
|
|
57
51
|
* If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
|
|
58
52
|
*/
|
|
59
|
-
export declare function transformMap<IN = any, OUT = IN>(mapper:
|
|
53
|
+
export declare function transformMap<IN = any, OUT = IN>(mapper: AbortableAsyncMapper<IN, OUT>, opt?: TransformMapOptions<IN, OUT>): TransformTyped<IN, OUT>;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.transformMap =
|
|
4
|
-
const stream_1 = require("stream");
|
|
3
|
+
exports.transformMap = void 0;
|
|
5
4
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
|
+
const through2Concurrent = require("through2-concurrent");
|
|
6
6
|
const colors_1 = require("../../colors");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
exports.notNullishPredicate = notNullishPredicate;
|
|
7
|
+
const stream_util_1 = require("../stream.util");
|
|
8
|
+
// doesn't work, cause here we don't construct our Transform instance ourselves
|
|
9
|
+
// export class TransformMap extends AbortableTransform {}
|
|
11
10
|
/**
|
|
12
11
|
* Like pMap, but for streams.
|
|
13
12
|
* Inspired by `through2`.
|
|
@@ -21,80 +20,69 @@ exports.notNullishPredicate = notNullishPredicate;
|
|
|
21
20
|
* If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
|
|
22
21
|
*/
|
|
23
22
|
function transformMap(mapper, opt = {}) {
|
|
24
|
-
const { concurrency = 16, predicate
|
|
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;
|
|
25
25
|
let index = -1;
|
|
26
|
-
let
|
|
26
|
+
let isSettled = false;
|
|
27
27
|
let errors = 0;
|
|
28
28
|
const collectedErrors = []; // only used if errorMode == THROW_AGGREGATED
|
|
29
|
-
|
|
30
|
-
concurrency,
|
|
31
|
-
resolveOn: 'start',
|
|
32
|
-
// debug: true,
|
|
33
|
-
});
|
|
34
|
-
return new stream_1.Transform({
|
|
35
|
-
objectMode: true,
|
|
29
|
+
return through2Concurrent.obj({
|
|
30
|
+
maxConcurrency: concurrency,
|
|
36
31
|
async final(cb) {
|
|
37
|
-
// console.log('transformMap final'
|
|
38
|
-
|
|
39
|
-
await q.onIdle();
|
|
40
|
-
logErrorStats(logger, true);
|
|
41
|
-
await beforeFinal?.(); // call beforeFinal if defined
|
|
32
|
+
// console.log('transformMap final')
|
|
33
|
+
logErrorStats(true);
|
|
42
34
|
if (collectedErrors.length) {
|
|
43
35
|
// emit Aggregated error
|
|
44
|
-
// For the same reason, magically, let's not call `cb`, but emit an error event instead
|
|
45
|
-
// this.emit('error', new AggregatedError(collectedErrors))
|
|
46
36
|
cb(new js_lib_1.AggregatedError(collectedErrors));
|
|
47
37
|
}
|
|
48
38
|
else {
|
|
49
39
|
// emit no error
|
|
50
|
-
|
|
51
|
-
// Commenting it out seems to work ?!
|
|
52
|
-
// ?!
|
|
53
|
-
// cb()
|
|
40
|
+
cb();
|
|
54
41
|
}
|
|
55
42
|
},
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const res = await mapper(chunk, currentIndex);
|
|
68
|
-
const passedResults = await (0, js_lib_1.pFilter)(flattenArrayOutput && Array.isArray(res) ? res : [res], async (r) => await predicate(r, currentIndex));
|
|
69
|
-
passedResults.forEach(r => this.push(r));
|
|
70
|
-
}
|
|
71
|
-
catch (err) {
|
|
72
|
-
logger.error(err);
|
|
73
|
-
errors++;
|
|
74
|
-
logErrorStats(logger);
|
|
75
|
-
if (onError) {
|
|
76
|
-
try {
|
|
77
|
-
onError(err, chunk);
|
|
78
|
-
}
|
|
79
|
-
catch { }
|
|
80
|
-
}
|
|
81
|
-
if (errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY) {
|
|
82
|
-
isRejected = true;
|
|
83
|
-
// Emit error immediately
|
|
84
|
-
// return cb(err as Error)
|
|
85
|
-
return this.emit('error', err);
|
|
86
|
-
}
|
|
87
|
-
if (errorMode === js_lib_1.ErrorMode.THROW_AGGREGATED) {
|
|
88
|
-
collectedErrors.push(err);
|
|
89
|
-
}
|
|
43
|
+
}, async function transformMapFn(chunk, _, cb) {
|
|
44
|
+
// Stop processing if isSettled (either THROW_IMMEDIATELY was fired or END received)
|
|
45
|
+
if (isSettled)
|
|
46
|
+
return cb();
|
|
47
|
+
const currentIndex = ++index;
|
|
48
|
+
try {
|
|
49
|
+
const res = await mapper(chunk, currentIndex);
|
|
50
|
+
const passedResults = await (0, js_lib_1.pFilter)(flattenArrayOutput && Array.isArray(res) ? res : [res], async (r) => {
|
|
51
|
+
if (r === js_lib_1.END) {
|
|
52
|
+
isSettled = true; // will be checked later
|
|
53
|
+
return false;
|
|
90
54
|
}
|
|
55
|
+
return r !== js_lib_1.SKIP && (!predicate || (await predicate(r, currentIndex)));
|
|
91
56
|
});
|
|
92
|
-
|
|
93
|
-
|
|
57
|
+
passedResults.forEach(r => this.push(r));
|
|
58
|
+
if (isSettled) {
|
|
59
|
+
logger.log(`transformMap END received at index ${currentIndex}`);
|
|
60
|
+
(0, stream_util_1.pipelineClose)('transformMap', this, this.sourceReadable, this.streamDone, logger);
|
|
61
|
+
}
|
|
62
|
+
cb(); // done processing
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
logger.error(err);
|
|
66
|
+
errors++;
|
|
67
|
+
logErrorStats();
|
|
68
|
+
if (onError) {
|
|
69
|
+
try {
|
|
70
|
+
onError(err, chunk);
|
|
71
|
+
}
|
|
72
|
+
catch { }
|
|
73
|
+
}
|
|
74
|
+
if (errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY) {
|
|
75
|
+
isSettled = true;
|
|
76
|
+
return cb(err); // Emit error immediately
|
|
77
|
+
}
|
|
78
|
+
if (errorMode === js_lib_1.ErrorMode.THROW_AGGREGATED) {
|
|
79
|
+
collectedErrors.push(err);
|
|
80
|
+
}
|
|
81
|
+
// Tell input stream that we're done processing, but emit nothing to output - not error nor result
|
|
94
82
|
cb();
|
|
95
|
-
}
|
|
83
|
+
}
|
|
96
84
|
});
|
|
97
|
-
function logErrorStats(
|
|
85
|
+
function logErrorStats(final = false) {
|
|
98
86
|
if (!errors)
|
|
99
87
|
return;
|
|
100
88
|
logger.log(`${metric} ${final ? 'final ' : ''}errors: ${(0, colors_1.yellow)(errors)}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ErrorMode, Mapper } from '@naturalcycles/js-lib';
|
|
1
|
+
import { CommonLogger, ErrorMode, Mapper } from '@naturalcycles/js-lib';
|
|
2
2
|
import { TransformTyped } from '../stream.model';
|
|
3
3
|
export interface TransformMapSimpleOptions {
|
|
4
4
|
/**
|
|
@@ -7,6 +7,7 @@ export interface TransformMapSimpleOptions {
|
|
|
7
7
|
* @default ErrorMode.THROW_IMMEDIATELY
|
|
8
8
|
*/
|
|
9
9
|
errorMode?: ErrorMode.THROW_IMMEDIATELY | ErrorMode.SUPPRESS;
|
|
10
|
+
logger?: CommonLogger;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
13
|
* Simplest version of `transformMap`.
|