@naturalcycles/nodejs-lib 13.9.1 → 13.11.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 (31) hide show
  1. package/dist/csv/csvReader.js +1 -1
  2. package/dist/index.d.ts +1 -2
  3. package/dist/index.js +1 -2
  4. package/dist/stream/progressLogger.d.ts +152 -0
  5. package/dist/stream/progressLogger.js +133 -0
  6. package/dist/stream/readable/readableCreate.d.ts +1 -1
  7. package/dist/stream/readable/readableCreate.js +2 -2
  8. package/dist/stream/readable/readableForEach.d.ts +2 -0
  9. package/dist/stream/readable/readableForEach.js +2 -0
  10. package/dist/stream/readable/readableToArray.d.ts +3 -0
  11. package/dist/stream/readable/readableToArray.js +11 -5
  12. package/dist/stream/stream.model.d.ts +24 -2
  13. package/dist/stream/transform/transformLogProgress.d.ts +2 -105
  14. package/dist/stream/transform/transformLogProgress.js +4 -82
  15. package/dist/stream/transform/transformTee.d.ts +1 -1
  16. package/package.json +1 -1
  17. package/src/csv/csvReader.ts +1 -1
  18. package/src/index.ts +1 -2
  19. package/src/stream/progressLogger.ts +324 -0
  20. package/src/stream/readable/readableCreate.ts +2 -2
  21. package/src/stream/readable/readableForEach.ts +2 -0
  22. package/src/stream/readable/readableToArray.ts +11 -7
  23. package/src/stream/stream.model.ts +46 -4
  24. package/src/stream/transform/transformLogProgress.ts +7 -253
  25. package/src/stream/transform/transformTee.ts +1 -1
  26. package/dist/stream/readable/readableMap.d.ts +0 -3
  27. package/dist/stream/readable/readableMap.js +0 -31
  28. package/dist/stream/readable/readableMapToArray.d.ts +0 -10
  29. package/dist/stream/readable/readableMapToArray.js +0 -16
  30. package/src/stream/readable/readableMap.ts +0 -34
  31. package/src/stream/readable/readableMapToArray.ts +0 -22
