@naturalcycles/nodejs-lib 12.57.0 → 12.61.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/got/getGot.js +98 -28
- package/dist/got/got.model.d.ts +6 -0
- package/dist/index.d.ts +37 -36
- package/dist/index.js +36 -129
- 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 +32 -1
- package/dist/stream/transform/transformLimit.js +33 -16
- package/dist/stream/transform/transformLogProgress.d.ts +20 -0
- package/dist/stream/transform/transformLogProgress.js +36 -18
- package/dist/stream/transform/transformMap.d.ts +4 -10
- package/dist/stream/transform/transformMap.js +52 -64
- package/dist/stream/transform/transformMapSimple.js +1 -1
- package/dist/stream/transform/transformMapSync.d.ts +5 -3
- package/dist/stream/transform/transformMapSync.js +28 -22
- package/dist/stream/transform/transformNoOp.js +1 -1
- package/dist/stream/transform/transformTap.js +3 -3
- 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.js +1 -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/got/getGot.ts +120 -31
- package/src/got/got.model.ts +8 -0
- package/src/index.ts +36 -194
- 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 +66 -20
- package/src/stream/transform/transformLogProgress.ts +72 -23
- package/src/stream/transform/transformMap.ts +74 -94
- package/src/stream/transform/transformMapSimple.ts +1 -1
- package/src/stream/transform/transformMapSync.ts +40 -26
- package/src/stream/transform/transformNoOp.ts +1 -1
- package/src/stream/transform/transformTap.ts +3 -3
- 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 +1 -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
|
@@ -4,6 +4,7 @@ import { SimpleMovingAverage, _mb, _since, AnyObject, CommonLogger } from '@natu
|
|
|
4
4
|
import { dayjs } from '@naturalcycles/time-lib'
|
|
5
5
|
import { boldWhite, dimGrey, white, yellow } from '../../colors'
|
|
6
6
|
import { hasColors } from '../../colors/colors'
|
|
7
|
+
import { SizeStack } from '../sizeStack'
|
|
7
8
|
import { TransformOptions, TransformTyped } from '../stream.model'
|
|
8
9
|
|
|
9
10
|
export interface TransformLogProgressOptions<IN = any> extends TransformOptions {
|
|
@@ -103,6 +104,41 @@ export interface TransformLogProgressOptions<IN = any> extends TransformOptions
|
|
|
103
104
|
* Defaults to 1.
|
|
104
105
|
*/
|
|
105
106
|
batchSize?: number
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Experimental logging of item (shunk) sizes, when json-stringified.
|
|
110
|
+
*
|
|
111
|
+
* Defaults to false.
|
|
112
|
+
*
|
|
113
|
+
* @experimental
|
|
114
|
+
*/
|
|
115
|
+
logSizes?: boolean
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* How many last item sizes to keep in a buffer, to calculate stats (p50, p90, avg, etc).
|
|
119
|
+
* Defaults to 100_000.
|
|
120
|
+
* Cannot be Infinity.
|
|
121
|
+
*/
|
|
122
|
+
logSizesBuffer?: number
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Works in addition to `logSizes`. Adds "zipped sizes".
|
|
126
|
+
*
|
|
127
|
+
* @experimental
|
|
128
|
+
*/
|
|
129
|
+
logZippedSizes?: boolean
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface LogItem extends AnyObject {
|
|
133
|
+
heapUsed?: number
|
|
134
|
+
heapTotal?: number
|
|
135
|
+
rss?: number
|
|
136
|
+
peakRSS?: number
|
|
137
|
+
rssMinusHeap?: number
|
|
138
|
+
external?: number
|
|
139
|
+
arrayBuffers?: number
|
|
140
|
+
rps10?: number
|
|
141
|
+
rpsTotal?: number
|
|
106
142
|
}
|
|
107
143
|
|
|
108
144
|
const inspectOpt: InspectOptions = {
|
|
@@ -124,6 +160,9 @@ export function transformLogProgress<IN = any>(
|
|
|
124
160
|
peakRSS: logPeakRSS = true,
|
|
125
161
|
logRPS = true,
|
|
126
162
|
logEvery = 1000,
|
|
163
|
+
logSizes = false,
|
|
164
|
+
logSizesBuffer = 100_000,
|
|
165
|
+
logZippedSizes = false,
|
|
127
166
|
batchSize = 1,
|
|
128
167
|
extra,
|
|
129
168
|
logger = console,
|
|
@@ -138,15 +177,23 @@ export function transformLogProgress<IN = any>(
|
|
|
138
177
|
let progress = 0
|
|
139
178
|
let peakRSS = 0
|
|
140
179
|
|
|
180
|
+
const sizes = logSizes ? new SizeStack('json', logSizesBuffer) : undefined
|
|
181
|
+
const sizesZipped = logZippedSizes ? new SizeStack('json.gz', logSizesBuffer) : undefined
|
|
182
|
+
|
|
141
183
|
logStats() // initial
|
|
142
184
|
|
|
143
185
|
return new Transform({
|
|
144
186
|
objectMode: true,
|
|
145
187
|
...opt,
|
|
146
|
-
transform(chunk: IN,
|
|
188
|
+
transform(chunk: IN, _, cb) {
|
|
147
189
|
progress++
|
|
148
190
|
processedLastSecond++
|
|
149
191
|
|
|
192
|
+
if (sizes) {
|
|
193
|
+
// Check it, cause gzipping might be delayed here..
|
|
194
|
+
void SizeStack.countItem(chunk, logger, sizes, sizesZipped)
|
|
195
|
+
}
|
|
196
|
+
|
|
150
197
|
if (logProgress && progress % logEvery === 0) {
|
|
151
198
|
logStats(chunk, false, progress % logEvery10 === 0)
|
|
152
199
|
}
|
|
@@ -175,28 +222,30 @@ export function transformLogProgress<IN = any>(
|
|
|
175
222
|
const rps10 = Math.round(sma.push(lastRPS))
|
|
176
223
|
if (mem.rss > peakRSS) peakRSS = mem.rss
|
|
177
224
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
199
|
-
|
|
225
|
+
const o: LogItem = {
|
|
226
|
+
[final ? `${metric}_final` : metric]: batchedProgress,
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (extra) Object.assign(o, extra(chunk, progress))
|
|
230
|
+
if (logHeapUsed) o.heapUsed = _mb(mem.heapUsed)
|
|
231
|
+
if (logHeapTotal) o.heapTotal = _mb(mem.heapTotal)
|
|
232
|
+
if (logRss) o.rss = _mb(mem.rss)
|
|
233
|
+
if (logPeakRSS) o.peakRSS = _mb(peakRSS)
|
|
234
|
+
if (opt.rssMinusHeap) o.rssMinusHeap = _mb(mem.rss - mem.heapTotal)
|
|
235
|
+
if (opt.external) o.external = _mb(mem.external)
|
|
236
|
+
if (opt.arrayBuffers) o.arrayBuffers = _mb(mem.arrayBuffers || 0)
|
|
237
|
+
|
|
238
|
+
if (logRPS) Object.assign(o, { rps10, rpsTotal })
|
|
239
|
+
|
|
240
|
+
logger.log(inspect(o, inspectOpt))
|
|
241
|
+
|
|
242
|
+
if (sizes?.items.length) {
|
|
243
|
+
logger.log(sizes.getStats())
|
|
244
|
+
|
|
245
|
+
if (sizesZipped?.items.length) {
|
|
246
|
+
logger.log(sizesZipped.getStats())
|
|
247
|
+
}
|
|
248
|
+
}
|
|
200
249
|
|
|
201
250
|
if (tenx) {
|
|
202
251
|
let perHour: number | string =
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import { Transform } from 'stream'
|
|
2
1
|
import {
|
|
2
|
+
AbortableAsyncMapper,
|
|
3
3
|
AggregatedError,
|
|
4
|
-
AsyncMapper,
|
|
5
4
|
AsyncPredicate,
|
|
6
5
|
CommonLogger,
|
|
6
|
+
END,
|
|
7
7
|
ErrorMode,
|
|
8
8
|
pFilter,
|
|
9
|
-
|
|
9
|
+
SKIP,
|
|
10
10
|
} from '@naturalcycles/js-lib'
|
|
11
|
+
import through2Concurrent = require('through2-concurrent')
|
|
11
12
|
import { yellow } from '../../colors'
|
|
13
|
+
import { AbortableTransform } from '../pipeline/pipeline'
|
|
12
14
|
import { TransformTyped } from '../stream.model'
|
|
15
|
+
import { pipelineClose } from '../stream.util'
|
|
13
16
|
|
|
14
17
|
export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
15
18
|
/**
|
|
@@ -23,9 +26,8 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
23
26
|
* Predicate to filter outgoing results (after mapper).
|
|
24
27
|
* Allows to not emit all results.
|
|
25
28
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* @default to filter out undefined/null values, but pass anything else
|
|
29
|
+
* Defaults to "pass everything" (including null, undefined, etc).
|
|
30
|
+
* Simpler way to exclude certain cases is to return SKIP symbol from the mapper.
|
|
29
31
|
*/
|
|
30
32
|
predicate?: AsyncPredicate<OUT>
|
|
31
33
|
|
|
@@ -54,17 +56,11 @@ export interface TransformMapOptions<IN = any, OUT = IN> {
|
|
|
54
56
|
*/
|
|
55
57
|
metric?: string
|
|
56
58
|
|
|
57
|
-
/**
|
|
58
|
-
* If defined - called BEFORE `final()` callback is called.
|
|
59
|
-
*/
|
|
60
|
-
beforeFinal?: () => any
|
|
61
|
-
|
|
62
59
|
logger?: CommonLogger
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
62
|
+
// doesn't work, cause here we don't construct our Transform instance ourselves
|
|
63
|
+
// export class TransformMap extends AbortableTransform {}
|
|
68
64
|
|
|
69
65
|
/**
|
|
70
66
|
* Like pMap, but for streams.
|
|
@@ -79,112 +75,96 @@ export function notNullishPredicate(item: any): boolean {
|
|
|
79
75
|
* If an Array is returned by `mapper` - it will be flattened and multiple results will be emitted from it. Tested by Array.isArray().
|
|
80
76
|
*/
|
|
81
77
|
export function transformMap<IN = any, OUT = IN>(
|
|
82
|
-
mapper:
|
|
78
|
+
mapper: AbortableAsyncMapper<IN, OUT>,
|
|
83
79
|
opt: TransformMapOptions<IN, OUT> = {},
|
|
84
80
|
): TransformTyped<IN, OUT> {
|
|
85
81
|
const {
|
|
86
82
|
concurrency = 16,
|
|
87
|
-
predicate
|
|
83
|
+
predicate, // we now default to "no predicate" (meaning pass-everything)
|
|
88
84
|
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
89
85
|
flattenArrayOutput,
|
|
90
86
|
onError,
|
|
91
|
-
beforeFinal,
|
|
92
87
|
metric = 'stream',
|
|
93
88
|
logger = console,
|
|
94
89
|
} = opt
|
|
95
90
|
|
|
96
91
|
let index = -1
|
|
97
|
-
let
|
|
92
|
+
let isSettled = false
|
|
98
93
|
let errors = 0
|
|
99
94
|
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
100
95
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
return new Transform({
|
|
108
|
-
objectMode: true,
|
|
96
|
+
return through2Concurrent.obj(
|
|
97
|
+
{
|
|
98
|
+
maxConcurrency: concurrency,
|
|
99
|
+
async final(cb) {
|
|
100
|
+
// console.log('transformMap final')
|
|
109
101
|
|
|
110
|
-
|
|
111
|
-
// console.log('transformMap final', {index}, q.inFlight, q.queueSize)
|
|
102
|
+
logErrorStats(true)
|
|
112
103
|
|
|
113
|
-
|
|
114
|
-
|
|
104
|
+
if (collectedErrors.length) {
|
|
105
|
+
// emit Aggregated error
|
|
106
|
+
cb(new AggregatedError(collectedErrors))
|
|
107
|
+
} else {
|
|
108
|
+
// emit no error
|
|
109
|
+
cb()
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
async function transformMapFn(this: AbortableTransform, chunk: IN, _, cb) {
|
|
114
|
+
// Stop processing if isSettled (either THROW_IMMEDIATELY was fired or END received)
|
|
115
|
+
if (isSettled) return cb()
|
|
116
|
+
|
|
117
|
+
const currentIndex = ++index
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const res = await mapper(chunk, currentIndex)
|
|
121
|
+
const passedResults = await pFilter(
|
|
122
|
+
flattenArrayOutput && Array.isArray(res) ? res : [res],
|
|
123
|
+
async r => {
|
|
124
|
+
if (r === END) {
|
|
125
|
+
isSettled = true // will be checked later
|
|
126
|
+
return false
|
|
127
|
+
}
|
|
128
|
+
return r !== SKIP && (!predicate || (await predicate(r, currentIndex)))
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
passedResults.forEach(r => this.push(r))
|
|
133
|
+
|
|
134
|
+
if (isSettled) {
|
|
135
|
+
logger.log(`transformMap END received at index ${currentIndex}`)
|
|
136
|
+
pipelineClose('transformMap', this, this.sourceReadable, this.streamDone, logger)
|
|
137
|
+
}
|
|
115
138
|
|
|
116
|
-
|
|
139
|
+
cb() // done processing
|
|
140
|
+
} catch (err) {
|
|
141
|
+
logger.error(err)
|
|
142
|
+
errors++
|
|
143
|
+
logErrorStats()
|
|
117
144
|
|
|
118
|
-
|
|
145
|
+
if (onError) {
|
|
146
|
+
try {
|
|
147
|
+
onError(err, chunk)
|
|
148
|
+
} catch {}
|
|
149
|
+
}
|
|
119
150
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
cb(new AggregatedError(collectedErrors))
|
|
125
|
-
} else {
|
|
126
|
-
// emit no error
|
|
127
|
-
// It is truly a mistery, but calling cb() here was causing ERR_MULTIPLE_CALLBACK ?!
|
|
128
|
-
// Commenting it out seems to work ?!
|
|
129
|
-
// ?!
|
|
130
|
-
// cb()
|
|
131
|
-
}
|
|
132
|
-
},
|
|
151
|
+
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
152
|
+
isSettled = true
|
|
153
|
+
return cb(err) // Emit error immediately
|
|
154
|
+
}
|
|
133
155
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// console.log('transform', {index})
|
|
137
|
-
|
|
138
|
-
// Stop processing if THROW_IMMEDIATELY mode is used
|
|
139
|
-
if (isRejected && errorMode === ErrorMode.THROW_IMMEDIATELY) return cb()
|
|
140
|
-
|
|
141
|
-
// It resolves when it is successfully STARTED execution.
|
|
142
|
-
// If it's queued instead - it'll wait and resolve only upon START.
|
|
143
|
-
await q.push(async () => {
|
|
144
|
-
try {
|
|
145
|
-
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)
|
|
146
|
-
const res = await mapper(chunk, currentIndex)
|
|
147
|
-
const passedResults = await pFilter(
|
|
148
|
-
flattenArrayOutput && Array.isArray(res) ? res : [res],
|
|
149
|
-
async r => await predicate(r, currentIndex),
|
|
150
|
-
)
|
|
151
|
-
|
|
152
|
-
passedResults.forEach(r => this.push(r))
|
|
153
|
-
} catch (err) {
|
|
154
|
-
logger.error(err)
|
|
155
|
-
|
|
156
|
-
errors++
|
|
157
|
-
|
|
158
|
-
logErrorStats(logger)
|
|
159
|
-
|
|
160
|
-
if (onError) {
|
|
161
|
-
try {
|
|
162
|
-
onError(err, chunk)
|
|
163
|
-
} catch {}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
167
|
-
isRejected = true
|
|
168
|
-
// Emit error immediately
|
|
169
|
-
// return cb(err as Error)
|
|
170
|
-
return this.emit('error', err as Error)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (errorMode === ErrorMode.THROW_AGGREGATED) {
|
|
174
|
-
collectedErrors.push(err as Error)
|
|
175
|
-
}
|
|
156
|
+
if (errorMode === ErrorMode.THROW_AGGREGATED) {
|
|
157
|
+
collectedErrors.push(err as Error)
|
|
176
158
|
}
|
|
177
|
-
})
|
|
178
159
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
160
|
+
// Tell input stream that we're done processing, but emit nothing to output - not error nor result
|
|
161
|
+
cb()
|
|
162
|
+
}
|
|
182
163
|
},
|
|
183
|
-
|
|
164
|
+
)
|
|
184
165
|
|
|
185
|
-
function logErrorStats(
|
|
166
|
+
function logErrorStats(final = false): void {
|
|
186
167
|
if (!errors) return
|
|
187
|
-
|
|
188
168
|
logger.log(`${metric} ${final ? 'final ' : ''}errors: ${yellow(errors)}`)
|
|
189
169
|
}
|
|
190
170
|
}
|
|
@@ -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
|
|
|
@@ -47,6 +54,8 @@ export interface TransformMapSyncOptions<IN = any, OUT = IN> {
|
|
|
47
54
|
logger?: CommonLogger
|
|
48
55
|
}
|
|
49
56
|
|
|
57
|
+
export class TransformMapSync extends AbortableTransform {}
|
|
58
|
+
|
|
50
59
|
/**
|
|
51
60
|
* Sync (not async) version of transformMap.
|
|
52
61
|
* Supposedly faster, for cases when async is not needed.
|
|
@@ -58,7 +67,7 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
58
67
|
let index = -1
|
|
59
68
|
|
|
60
69
|
const {
|
|
61
|
-
predicate
|
|
70
|
+
predicate, // defaults to "no predicate" (pass everything)
|
|
62
71
|
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
63
72
|
flattenArrayOutput = false,
|
|
64
73
|
onError,
|
|
@@ -66,34 +75,39 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
66
75
|
objectMode = true,
|
|
67
76
|
logger = console,
|
|
68
77
|
} = opt
|
|
69
|
-
let
|
|
78
|
+
let isSettled = false
|
|
70
79
|
let errors = 0
|
|
71
80
|
const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
|
|
72
81
|
|
|
73
|
-
return new
|
|
82
|
+
return new TransformMapSync({
|
|
74
83
|
objectMode,
|
|
75
84
|
...opt,
|
|
76
|
-
transform(this:
|
|
77
|
-
// Stop processing if
|
|
78
|
-
if (
|
|
79
|
-
return cb()
|
|
80
|
-
}
|
|
85
|
+
transform(this: AbortableTransform, chunk: IN, _, cb) {
|
|
86
|
+
// Stop processing if isSettled
|
|
87
|
+
if (isSettled) return cb()
|
|
81
88
|
|
|
82
|
-
|
|
83
|
-
if (!predicate(chunk, ++index)) {
|
|
84
|
-
cb() // signal that we've finished processing, but emit no output here
|
|
85
|
-
return
|
|
86
|
-
}
|
|
89
|
+
const currentIndex = ++index
|
|
87
90
|
|
|
91
|
+
try {
|
|
88
92
|
// map and pass through
|
|
89
|
-
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
|
+
})
|
|
90
102
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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)
|
|
96
108
|
}
|
|
109
|
+
|
|
110
|
+
cb() // done processing
|
|
97
111
|
} catch (err) {
|
|
98
112
|
logger.error(err)
|
|
99
113
|
errors++
|
|
@@ -107,7 +121,7 @@ export function transformMapSync<IN = any, OUT = IN>(
|
|
|
107
121
|
}
|
|
108
122
|
|
|
109
123
|
if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
|
|
110
|
-
|
|
124
|
+
isSettled = true
|
|
111
125
|
// Emit error immediately
|
|
112
126
|
return cb(err as Error)
|
|
113
127
|
}
|
|
@@ -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
|
})
|
|
@@ -17,16 +17,16 @@ export function transformTap<IN>(
|
|
|
17
17
|
opt: TransformTapOptions = {},
|
|
18
18
|
): TransformTyped<IN, IN> {
|
|
19
19
|
const { logger = console } = opt
|
|
20
|
-
let index =
|
|
20
|
+
let index = -1
|
|
21
21
|
|
|
22
22
|
return new Transform({
|
|
23
23
|
objectMode: true,
|
|
24
24
|
...opt,
|
|
25
|
-
async transform(chunk: IN,
|
|
25
|
+
async transform(chunk: IN, _, cb) {
|
|
26
26
|
// console.log('tap', chunk)
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
|
-
await fn(chunk, index
|
|
29
|
+
await fn(chunk, ++index)
|
|
30
30
|
} catch (err) {
|
|
31
31
|
logger.error(err)
|
|
32
32
|
// suppressed error
|
|
@@ -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
|
|
|
@@ -31,7 +31,7 @@ export function writableFork<T>(
|
|
|
31
31
|
return new Writable({
|
|
32
32
|
objectMode: true,
|
|
33
33
|
...opt,
|
|
34
|
-
write(chunk: T,
|
|
34
|
+
write(chunk: T, _, cb) {
|
|
35
35
|
// Push/fork to all sub-streams
|
|
36
36
|
// No backpressure is ensured here, it'll push regardless of the
|
|
37
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
|
}
|