@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,8 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
AggregatedError,
|
|
3
|
+
CommonLogger,
|
|
4
|
+
END,
|
|
5
|
+
ErrorMode,
|
|
6
|
+
Mapper,
|
|
7
|
+
Predicate,
|
|
8
|
+
SKIP,
|
|
9
|
+
} from '@naturalcycles/js-lib'
|
|
3
10
|
import { yellow } from '../../colors'
|
|
11
|
+
import { AbortableTransform } from '../pipeline/pipeline'
|
|
4
12
|
import { TransformTyped } from '../stream.model'
|
|
5
|
-
import {
|
|
13
|
+
import { pipelineClose } from '../stream.util'
|
|
6
14
|
|
|
7
15
|
export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
8
16
|
/**
|
|
@@ -20,9 +28,8 @@ export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
|
20
28
|
* Predicate to filter outgoing results (after mapper).
|
|
21
29
|
* Allows to not emit all results.
|
|
22
30
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* Set to `r => r` (passthrough predicate) to pass ANY value (including undefined/null)
|
|
31
|
+
* Defaults to "pass everything".
|
|
32
|
+
* Simpler way to skip individual entries is to return SKIP symbol.
|
|
26
33
|
*/
|
|
27
34
|
predicate?: Predicate<OUT>
|
|
28
35
|
|
|
@@ -43,8 +50,12 @@ export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
|
43
50
|
* @default `stream`
|
|
44
51
|
*/
|
|
45
52
|
metric?: string
|
|
53
|
+
|
|
54
|
+
logger?: CommonLogger
|
|
46
55
|
}
|
|
47
56
|
|
|
57
|
+
export class TransformMapSync extends AbortableTransform {}
|
|
58
|
+
|
|
48
59
|
/**
|
|
49
60
|
* Sync (not async) version of transformMap.
|
|
50
61
|
* Supposedly faster, for cases when async is not needed.
|
|
@@ -56,43 +67,49 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
56
67
|
let index = -1
|
|
57
68
|
|
|
58
69
|
const {
|
|
59
|
-
predicate
|
|
70
|
+
predicate, // defaults to "no predicate" (pass everything)
|
|
60
71
|
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
61
72
|
flattenArrayOutput = false,
|
|
62
73
|
onError,
|
|
63
74
|
metric = 'stream',
|
|
64
75
|
objectMode = true,
|
|
76
|
+
logger = console,
|
|
65
77
|
} = opt
|
|
66
|
-
let
|
|
78
|
+
let isSettled = false
|
|
67
79
|
let errors = 0
|
|
68
80
|
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
69
81
|
|
|
70
|
-
return new
|
|
82
|
+
return new TransformMapSync({
|
|
71
83
|
objectMode,
|
|
72
84
|
...opt,
|
|
73
|
-
transform(this:
|
|
74
|
-
// Stop processing if
|
|
75
|
-
if (
|
|
76
|
-
return cb()
|
|
77
|
-
}
|
|
85
|
+
transform(this: AbortableTransform, chunk: IN, _, cb) {
|
|
86
|
+
// Stop processing if isSettled
|
|
87
|
+
if (isSettled) return cb()
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
if (!predicate(chunk, ++index)) {
|
|
81
|
-
cb() // signal that we've finished processing, but emit no output here
|
|
82
|
-
return
|
|
83
|
-
}
|
|
89
|
+
const currentIndex = ++index
|
|
84
90
|
|
|
91
|
+
try {
|
|
85
92
|
// map and pass through
|
|
86
|
-
const v = mapper(chunk,
|
|
93
|
+
const v = mapper(chunk, currentIndex)
|
|
94
|
+
|
|
95
|
+
const passedResults = (flattenArrayOutput && Array.isArray(v) ? v : [v]).filter(r => {
|
|
96
|
+
if (r === END) {
|
|
97
|
+
isSettled = true // will be checked later
|
|
98
|
+
return false
|
|
99
|
+
}
|
|
100
|
+
return r !== SKIP && (!predicate || predicate(r, currentIndex))
|
|
101
|
+
})
|
|
87
102
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
103
|
+
passedResults.forEach(r => this.push(r))
|
|
104
|
+
|
|
105
|
+
if (isSettled) {
|
|
106
|
+
logger.log(`transformMapSync END received at index ${currentIndex}`)
|
|
107
|
+
pipelineClose('transformMapSync', this, this.sourceReadable, this.streamDone, logger)
|
|
93
108
|
}
|
|
109
|
+
|
|
110
|
+
cb() // done processing
|
|
94
111
|
} catch (err) {
|
|
95
|
-
|
|
112
|
+
logger.error(err)
|
|
96
113
|
errors++
|
|
97
114
|
|
|
98
115
|
logErrorStats()
|
|
@@ -104,7 +121,7 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
104
121
|
}
|
|
105
122
|
|
|
106
123
|
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
107
|
-
|
|
124
|
+
isSettled = true
|
|
108
125
|
// Emit error immediately
|
|
109
126
|
return cb(err as Error)
|
|
110
127
|
}
|
|
@@ -134,6 +151,6 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
134
151
|
function logErrorStats(final = false): void {
|
|
135
152
|
if (!errors) return
|
|
136
153
|
|
|
137
|
-
|
|
154
|
+
logger.log(`${metric} ${final ? 'final ' : ''}errors: ${yellow(errors)}`)
|
|
138
155
|
}
|
|
139
156
|
}
|
|
@@ -9,7 +9,7 @@ import { TransformTyped } from '../stream.model'
|
|
|
9
9
|
export function transformNoOp<T = any>(): TransformTyped<T, T> {
|
|
10
10
|
return new Transform({
|
|
11
11
|
objectMode: true,
|
|
12
|
-
transform(chunk: T,
|
|
12
|
+
transform(chunk: T, _, cb) {
|
|
13
13
|
cb(null, chunk)
|
|
14
14
|
},
|
|
15
15
|
})
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { Transform } from 'stream'
|
|
2
|
-
import { AsyncMapper } from '@naturalcycles/js-lib'
|
|
2
|
+
import { AsyncMapper, CommonLogger } from '@naturalcycles/js-lib'
|
|
3
3
|
import { TransformOptions, TransformTyped } from '../stream.model'
|
|
4
4
|
|
|
5
|
+
export interface TransformTapOptions extends TransformOptions {
|
|
6
|
+
logger?: CommonLogger
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* Similar to RxJS `tap` - allows to run a function for each stream item, without affecting the result.
|
|
7
11
|
* Item is passed through to the output.
|
|
@@ -10,20 +14,21 @@ import { TransformOptions, TransformTyped } from '../stream.model'
|
|
|
10
14
|
*/
|
|
11
15
|
export function transformTap<IN>(
|
|
12
16
|
fn: AsyncMapper<IN, any>,
|
|
13
|
-
opt:
|
|
17
|
+
opt: TransformTapOptions = {},
|
|
14
18
|
): TransformTyped<IN, IN> {
|
|
15
|
-
|
|
19
|
+
const { logger = console } = opt
|
|
20
|
+
let index = -1
|
|
16
21
|
|
|
17
22
|
return new Transform({
|
|
18
23
|
objectMode: true,
|
|
19
24
|
...opt,
|
|
20
|
-
async transform(chunk: IN,
|
|
25
|
+
async transform(chunk: IN, _, cb) {
|
|
21
26
|
// console.log('tap', chunk)
|
|
22
27
|
|
|
23
28
|
try {
|
|
24
|
-
await fn(chunk, index
|
|
29
|
+
await fn(chunk, ++index)
|
|
25
30
|
} catch (err) {
|
|
26
|
-
|
|
31
|
+
logger.error(err)
|
|
27
32
|
// suppressed error
|
|
28
33
|
}
|
|
29
34
|
|
|
@@ -10,7 +10,7 @@ export function transformToArray<IN>(opt: TransformOptions = {}): TransformTyped
|
|
|
10
10
|
return new Transform({
|
|
11
11
|
objectMode: true,
|
|
12
12
|
...opt,
|
|
13
|
-
transform(chunk: IN,
|
|
13
|
+
transform(chunk: IN, _, cb) {
|
|
14
14
|
res.push(chunk)
|
|
15
15
|
// callback to signal that we processed input, but not emitting any output
|
|
16
16
|
cb()
|
|
@@ -14,8 +14,8 @@ export function transformToString(): TransformTyped<Buffer, string> {
|
|
|
14
14
|
return new Transform({
|
|
15
15
|
objectMode: false,
|
|
16
16
|
readableObjectMode: true,
|
|
17
|
-
transform(chunk: Buffer,
|
|
18
|
-
// console.log(`enc: ${
|
|
17
|
+
transform(chunk: Buffer, _, cb) {
|
|
18
|
+
// console.log(`enc: ${_}`, chunk.toString())
|
|
19
19
|
cb(null, chunk.toString())
|
|
20
20
|
},
|
|
21
21
|
})
|
|
@@ -115,7 +115,7 @@ export function transformMultiThreaded<IN, OUT>(
|
|
|
115
115
|
}
|
|
116
116
|
},
|
|
117
117
|
},
|
|
118
|
-
async function transformMapFn(chunk: IN,
|
|
118
|
+
async function transformMapFn(chunk: IN, _, cb) {
|
|
119
119
|
// Freezing the index, because it may change due to concurrency
|
|
120
120
|
const currentIndex = ++index
|
|
121
121
|
|
|
@@ -7,6 +7,8 @@ import { TransformOptions, WritableTyped } from '../stream.model'
|
|
|
7
7
|
* Currently does NOT (!) maintain backpressure.
|
|
8
8
|
* Error in the forked pipeline will propagate up to the main pipeline (and log error, to be sure).
|
|
9
9
|
* Will wait until all forked pipelines are completed before completing the stream.
|
|
10
|
+
*
|
|
11
|
+
* @experimental
|
|
10
12
|
*/
|
|
11
13
|
export function writableFork<T>(
|
|
12
14
|
chains: NodeJS.WritableStream[][],
|
|
@@ -29,7 +31,7 @@ export function writableFork<T>(
|
|
|
29
31
|
return new Writable({
|
|
30
32
|
objectMode: true,
|
|
31
33
|
...opt,
|
|
32
|
-
write(chunk: T,
|
|
34
|
+
write(chunk: T, _, cb) {
|
|
33
35
|
// Push/fork to all sub-streams
|
|
34
36
|
// No backpressure is ensured here, it'll push regardless of the
|
|
35
37
|
readables.forEach(readable => readable.push(chunk))
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Readable, Writable } from 'stream'
|
|
2
|
+
import { WritableTyped } from '../stream.model'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Allows to stop the Readable stream after the pipeline has processed X number of rows.
|
|
6
|
+
* It counts OUTPUT rows (not input), because this Writable is always at the end of the Pipeline.
|
|
7
|
+
* It ensures that everything has been processed before issuing a STOP on the readable.
|
|
8
|
+
*/
|
|
9
|
+
export function writableLimit<T>(readable: Readable, limit: number): WritableTyped<T> {
|
|
10
|
+
let i = 0
|
|
11
|
+
|
|
12
|
+
return new Writable({
|
|
13
|
+
objectMode: true,
|
|
14
|
+
write(chunk, _, cb) {
|
|
15
|
+
if (limit === 0) return cb() // no limit, just passthrough
|
|
16
|
+
|
|
17
|
+
i++
|
|
18
|
+
|
|
19
|
+
if (i === limit) {
|
|
20
|
+
console.log(`writableLimit of ${limit} reached`)
|
|
21
|
+
readable.destroy()
|
|
22
|
+
cb() // do we need it?
|
|
23
|
+
} else {
|
|
24
|
+
cb() // passthrough
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
}
|
|
@@ -8,7 +8,7 @@ export function writablePushToArray<IN>(arr: IN[], opt: TransformOptions = {}):
|
|
|
8
8
|
return new Writable({
|
|
9
9
|
objectMode: true,
|
|
10
10
|
...opt,
|
|
11
|
-
write(chunk: IN,
|
|
11
|
+
write(chunk: IN, _, cb) {
|
|
12
12
|
arr.push(chunk)
|
|
13
13
|
// callback to signal that we processed input, but not emitting any output
|
|
14
14
|
cb()
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import { Writable } from 'stream'
|
|
2
|
+
import { DeferredPromise } from '@naturalcycles/js-lib'
|
|
2
3
|
import { TransformOptions } from '../stream.model'
|
|
3
4
|
|
|
5
|
+
export interface WritableVoidOptions extends TransformOptions {
|
|
6
|
+
/**
|
|
7
|
+
* If set - it will be Resolved when the Stream is done (after final.cb)
|
|
8
|
+
*/
|
|
9
|
+
streamDone?: DeferredPromise
|
|
10
|
+
}
|
|
11
|
+
|
|
4
12
|
/**
|
|
5
13
|
* Use as a "null-terminator" of stream.pipeline.
|
|
6
14
|
* It consumes the stream as quickly as possible without doing anything.
|
|
7
15
|
* Put it in the end of your pipeline in case it ends with Transform that needs a consumer.
|
|
8
16
|
*/
|
|
9
|
-
export function writableVoid(opt
|
|
17
|
+
export function writableVoid(opt: WritableVoidOptions = {}): Writable {
|
|
10
18
|
return new Writable({
|
|
11
19
|
objectMode: true,
|
|
12
20
|
...opt,
|
|
13
|
-
write(chunk,
|
|
21
|
+
write(chunk, _, cb) {
|
|
22
|
+
cb()
|
|
23
|
+
},
|
|
24
|
+
final(cb) {
|
|
14
25
|
cb()
|
|
26
|
+
opt.streamDone?.resolve()
|
|
15
27
|
},
|
|
16
28
|
})
|
|
17
29
|
}
|
package/src/util/zip.util.ts
CHANGED
|
@@ -8,9 +8,10 @@ const gzip = promisify(zlib.gzip.bind(zlib))
|
|
|
8
8
|
const gunzip = promisify(zlib.gunzip.bind(zlib))
|
|
9
9
|
|
|
10
10
|
// string > zip
|
|
11
|
+
|
|
11
12
|
/**
|
|
12
13
|
* zipBuffer uses `deflate`.
|
|
13
|
-
* It's 9 bytes shorter than gzip
|
|
14
|
+
* It's 9 bytes shorter than `gzip`.
|
|
14
15
|
*/
|
|
15
16
|
export async function zipBuffer(buf: Buffer, options: ZlibOptions = {}): Promise<Buffer> {
|
|
16
17
|
return await deflate(buf, options)
|
|
@@ -18,7 +19,7 @@ export async function zipBuffer(buf: Buffer, options: ZlibOptions = {}): Promise
|
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* gzipBuffer uses `gzip`
|
|
21
|
-
* It's 9 bytes longer than deflate
|
|
22
|
+
* It's 9 bytes longer than `deflate`.
|
|
22
23
|
*/
|
|
23
24
|
export async function gzipBuffer(buf: Buffer, options: ZlibOptions = {}): Promise<Buffer> {
|
|
24
25
|
return await gzip(buf, options)
|
|
@@ -33,11 +34,18 @@ export async function gunzipBuffer(buf: Buffer, options: ZlibOptions = {}): Prom
|
|
|
33
34
|
return await gunzip(buf, options)
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
/**
|
|
38
|
+
* zipString uses `deflate`.
|
|
39
|
+
* It's 9 bytes shorter than `gzip`.
|
|
40
|
+
*/
|
|
37
41
|
export async function zipString(s: string, options?: ZlibOptions): Promise<Buffer> {
|
|
38
42
|
return await zipBuffer(Buffer.from(s), options)
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
/**
|
|
46
|
+
* gzipString uses `gzip`.
|
|
47
|
+
* It's 9 bytes longer than `deflate`.
|
|
48
|
+
*/
|
|
41
49
|
export async function gzipString(s: string, options?: ZlibOptions): Promise<Buffer> {
|
|
42
50
|
return await gzipBuffer(Buffer.from(s), options)
|
|
43
51
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { AsyncMapper } from '@naturalcycles/js-lib';
|
|
2
|
-
import { TransformTyped } from '../../stream.model';
|
|
3
|
-
import { TransformMapOptions } from '../transformMap';
|
|
4
|
-
export declare function notNullishPredicate(item: any): boolean;
|
|
5
|
-
/**
|
|
6
|
-
* Like pMap, but for streams.
|
|
7
|
-
* Inspired by `through2`.
|
|
8
|
-
* Main feature is concurrency control (implemented via `through2-concurrent`) and convenient options.
|
|
9
|
-
* Using this allows native stream .pipe() to work and use backpressure.
|
|
10
|
-
*
|
|
11
|
-
* Only works in objectMode (due to through2Concurrent).
|
|
12
|
-
*
|
|
13
|
-
* Concurrency defaults to 16.
|
|
14
|
-
*
|
|
15
|
-
* If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
|
|
16
|
-
*/
|
|
17
|
-
export declare function transformMapLegacy<IN = any, OUT = IN>(mapper: AsyncMapper<IN, OUT>, opt?: TransformMapOptions<IN, OUT>): TransformTyped<IN, OUT>;
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.transformMapLegacy = exports.notNullishPredicate = void 0;
|
|
4
|
-
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
|
-
const through2Concurrent = require("through2-concurrent");
|
|
6
|
-
const colors_1 = require("../../../colors");
|
|
7
|
-
function notNullishPredicate(item) {
|
|
8
|
-
return item !== undefined && item !== null;
|
|
9
|
-
}
|
|
10
|
-
exports.notNullishPredicate = notNullishPredicate;
|
|
11
|
-
/**
|
|
12
|
-
* Like pMap, but for streams.
|
|
13
|
-
* Inspired by `through2`.
|
|
14
|
-
* Main feature is concurrency control (implemented via `through2-concurrent`) and convenient options.
|
|
15
|
-
* Using this allows native stream .pipe() to work and use backpressure.
|
|
16
|
-
*
|
|
17
|
-
* Only works in objectMode (due to through2Concurrent).
|
|
18
|
-
*
|
|
19
|
-
* Concurrency defaults to 16.
|
|
20
|
-
*
|
|
21
|
-
* If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
|
|
22
|
-
*/
|
|
23
|
-
function transformMapLegacy(mapper, opt = {}) {
|
|
24
|
-
const { concurrency = 16, predicate = notNullishPredicate, errorMode = js_lib_1.ErrorMode.THROW_IMMEDIATELY, flattenArrayOutput, onError, beforeFinal, metric = 'stream', logger = console, } = opt;
|
|
25
|
-
let index = -1;
|
|
26
|
-
let isRejected = false;
|
|
27
|
-
let errors = 0;
|
|
28
|
-
const collectedErrors = []; // only used if errorMode == THROW_AGGREGATED
|
|
29
|
-
return through2Concurrent.obj({
|
|
30
|
-
maxConcurrency: concurrency,
|
|
31
|
-
// autoDestroy: true,
|
|
32
|
-
async final(cb) {
|
|
33
|
-
// console.log('transformMap final')
|
|
34
|
-
logErrorStats(logger, true);
|
|
35
|
-
await beforeFinal?.(); // call beforeFinal if defined
|
|
36
|
-
if (collectedErrors.length) {
|
|
37
|
-
// emit Aggregated error
|
|
38
|
-
cb(new js_lib_1.AggregatedError(collectedErrors));
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
// emit no error
|
|
42
|
-
cb();
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
}, async function transformMapFn(chunk, _encoding, cb) {
|
|
46
|
-
index++;
|
|
47
|
-
// console.log({chunk, _encoding})
|
|
48
|
-
// Stop processing if THROW_IMMEDIATELY mode is used
|
|
49
|
-
if (isRejected && errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY)
|
|
50
|
-
return cb();
|
|
51
|
-
try {
|
|
52
|
-
const currentIndex = index; // because we need to pass it to 2 functions - mapper and predicate. Refers to INPUT index (since it may return multiple outputs)
|
|
53
|
-
const res = await mapper(chunk, currentIndex);
|
|
54
|
-
const passedResults = await (0, js_lib_1.pFilter)(flattenArrayOutput && Array.isArray(res) ? res : [res], async (r) => await predicate(r, currentIndex));
|
|
55
|
-
if (passedResults.length === 0) {
|
|
56
|
-
cb(); // 0 results
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
passedResults.forEach(r => {
|
|
60
|
-
this.push(r);
|
|
61
|
-
// cb(null, r)
|
|
62
|
-
});
|
|
63
|
-
cb(); // done processing
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
logger.error(err);
|
|
68
|
-
errors++;
|
|
69
|
-
logErrorStats(logger);
|
|
70
|
-
if (onError) {
|
|
71
|
-
try {
|
|
72
|
-
onError(err, chunk);
|
|
73
|
-
}
|
|
74
|
-
catch { }
|
|
75
|
-
}
|
|
76
|
-
if (errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY) {
|
|
77
|
-
isRejected = true;
|
|
78
|
-
// Emit error immediately
|
|
79
|
-
return cb(err);
|
|
80
|
-
}
|
|
81
|
-
if (errorMode === js_lib_1.ErrorMode.THROW_AGGREGATED) {
|
|
82
|
-
collectedErrors.push(err);
|
|
83
|
-
}
|
|
84
|
-
// Tell input stream that we're done processing, but emit nothing to output - not error nor result
|
|
85
|
-
cb();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
function logErrorStats(logger, final = false) {
|
|
89
|
-
if (!errors)
|
|
90
|
-
return;
|
|
91
|
-
logger.log(`${metric} ${final ? 'final ' : ''}errors: ${(0, colors_1.yellow)(errors)}`);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
exports.transformMapLegacy = transformMapLegacy;
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { Transform } from 'stream'
|
|
2
|
-
import {
|
|
3
|
-
AggregatedError,
|
|
4
|
-
AsyncMapper,
|
|
5
|
-
CommonLogger,
|
|
6
|
-
ErrorMode,
|
|
7
|
-
pFilter,
|
|
8
|
-
} from '@naturalcycles/js-lib'
|
|
9
|
-
import through2Concurrent = require('through2-concurrent')
|
|
10
|
-
import { yellow } from '../../../colors'
|
|
11
|
-
import { TransformTyped } from '../../stream.model'
|
|
12
|
-
import { TransformMapOptions } from '../transformMap'
|
|
13
|
-
|
|
14
|
-
export function notNullishPredicate(item: any): boolean {
|
|
15
|
-
return item !== undefined && item !== null
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Like pMap, but for streams.
|
|
20
|
-
* Inspired by `through2`.
|
|
21
|
-
* Main feature is concurrency control (implemented via `through2-concurrent`) and convenient options.
|
|
22
|
-
* Using this allows native stream .pipe() to work and use backpressure.
|
|
23
|
-
*
|
|
24
|
-
* Only works in objectMode (due to through2Concurrent).
|
|
25
|
-
*
|
|
26
|
-
* Concurrency defaults to 16.
|
|
27
|
-
*
|
|
28
|
-
* If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
|
|
29
|
-
*/
|
|
30
|
-
export function transformMapLegacy<IN = any, OUT = IN>(
|
|
31
|
-
mapper: AsyncMapper<IN, OUT>,
|
|
32
|
-
opt: TransformMapOptions<IN, OUT> = {},
|
|
33
|
-
): TransformTyped<IN, OUT> {
|
|
34
|
-
const {
|
|
35
|
-
concurrency = 16,
|
|
36
|
-
predicate = notNullishPredicate,
|
|
37
|
-
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
38
|
-
flattenArrayOutput,
|
|
39
|
-
onError,
|
|
40
|
-
beforeFinal,
|
|
41
|
-
metric = 'stream',
|
|
42
|
-
logger = console,
|
|
43
|
-
} = opt
|
|
44
|
-
|
|
45
|
-
let index = -1
|
|
46
|
-
let isRejected = false
|
|
47
|
-
let errors = 0
|
|
48
|
-
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
49
|
-
|
|
50
|
-
return through2Concurrent.obj(
|
|
51
|
-
{
|
|
52
|
-
maxConcurrency: concurrency,
|
|
53
|
-
// autoDestroy: true,
|
|
54
|
-
async final(cb) {
|
|
55
|
-
// console.log('transformMap final')
|
|
56
|
-
|
|
57
|
-
logErrorStats(logger, true)
|
|
58
|
-
|
|
59
|
-
await beforeFinal?.() // call beforeFinal if defined
|
|
60
|
-
|
|
61
|
-
if (collectedErrors.length) {
|
|
62
|
-
// emit Aggregated error
|
|
63
|
-
cb(new AggregatedError(collectedErrors))
|
|
64
|
-
} else {
|
|
65
|
-
// emit no error
|
|
66
|
-
cb()
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
async function transformMapFn(
|
|
71
|
-
this: Transform,
|
|
72
|
-
chunk: IN,
|
|
73
|
-
_encoding: any,
|
|
74
|
-
cb: (...args: any[]) => any,
|
|
75
|
-
) {
|
|
76
|
-
index++
|
|
77
|
-
// console.log({chunk, _encoding})
|
|
78
|
-
|
|
79
|
-
// Stop processing if THROW_IMMEDIATELY mode is used
|
|
80
|
-
if (isRejected && errorMode === ErrorMode.THROW_IMMEDIATELY) return cb()
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const currentIndex = index // because we need to pass it to 2 functions - mapper and predicate. Refers to INPUT index (since it may return multiple outputs)
|
|
84
|
-
const res = await mapper(chunk, currentIndex)
|
|
85
|
-
const passedResults = await pFilter(
|
|
86
|
-
flattenArrayOutput && Array.isArray(res) ? res : [res],
|
|
87
|
-
async r => await predicate(r, currentIndex),
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
if (passedResults.length === 0) {
|
|
91
|
-
cb() // 0 results
|
|
92
|
-
} else {
|
|
93
|
-
passedResults.forEach(r => {
|
|
94
|
-
this.push(r)
|
|
95
|
-
// cb(null, r)
|
|
96
|
-
})
|
|
97
|
-
cb() // done processing
|
|
98
|
-
}
|
|
99
|
-
} catch (err) {
|
|
100
|
-
logger.error(err)
|
|
101
|
-
|
|
102
|
-
errors++
|
|
103
|
-
|
|
104
|
-
logErrorStats(logger)
|
|
105
|
-
|
|
106
|
-
if (onError) {
|
|
107
|
-
try {
|
|
108
|
-
onError(err, chunk)
|
|
109
|
-
} catch {}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
113
|
-
isRejected = true
|
|
114
|
-
// Emit error immediately
|
|
115
|
-
return cb(err)
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (errorMode === ErrorMode.THROW_AGGREGATED) {
|
|
119
|
-
collectedErrors.push(err as Error)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Tell input stream that we're done processing, but emit nothing to output - not error nor result
|
|
123
|
-
cb()
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
function logErrorStats(logger: CommonLogger, final = false): void {
|
|
129
|
-
if (!errors) return
|
|
130
|
-
|
|
131
|
-
logger.log(`${metric} ${final ? 'final ' : ''}errors: ${yellow(errors)}`)
|
|
132
|
-
}
|
|
133
|
-
}
|