@@ -35,7 +35,7 @@ function csvStringParse(str, cfg = {}) {
35
35
  exports.csvStringParse = csvStringParse;
36
36
  function csvStringToArray(str) {
37
37
  const objPattern = new RegExp('(,|\\r?\\n|\\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\\r\\n]*))', 'gi');
38
- let matches = null;
38
+ let matches;
39
39
  const arr = [[]];
40
40
  while ((matches = objPattern.exec(str))) {
41
41
  if (matches[1].length && matches[1] !== ',') {
package/dist/index.d.ts CHANGED
@@ -31,10 +31,9 @@ export * from './stream/pipeline/pipeline';
31
31
  export * from './stream/readable/readableCreate';
32
32
  export * from './stream/readable/readableForEach';
33
33
  export * from './stream/readable/readableFromArray';
34
- export * from './stream/readable/readableMap';
35
- export * from './stream/readable/readableMapToArray';
36
34
  export * from './stream/readable/readableToArray';
37
35
  export * from './stream/stream.model';
36
+ export * from './stream/progressLogger';
38
37
  export * from './stream/transform/transformBuffer';
39
38
  export * from './stream/transform/transformFilter';
40
39
  export * from './stream/transform/transformLimit';
package/dist/index.js CHANGED
@@ -35,10 +35,9 @@ tslib_1.__exportStar(require("./stream/pipeline/pipeline"), exports);
35
35
  tslib_1.__exportStar(require("./stream/readable/readableCreate"), exports);
36
36
  tslib_1.__exportStar(require("./stream/readable/readableForEach"), exports);
37
37
  tslib_1.__exportStar(require("./stream/readable/readableFromArray"), exports);
38
- tslib_1.__exportStar(require("./stream/readable/readableMap"), exports);
39
- tslib_1.__exportStar(require("./stream/readable/readableMapToArray"), exports);
40
38
  tslib_1.__exportStar(require("./stream/readable/readableToArray"), exports);
41
39
  tslib_1.__exportStar(require("./stream/stream.model"), exports);
40
+ tslib_1.__exportStar(require("./stream/progressLogger"), exports);
42
41
  tslib_1.__exportStar(require("./stream/transform/transformBuffer"), exports);
43
42
  tslib_1.__exportStar(require("./stream/transform/transformFilter"), exports);
44
43
  tslib_1.__exportStar(require("./stream/transform/transformLimit"), exports);
@@ -0,0 +1,152 @@
1
+ import { AnyObject, CommonLogger } from '@naturalcycles/js-lib';
2
+ import { ReadableMapper } from './stream.model';
3
+ export interface ProgressLoggerCfg<T = any> {
4
+ /**
5
+ * Progress metric
6
+ *
7
+ * @default `progress`
8
+ */
9
+ metric?: string;
10
+ /**
11
+ * Include `heapUsed` in log.
12
+ *
13
+ * @default false
14
+ */
15
+ heapUsed?: boolean;
16
+ /**
17
+ * Include `heapTotal` in log.
18
+ *
19
+ * @default false
20
+ */
21
+ heapTotal?: boolean;
22
+ /**
23
+ * Include `rss` in log.
24
+ *
25
+ * @default true
26
+ */
27
+ rss?: boolean;
28
+ /**
29
+ * Incude Peak RSS in log.
30
+ *
31
+ * @default true
32
+ */
33
+ peakRSS?: boolean;
34
+ /**
35
+ * Include `external` in log.
36
+ *
37
+ * @default false
38
+ */
39
+ external?: boolean;
40
+ /**
41
+ * Include `arrayBuffers` in log.
42
+ *
43
+ * @default false
44
+ */
45
+ arrayBuffers?: boolean;
46
+ /**
47
+ * Log (rss - heapTotal)
48
+ * For convenience of debugging "out-of-heap" memory size.
49
+ *
50
+ * @default false
51
+ */
52
+ rssMinusHeap?: boolean;
53
+ /**
54
+ * Log "rows per second"
55
+ *
56
+ * @default true
57
+ */
58
+ logRPS?: boolean;
59
+ /**
60
+ * Set to false to disable logging progress
61
+ *
62
+ * @default true
63
+ */
64
+ logProgress?: boolean;
65
+ /**
66
+ * Log progress event Nth record that is _processed_ (went through mapper).
67
+ * Set to 0 to disable logging.
68
+ *
69
+ * @default 1000
70
+ */
71
+ logEvery?: number;
72
+ logger?: CommonLogger;
73
+ /**
74
+ * Function to return extra properties to the "progress object".
75
+ *
76
+ * chunk is undefined for "final" stats, otherwise is defined.
77
+ */
78
+ extra?: (chunk: T | undefined, index: number) => AnyObject;
79
+ /**
80
+ * If specified - will multiply the counter by this number.
81
+ * Useful e.g when using `transformBuffer({ batchSize: 500 })`, so
82
+ * it'll accurately represent the number of processed entries (not batches).
83
+ *
84
+ * Defaults to 1.
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;
107
+ }
108
+ export interface ProgressLogItem extends AnyObject {
109
+ heapUsed?: number;
110
+ heapTotal?: number;
111
+ rss?: number;
112
+ peakRSS?: number;
113
+ rssMinusHeap?: number;
114
+ external?: number;
115
+ arrayBuffers?: number;
116
+ rps10?: number;
117
+ rpsTotal?: number;
118
+ }
119
+ export declare class ProgressLogger<T> implements Disposable {
120
+ constructor(cfg?: ProgressLoggerCfg<T>);
121
+ cfg: ProgressLoggerCfg<T> & {
122
+ logEvery: number;
123
+ logSizesBuffer: number;
124
+ batchSize: number;
125
+ metric: string;
126
+ logger: CommonLogger;
127
+ };
128
+ private started;
129
+ private lastSecondStarted;
130
+ private sma;
131
+ private logEvery10;
132
+ private processedLastSecond;
133
+ private progress;
134
+ private peakRSS;
135
+ private sizes?;
136
+ private sizesZipped?;
137
+ private start;
138
+ log(chunk?: T): void;
139
+ done(): void;
140
+ [Symbol.dispose](): void;
141
+ private logStats;
142
+ }
143
+ /**
144
+ * Create new ProgressLogger.
145
+ */
146
+ export declare function progressLogger<T>(cfg?: ProgressLoggerCfg<T>): ProgressLogger<T>;
147
+ /**
148
+ * Limitation: I don't know how to catch the `final` callback to log final stats.
149
+ *
150
+ * @experimental
151
+ */
152
+ export declare function progressReadableMapper<T>(cfg?: ProgressLoggerCfg<T>): ReadableMapper<T, T>;
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.progressReadableMapper = exports.progressLogger = exports.ProgressLogger = void 0;
4
+ const node_util_1 = require("node:util");
5
+ const js_lib_1 = require("@naturalcycles/js-lib");
6
+ const colors_1 = require("../colors/colors");
7
+ const sizeStack_1 = require("./sizeStack");
8
+ const inspectOpt = {
9
+ colors: colors_1.hasColors,
10
+ breakLength: 300,
11
+ };
12
+ class ProgressLogger {
13
+ constructor(cfg = {}) {
14
+ this.cfg = {
15
+ metric: 'progress',
16
+ rss: true,
17
+ peakRSS: true,
18
+ logRPS: true,
19
+ logEvery: 1000,
20
+ logSizesBuffer: 100_000,
21
+ batchSize: 1,
22
+ logger: console,
23
+ logProgress: cfg.logProgress !== false && cfg.logEvery !== 0,
24
+ ...cfg,
25
+ };
26
+ this.logEvery10 = this.cfg.logEvery * 10;
27
+ this.start();
28
+ this.logStats(); // initial
29
+ }
30
+ start() {
31
+ this.started = Date.now();
32
+ this.lastSecondStarted = Date.now();
33
+ this.sma = new js_lib_1.SimpleMovingAverage(10);
34
+ this.processedLastSecond = 0;
35
+ this.progress = 0;
36
+ this.peakRSS = 0;
37
+ this.sizes = this.cfg.logSizes ? new sizeStack_1.SizeStack('json', this.cfg.logSizesBuffer) : undefined;
38
+ this.sizesZipped = this.cfg.logZippedSizes
39
+ ? new sizeStack_1.SizeStack('json.gz', this.cfg.logSizesBuffer)
40
+ : undefined;
41
+ }
42
+ log(chunk) {
43
+ this.progress++;
44
+ this.processedLastSecond++;
45
+ if (this.sizes) {
46
+ // Check it, cause gzipping might be delayed here..
47
+ void sizeStack_1.SizeStack.countItem(chunk, this.cfg.logger, this.sizes, this.sizesZipped);
48
+ }
49
+ if (this.cfg.logProgress && this.progress % this.cfg.logEvery === 0) {
50
+ this.logStats(chunk, false, this.progress % this.logEvery10 === 0);
51
+ }
52
+ }
53
+ done() {
54
+ this.logStats(undefined, true);
55
+ }
56
+ [Symbol.dispose]() {
57
+ this.done();
58
+ }
59
+ logStats(chunk, final = false, tenx = false) {
60
+ if (!this.cfg.logProgress)
61
+ return;
62
+ const { metric, extra, batchSize, heapUsed: logHeapUsed, heapTotal: logHeapTotal, rss: logRss, peakRSS: logPeakRss, rssMinusHeap, external, arrayBuffers, logRPS, logger, } = this.cfg;
63
+ const mem = process.memoryUsage();
64
+ const now = Date.now();
65
+ const batchedProgress = this.progress * batchSize;
66
+ const lastRPS = (this.processedLastSecond * batchSize) / ((now - this.lastSecondStarted) / 1000) || 0;
67
+ const rpsTotal = Math.round(batchedProgress / ((now - this.started) / 1000)) || 0;
68
+ this.lastSecondStarted = now;
69
+ this.processedLastSecond = 0;
70
+ const rps10 = Math.round(this.sma.pushGetAvg(lastRPS));
71
+ if (mem.rss > this.peakRSS)
72
+ this.peakRSS = mem.rss;
73
+ const o = {
74
+ [final ? `${this.cfg.metric}_final` : this.cfg.metric]: batchedProgress,
75
+ };
76
+ if (extra)
77
+ Object.assign(o, extra(chunk, this.progress));
78
+ if (logHeapUsed)
79
+ o.heapUsed = (0, js_lib_1._mb)(mem.heapUsed);
80
+ if (logHeapTotal)
81
+ o.heapTotal = (0, js_lib_1._mb)(mem.heapTotal);
82
+ if (logRss)
83
+ o.rss = (0, js_lib_1._mb)(mem.rss);
84
+ if (logPeakRss)
85
+ o.peakRSS = (0, js_lib_1._mb)(this.peakRSS);
86
+ if (rssMinusHeap)
87
+ o.rssMinusHeap = (0, js_lib_1._mb)(mem.rss - mem.heapTotal);
88
+ if (external)
89
+ o.external = (0, js_lib_1._mb)(mem.external);
90
+ if (arrayBuffers)
91
+ o.arrayBuffers = (0, js_lib_1._mb)(mem.arrayBuffers || 0);
92
+ if (logRPS)
93
+ Object.assign(o, { rps10, rpsTotal });
94
+ logger.log((0, node_util_1.inspect)(o, inspectOpt));
95
+ if (this.sizes?.items.length) {
96
+ logger.log(this.sizes.getStats());
97
+ if (this.sizesZipped?.items.length) {
98
+ logger.log(this.sizesZipped.getStats());
99
+ }
100
+ }
101
+ if (tenx) {
102
+ let perHour = Math.round((batchedProgress * 1000 * 60 * 60) / (now - this.started)) || 0;
103
+ if (perHour > 900) {
104
+ perHour = Math.round(perHour / 1000) + 'K';
105
+ }
106
+ logger.log(`${(0, colors_1.dimGrey)((0, js_lib_1.localTimeNow)().toPretty())} ${(0, colors_1.white)(metric)} took ${(0, colors_1.yellow)((0, js_lib_1._since)(this.started))} so far to process ${(0, colors_1.yellow)(batchedProgress)} rows, ~${(0, colors_1.yellow)(perHour)}/hour`);
107
+ }
108
+ else if (final) {
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
+ }
111
+ }
112
+ }
113
+ exports.ProgressLogger = ProgressLogger;
114
+ /**
115
+ * Create new ProgressLogger.
116
+ */
117
+ function progressLogger(cfg = {}) {
118
+ return new ProgressLogger(cfg);
119
+ }
120
+ exports.progressLogger = progressLogger;
121
+ /**
122
+ * Limitation: I don't know how to catch the `final` callback to log final stats.
123
+ *
124
+ * @experimental
125
+ */
126
+ function progressReadableMapper(cfg = {}) {
127
+ const progress = new ProgressLogger(cfg);
128
+ return chunk => {
129
+ progress.log(chunk);
130
+ return chunk;
131
+ };
132
+ }
133
+ exports.progressReadableMapper = progressReadableMapper;
@@ -19,4 +19,4 @@ export declare function readableCreate<T>(items?: Iterable<T>, opt?: ReadableOpt
19
19
  /**
20
20
  * Convenience type-safe wrapper around Readable.from() that infers the Type of input.
21
21
  */
22
- export declare function readableFrom<T>(items: Iterable<T> | AsyncIterable<T>, opt?: ReadableOptions): ReadableTyped<T>;
22
+ export declare function readableFrom<T>(iterable: Iterable<T> | AsyncIterable<T>, opt?: ReadableOptions): ReadableTyped<T>;
@@ -31,7 +31,7 @@ exports.readableCreate = readableCreate;
31
31
  /**
32
32
  * Convenience type-safe wrapper around Readable.from() that infers the Type of input.
33
33
  */
34
- function readableFrom(items, opt) {
35
- return node_stream_1.Readable.from(items, opt);
34
+ function readableFrom(iterable, opt) {
35
+ return node_stream_1.Readable.from(iterable, opt);
36
36
  }
37
37
  exports.readableFrom = readableFrom;
@@ -5,6 +5,8 @@ import { TransformMapOptions } from '../transform/transformMap';
5
5
  * Convenience function to do `.forEach` over a Readable.
6
6
  * Typed! (unlike default Readable).
7
7
  *
8
+ * Try native readable.forEach() instead!
9
+ *
8
10
  * @experimental
9
11
  */
10
12
  export declare function readableForEach<T>(readable: ReadableTyped<T>, mapper: AbortableAsyncMapper<T, void>, opt?: TransformMapOptions<T, void>): Promise<void>;
@@ -8,6 +8,8 @@ const transformMap_1 = require("../transform/transformMap");
8
8
  * Convenience function to do `.forEach` over a Readable.
9
9
  * Typed! (unlike default Readable).
10
10
  *
11
+ * Try native readable.forEach() instead!
12
+ *
11
13
  * @experimental
12
14
  */
13
15
  async function readableForEach(readable, mapper, opt = {}) {
@@ -2,5 +2,8 @@ import { ReadableTyped } from '../stream.model';
2
2
  /**
3
3
  * Convenience function to read the whole Readable stream into Array (in-memory)
4
4
  * and return that array.
5
+ *
6
+ * Native `await readable.toArray()` can be used instead.
7
+ * This helper is kept for type-safery support.
5
8
  */
6
9
  export declare function readableToArray<T>(readable: ReadableTyped<T>): Promise<T[]>;
@@ -4,12 +4,18 @@ exports.readableToArray = void 0;
4
4
  /**
5
5
  * Convenience function to read the whole Readable stream into Array (in-memory)
6
6
  * and return that array.
7
+ *
8
+ * Native `await readable.toArray()` can be used instead.
9
+ * This helper is kept for type-safery support.
7
10
  */
8
11
  async function readableToArray(readable) {
9
- const a = [];
10
- for await (const item of readable) {
11
- a.push(item);
12
- }
13
- return a;
12
+ return await readable.toArray();
13
+ // const a: T[] = []
14
+ //
15
+ // for await (const item of readable) {
16
+ // a.push(item)
17
+ // }
18
+ //
19
+ // return a
14
20
  }
15
21
  exports.readableToArray = readableToArray;
@@ -1,10 +1,32 @@
1
1
  /// <reference types="node" />
2
- import { Readable, Transform, Writable } from 'node:stream';
2
+ import type { Readable, Transform, Writable } from 'node:stream';
3
+ import type { Promisable } from '@naturalcycles/js-lib';
4
+ export interface ReadableSignalOptions {
5
+ /** allows destroying the stream if the signal is aborted. */
6
+ signal?: AbortSignal;
7
+ }
8
+ export interface ReadableArrayOptions {
9
+ /** the maximum concurrent invocations of `fn` to call on the stream at once. **Default: 1**. */
10
+ concurrency?: number;
11
+ /** allows destroying the stream if the signal is aborted. */
12
+ signal?: AbortSignal;
13
+ }
14
+ export type ReadableMapper<IN, OUT> = (data: IN, opt?: ReadableSignalOptions) => Promisable<OUT>;
15
+ export type ReadableFlatMapper<IN, OUT> = (data: IN, opt?: ReadableSignalOptions) => Promisable<OUT[]>;
16
+ export type ReadableVoidMapper<IN> = (data: IN, opt?: ReadableSignalOptions) => void | Promise<void>;
17
+ export type ReadablePredicate<IN> = (data: IN, opt?: ReadableSignalOptions) => boolean | Promise<boolean>;
3
18
  export interface ReadableTyped<T> extends Readable {
19
+ toArray: (opt?: ReadableSignalOptions) => Promise<T[]>;
20
+ map: <OUT>(mapper: ReadableMapper<T, OUT>, opt?: ReadableArrayOptions) => ReadableTyped<OUT>;
21
+ flatMap: <OUT>(mapper: ReadableFlatMapper<T, OUT>, opt?: ReadableArrayOptions) => ReadableTyped<OUT>;
22
+ filter: (predicate: ReadablePredicate<T>, opt?: ReadableArrayOptions) => ReadableTyped<T>;
23
+ forEach: (mapper: ReadableVoidMapper<T>, opt?: ReadableArrayOptions) => Promise<void>;
24
+ take: (limit: number, opt?: ReadableSignalOptions) => ReadableTyped<T>;
25
+ drop: (limit: number, opt?: ReadableSignalOptions) => ReadableTyped<T>;
4
26
  }
5
27
  export interface WritableTyped<T> extends Writable {
6
28
  }
7
- export interface TransformTyped<IN, OUT = IN> extends Transform {
29
+ export interface TransformTyped<IN, OUT> extends Transform {
8
30
  }
9
31
  export interface TransformOptions {
10
32
  /**
@@ -1,109 +1,6 @@
1
- import { AnyObject, CommonLogger } from '@naturalcycles/js-lib';
1
+ import { ProgressLoggerCfg } from '../progressLogger';
2
2
  import { TransformOptions, TransformTyped } from '../stream.model';
3
- export interface TransformLogProgressOptions<IN = any> extends TransformOptions {
4
- /**
5
- * Progress metric
6
- *
7
- * @default `progress`
8
- */
9
- metric?: string;
10
- /**
11
- * Include `heapUsed` in log.
12
- *
13
- * @default false
14
- */
15
- heapUsed?: boolean;
16
- /**
17
- * Include `heapTotal` in log.
18
- *
19
- * @default false
20
- */
21
- heapTotal?: boolean;
22
- /**
23
- * Include `rss` in log.
24
- *
25
- * @default true
26
- */
27
- rss?: boolean;
28
- /**
29
- * Incude Peak RSS in log.
30
- *
31
- * @default true
32
- */
33
- peakRSS?: boolean;
34
- /**
35
- * Include `external` in log.
36
- *
37
- * @default false
38
- */
39
- external?: boolean;
40
- /**
41
- * Include `arrayBuffers` in log.
42
- *
43
- * @default false
44
- */
45
- arrayBuffers?: boolean;
46
- /**
47
- * Log (rss - heapTotal)
48
- * For convenience of debugging "out-of-heap" memory size.
49
- *
50
- * @default false
51
- */
52
- rssMinusHeap?: boolean;
53
- /**
54
- * Log "rows per second"
55
- *
56
- * @default true
57
- */
58
- logRPS?: boolean;
59
- /**
60
- * Set to false to disable logging progress
61
- *
62
- * @default true
63
- */
64
- logProgress?: boolean;
65
- /**
66
- * Log progress event Nth record that is _processed_ (went through mapper).
67
- * Set to 0 to disable logging.
68
- *
69
- * @default 1000
70
- */
71
- logEvery?: number;
72
- logger?: CommonLogger;
73
- /**
74
- * Function to return extra properties to the "progress object".
75
- *
76
- * chunk is undefined for "final" stats, otherwise is defined.
77
- */
78
- extra?: (chunk: IN | undefined, index: number) => AnyObject;
79
- /**
80
- * If specified - will multiply the counter by this number.
81
- * Useful e.g when using `transformBuffer({ batchSize: 500 })`, so
82
- * it'll accurately represent the number of processed entries (not batches).
83
- *
84
- * Defaults to 1.
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;
3
+ export interface TransformLogProgressOptions<IN = any> extends ProgressLoggerCfg<IN>, TransformOptions {
107
4
  }
108
5
  /**
109
6
  * Pass-through transform that optionally logs progress.
@@ -2,101 +2,23 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformLogProgress = void 0;
4
4
  const node_stream_1 = require("node:stream");
5
- const node_util_1 = require("node:util");
6
- const js_lib_1 = require("@naturalcycles/js-lib");
7
- const colors_1 = require("../../colors/colors");
8
- const sizeStack_1 = require("../sizeStack");
9
- const inspectOpt = {
10
- colors: colors_1.hasColors,
11
- breakLength: 300,
12
- };
5
+ const progressLogger_1 = require("../progressLogger");
13
6
  /**
14
7
  * Pass-through transform that optionally logs progress.
15
8
  */
16
9
  function transformLogProgress(opt = {}) {
17
- const { metric = 'progress', heapTotal: logHeapTotal = false, heapUsed: logHeapUsed = false, rss: logRss = true, peakRSS: logPeakRSS = true, logRPS = true, logEvery = 1000, logSizes = false, logSizesBuffer = 100_000, logZippedSizes = false, batchSize = 1, extra, logger = console, } = opt;
18
- const logProgress = opt.logProgress !== false && logEvery !== 0; // true by default
19
- const logEvery10 = logEvery * 10;
20
- const started = Date.now();
21
- let lastSecondStarted = Date.now();
22
- const sma = new js_lib_1.SimpleMovingAverage(10); // over last 10 seconds
23
- let processedLastSecond = 0;
24
- let progress = 0;
25
- let peakRSS = 0;
26
- const sizes = logSizes ? new sizeStack_1.SizeStack('json', logSizesBuffer) : undefined;
27
- const sizesZipped = logZippedSizes ? new sizeStack_1.SizeStack('json.gz', logSizesBuffer) : undefined;
28
- logStats(); // initial
10
+ const progress = (0, progressLogger_1.progressLogger)(opt);
29
11
  return new node_stream_1.Transform({
30
12
  objectMode: true,
31
13
  ...opt,
32
14
  transform(chunk, _, cb) {
33
- progress++;
34
- processedLastSecond++;
35
- if (sizes) {
36
- // Check it, cause gzipping might be delayed here..
37
- void sizeStack_1.SizeStack.countItem(chunk, logger, sizes, sizesZipped);
38
- }
39
- if (logProgress && progress % logEvery === 0) {
40
- logStats(chunk, false, progress % logEvery10 === 0);
41
- }
15
+ progress.log(chunk);
42
16
  cb(null, chunk); // pass-through
43
17
  },
44
18
  final(cb) {
45
- logStats(undefined, true);
19
+ progress.done();
46
20
  cb();
47
21
  },
48
22
  });
49
- function logStats(chunk, final = false, tenx = false) {
50
- if (!logProgress)
51
- return;
52
- const mem = process.memoryUsage();
53
- const now = Date.now();
54
- const batchedProgress = progress * batchSize;
55
- const lastRPS = (processedLastSecond * batchSize) / ((now - lastSecondStarted) / 1000) || 0;
56
- const rpsTotal = Math.round(batchedProgress / ((now - started) / 1000)) || 0;
57
- lastSecondStarted = now;
58
- processedLastSecond = 0;
59
- const rps10 = Math.round(sma.pushGetAvg(lastRPS));
60
- if (mem.rss > peakRSS)
61
- peakRSS = mem.rss;
62
- const o = {
63
- [final ? `${metric}_final` : metric]: batchedProgress,
64
- };
65
- if (extra)
66
- Object.assign(o, extra(chunk, progress));
67
- if (logHeapUsed)
68
- o.heapUsed = (0, js_lib_1._mb)(mem.heapUsed);
69
- if (logHeapTotal)
70
- o.heapTotal = (0, js_lib_1._mb)(mem.heapTotal);
71
- if (logRss)
72
- o.rss = (0, js_lib_1._mb)(mem.rss);
73
- if (logPeakRSS)
74
- o.peakRSS = (0, js_lib_1._mb)(peakRSS);
75
- if (opt.rssMinusHeap)
76
- o.rssMinusHeap = (0, js_lib_1._mb)(mem.rss - mem.heapTotal);
77
- if (opt.external)
78
- o.external = (0, js_lib_1._mb)(mem.external);
79
- if (opt.arrayBuffers)
80
- o.arrayBuffers = (0, js_lib_1._mb)(mem.arrayBuffers || 0);
81
- if (logRPS)
82
- Object.assign(o, { rps10, rpsTotal });
83
- logger.log((0, node_util_1.inspect)(o, inspectOpt));
84
- if (sizes?.items.length) {
85
- logger.log(sizes.getStats());
86
- if (sizesZipped?.items.length) {
87
- logger.log(sizesZipped.getStats());
88
- }
89
- }
90
- if (tenx) {
91
- let perHour = Math.round((batchedProgress * 1000 * 60 * 60) / (now - started)) || 0;
92
- if (perHour > 900) {
93
- perHour = Math.round(perHour / 1000) + 'K';
94
- }
95
- logger.log(`${(0, colors_1.dimGrey)((0, js_lib_1.localTimeNow)().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`);
96
- }
97
- else if (final) {
98
- 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)}`);
99
- }
100
- }
101
23
  }
102
24
  exports.transformLogProgress = transformLogProgress;
@@ -10,5 +10,5 @@ type AnyStream = NodeJS.WritableStream | NodeJS.ReadWriteStream;
10
10
  *
11
11
  * @experimental
12
12
  */
13
- export declare function transformTee<T>(streams: AnyStream[]): TransformTyped<T>;
13
+ export declare function transformTee<T>(streams: AnyStream[]): TransformTyped<T, T>;
14
14
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
- "version": "13.9.1",
3
+ "version": "13.11.0",
4
4
  "scripts": {
5
5
  "prepare": "husky",
6
6
  "docs-serve": "vuepress dev docs",
@@ -57,7 +57,7 @@ export function csvStringParse<T extends AnyObject = any>(
57
57
 
58
58
  export function csvStringToArray(str: string): string[][] {
59
59
  const objPattern = new RegExp('(,|\\r?\\n|\\r|^)(?:"([^"]*(?:""[^"]*)*)"|([^,\\r\\n]*))', 'gi')
60
- let matches = null
60
+ let matches: RegExpExecArray | null
61
61
  const arr: any[][] = [[]]
62
62
 
63
63
  while ((matches = objPattern.exec(str))) {