@naturalcycles/nodejs-lib 15.27.3 → 15.28.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.
@@ -5,8 +5,8 @@ export * from './ndjson/transformJsonParse.js';
5
5
  export * from './ndjson/transformToNDJson.js';
6
6
  export * from './pipeline.js';
7
7
  export * from './progressLogger.js';
8
+ export * from './readable/createReadable.js';
8
9
  export * from './readable/readableCombined.js';
9
- export * from './readable/readableCreate.js';
10
10
  export * from './readable/readableFromArray.js';
11
11
  export * from './stream.model.js';
12
12
  export * from './transform/transformChunk.js';
@@ -5,8 +5,8 @@ export * from './ndjson/transformJsonParse.js';
5
5
  export * from './ndjson/transformToNDJson.js';
6
6
  export * from './pipeline.js';
7
7
  export * from './progressLogger.js';
8
+ export * from './readable/createReadable.js';
8
9
  export * from './readable/readableCombined.js';
9
- export * from './readable/readableCreate.js';
10
10
  export * from './readable/readableFromArray.js';
11
11
  export * from './stream.model.js';
12
12
  export * from './transform/transformChunk.js';
@@ -8,9 +8,8 @@ import { type TransformMapOptions } from './transform/transformMap.js';
8
8
  import { type TransformMapSimpleOptions } from './transform/transformMapSimple.js';
9
9
  import { type TransformMapSyncOptions } from './transform/transformMapSync.js';
10
10
  import { type TransformOffsetOptions } from './transform/transformOffset.js';
11
- import { type TransformTapOptions } from './transform/transformTap.js';
12
11
  import { type TransformThrottleOptions } from './transform/transformThrottle.js';
13
- export declare class Pipeline<T> {
12
+ export declare class Pipeline<T = unknown> {
14
13
  private readonly source;
15
14
  private transforms;
16
15
  private destination?;
@@ -19,6 +18,12 @@ export declare class Pipeline<T> {
19
18
  private abortableSignal;
20
19
  private constructor();
21
20
  static from<T>(source: ReadableTyped<T>): Pipeline<T>;
21
+ /**
22
+ * Useful in cases when Readable is not immediately available,
23
+ * but only available after an async operation is completed.
24
+ * Implemented via a proxy Transform, which should be transparent.
25
+ */
26
+ static fromAsyncReadable<T = unknown>(fn: () => Promise<ReadableTyped<T>>): Pipeline<T>;
22
27
  static fromWeb<T>(webReadableStream: WebReadableStream<T>): Pipeline<T>;
23
28
  /**
24
29
  * Technically same as `fromIterable` (since Array is Iterable),
@@ -61,7 +66,8 @@ export declare class Pipeline<T> {
61
66
  filter(predicate: AsyncPredicate<T>, opt?: TransformMapOptions): this;
62
67
  filterSync(predicate: Predicate<T>, opt?: TransformOptions): this;
63
68
  offset(opt: TransformOffsetOptions): this;
64
- tap(fn: AsyncIndexedMapper<T, any>, opt?: TransformTapOptions): this;
69
+ tap(fn: AsyncIndexedMapper<T, any>, opt?: TransformOptions): this;
70
+ tapSync(fn: IndexedMapper<T, any>, opt?: TransformOptions): this;
65
71
  throttle(opt: TransformThrottleOptions): this;
66
72
  transform<TO>(transform: TransformTyped<T, TO>): Pipeline<TO>;
67
73
  /**
@@ -7,6 +7,7 @@ import { fs2 } from '../fs/fs2.js';
7
7
  import { createReadStreamAsNDJson } from './ndjson/createReadStreamAsNDJson.js';
8
8
  import { transformJsonParse } from './ndjson/transformJsonParse.js';
9
9
  import { transformToNDJson } from './ndjson/transformToNDJson.js';
10
+ import { createReadableFromAsync } from './readable/createReadable.js';
10
11
  import { PIPELINE_GRACEFUL_ABORT } from './stream.util.js';
11
12
  import { transformChunk } from './transform/transformChunk.js';
12
13
  import { transformFilterSync } from './transform/transformFilter.js';
@@ -19,7 +20,7 @@ import { transformMapSimple, } from './transform/transformMapSimple.js';
19
20
  import { transformMapSync } from './transform/transformMapSync.js';
20
21
  import { transformOffset } from './transform/transformOffset.js';
21
22
  import { transformSplitOnNewline } from './transform/transformSplit.js';
22
- import { transformTap } from './transform/transformTap.js';
23
+ import { transformTap, transformTapSync } from './transform/transformTap.js';
23
24
  import { transformThrottle } from './transform/transformThrottle.js';
24
25
  import { writablePushToArray } from './writable/writablePushToArray.js';
25
26
  import { writableVoid } from './writable/writableVoid.js';
@@ -39,6 +40,14 @@ export class Pipeline {
39
40
  static from(source) {
40
41
  return new Pipeline(source);
41
42
  }
43
+ /**
44
+ * Useful in cases when Readable is not immediately available,
45
+ * but only available after an async operation is completed.
46
+ * Implemented via a proxy Transform, which should be transparent.
47
+ */
48
+ static fromAsyncReadable(fn) {
49
+ return new Pipeline(createReadableFromAsync(fn));
50
+ }
42
51
  static fromWeb(webReadableStream) {
43
52
  return new Pipeline(Readable.fromWeb(webReadableStream));
44
53
  }
@@ -152,6 +161,10 @@ export class Pipeline {
152
161
  this.transforms.push(transformTap(fn, opt));
153
162
  return this;
154
163
  }
164
+ tapSync(fn, opt) {
165
+ this.transforms.push(transformTapSync(fn, opt));
166
+ return this;
167
+ }
155
168
  throttle(opt) {
156
169
  this.transforms.push(transformThrottle(opt));
157
170
  return this;
@@ -1,4 +1,4 @@
1
- import type { ReadableOptions } from 'node:stream';
1
+ import { type ReadableOptions } from 'node:stream';
2
2
  import type { ReadableTyped } from '../stream.model.js';
3
3
  /**
4
4
  * Convenience function to create a Readable that can be pushed into (similar to RxJS Subject).
@@ -12,8 +12,14 @@ import type { ReadableTyped } from '../stream.model.js';
12
12
  * if read() will be called AFTER everything was pushed and Readable is closed (by pushing `null`).
13
13
  * Beware of it when e.g doing unit testing! Jest prefers to hang (not exit-0).
14
14
  */
15
- export declare function readableCreate<T>(items?: Iterable<T>, opt?: ReadableOptions, onRead?: () => void): ReadableTyped<T>;
15
+ export declare function createReadable<T>(items?: Iterable<T>, opt?: ReadableOptions, onRead?: () => void): ReadableTyped<T>;
16
16
  /**
17
17
  * Convenience type-safe wrapper around Readable.from() that infers the Type of input.
18
18
  */
19
- export declare function readableFrom<T>(iterable: Iterable<T> | AsyncIterable<T>, opt?: ReadableOptions): ReadableTyped<T>;
19
+ export declare function createReadableFrom<T>(iterable: Iterable<T> | AsyncIterable<T>, opt?: ReadableOptions): ReadableTyped<T>;
20
+ /**
21
+ * Allows to "create Readable asynchronously".
22
+ * Implemented via a proxy Transform, which is created (and returned) eagerly,
23
+ * and later (when source Readable is created) serves as a pass-through proxy.
24
+ */
25
+ export declare function createReadableFromAsync<T>(fn: () => Promise<ReadableTyped<T>>): ReadableTyped<T>;
@@ -1,3 +1,4 @@
1
+ import { Transform } from 'node:stream';
1
2
  import { Readable } from 'node:stream';
2
3
  /**
3
4
  * Convenience function to create a Readable that can be pushed into (similar to RxJS Subject).
@@ -11,7 +12,7 @@ import { Readable } from 'node:stream';
11
12
  * if read() will be called AFTER everything was pushed and Readable is closed (by pushing `null`).
12
13
  * Beware of it when e.g doing unit testing! Jest prefers to hang (not exit-0).
13
14
  */
14
- export function readableCreate(items = [], opt, onRead) {
15
+ export function createReadable(items = [], opt, onRead) {
15
16
  const readable = new Readable({
16
17
  objectMode: true,
17
18
  ...opt,
@@ -27,6 +28,26 @@ export function readableCreate(items = [], opt, onRead) {
27
28
  /**
28
29
  * Convenience type-safe wrapper around Readable.from() that infers the Type of input.
29
30
  */
30
- export function readableFrom(iterable, opt) {
31
+ export function createReadableFrom(iterable, opt) {
31
32
  return Readable.from(iterable, opt);
32
33
  }
34
+ /**
35
+ * Allows to "create Readable asynchronously".
36
+ * Implemented via a proxy Transform, which is created (and returned) eagerly,
37
+ * and later (when source Readable is created) serves as a pass-through proxy.
38
+ */
39
+ export function createReadableFromAsync(fn) {
40
+ const transform = new Transform({
41
+ objectMode: true,
42
+ highWaterMark: 1,
43
+ transform: (chunk, _encoding, cb) => {
44
+ cb(null, chunk);
45
+ },
46
+ });
47
+ void fn()
48
+ .then(readable => {
49
+ readable.on('error', err => transform.destroy(err)).pipe(transform);
50
+ })
51
+ .catch(err => transform.destroy(err));
52
+ return transform;
53
+ }
@@ -11,11 +11,11 @@ export interface ReadableArrayOptions {
11
11
  /** allows destroying the stream if the signal is aborted. */
12
12
  signal?: AbortSignal;
13
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[]>;
14
+ export type ReadableMapper<IN, OUT = unknown> = (data: IN, opt?: ReadableSignalOptions) => Promisable<OUT>;
15
+ export type ReadableFlatMapper<IN, OUT = unknown> = (data: IN, opt?: ReadableSignalOptions) => Promisable<OUT[]>;
16
16
  export type ReadableVoidMapper<IN> = (data: IN, opt?: ReadableSignalOptions) => void | Promise<void>;
17
17
  export type ReadablePredicate<IN> = (data: IN, opt?: ReadableSignalOptions) => boolean | Promise<boolean>;
18
- export interface ReadableTyped<T> extends Readable {
18
+ export interface ReadableTyped<T = unknown> extends Readable {
19
19
  toArray: (opt?: ReadableSignalOptions) => Promise<T[]>;
20
20
  map: <OUT>(mapper: ReadableMapper<T, OUT>, opt?: ReadableArrayOptions) => ReadableTyped<OUT>;
21
21
  flatMap: <OUT>(mapper: ReadableFlatMapper<T, OUT>, opt?: ReadableArrayOptions) => ReadableTyped<OUT>;
@@ -26,17 +26,7 @@ export interface ReadableTyped<T> extends Readable {
26
26
  }
27
27
  export interface WritableTyped<T> extends Writable {
28
28
  }
29
- /**
30
- * Type alias that indicates that the Readable is not in objectMode,
31
- * e.g returns a binary stream (like a gzip stream).
32
- */
33
- export type ReadableBinary = Readable;
34
- /**
35
- * Type alias that indicates that the Writable is not in objectMode,
36
- * e.g reads a binary stream (like a gzip stream).
37
- */
38
- export type WritableBinary = Writable;
39
- export interface TransformTyped<IN, OUT> extends Transform {
29
+ export interface TransformTyped<IN = unknown, OUT = unknown> extends Transform {
40
30
  }
41
31
  export interface TransformOptions {
42
32
  /**
@@ -2,7 +2,7 @@ import { Transform } from 'node:stream';
2
2
  import { createCommonLoggerAtLevel } from '@naturalcycles/js-lib/log';
3
3
  import { pDefer } from '@naturalcycles/js-lib/promise/pDefer.js';
4
4
  import { Pipeline } from '../pipeline.js';
5
- import { readableCreate } from '../readable/readableCreate.js';
5
+ import { createReadable } from '../readable/createReadable.js';
6
6
  /**
7
7
  * Allows to "fork" away from the "main pipeline" into the "forked pipeline".
8
8
  *
@@ -14,12 +14,12 @@ export function transformFork(fn, opt = {}) {
14
14
  const { objectMode = true, highWaterMark } = opt;
15
15
  const logger = createCommonLoggerAtLevel(opt.logger, opt.logLevel);
16
16
  let lock;
17
- const fork = readableCreate([], {}, () => {
17
+ const fork = createReadable([], {}, () => {
18
18
  // `_read` is called
19
19
  if (!lock)
20
20
  return;
21
21
  // We had a lock - let's Resume
22
- logger.log(`TransformFork: resume`);
22
+ logger.debug(`TransformFork: resume`);
23
23
  const lockCopy = lock;
24
24
  lock = undefined;
25
25
  lockCopy.resolve();
@@ -45,7 +45,7 @@ export function transformFork(fn, opt = {}) {
45
45
  if (!shouldContinue && !lock) {
46
46
  // Forked pipeline indicates that we should Pause
47
47
  lock = pDefer();
48
- logger.log(`TransformFork: pause`);
48
+ logger.debug(`TransformFork: pause`);
49
49
  }
50
50
  // acknowledge that we've finished processing the input chunk
51
51
  cb();
@@ -1,6 +1,6 @@
1
1
  import { type AbortableSignal } from '@naturalcycles/js-lib';
2
2
  import { ErrorMode } from '@naturalcycles/js-lib/error';
3
- import { type AbortableAsyncMapper, type AsyncPredicate, END, type PositiveInteger, type Promisable, SKIP, type StringMap, type UnixTimestampMillis } from '@naturalcycles/js-lib/types';
3
+ import { type AbortableAsyncMapper, type AsyncPredicate, END, type PositiveInteger, type Predicate, type Promisable, SKIP, type StringMap, type UnixTimestampMillis } from '@naturalcycles/js-lib/types';
4
4
  import type { TransformOptions, TransformTyped } from '../stream.model.js';
5
5
  export interface TransformMapOptions<IN = any, OUT = IN> extends TransformOptions {
6
6
  /**
@@ -10,7 +10,7 @@ export interface TransformMapOptions<IN = any, OUT = IN> extends TransformOption
10
10
  * Defaults to "pass everything" (including null, undefined, etc).
11
11
  * Simpler way to exclude certain cases is to return SKIP symbol from the mapper.
12
12
  */
13
- predicate?: AsyncPredicate<OUT>;
13
+ predicate?: Predicate<OUT> | AsyncPredicate<OUT>;
14
14
  /**
15
15
  * Number of concurrently pending promises returned by `mapper`.
16
16
  *
@@ -1,13 +1,13 @@
1
- import type { CommonLogger } from '@naturalcycles/js-lib/log';
2
- import type { AsyncIndexedMapper } from '@naturalcycles/js-lib/types';
1
+ import type { AsyncIndexedMapper, IndexedMapper } from '@naturalcycles/js-lib/types';
3
2
  import type { TransformOptions, TransformTyped } from '../stream.model.js';
4
- export interface TransformTapOptions extends TransformOptions {
5
- logger?: CommonLogger;
6
- }
7
3
  /**
8
4
  * Similar to RxJS `tap` - allows to run a function for each stream item, without affecting the result.
9
5
  * Item is passed through to the output.
10
6
  *
11
7
  * Can also act as a counter, since `index` is passed to `fn`
12
8
  */
13
- export declare function transformTap<IN>(fn: AsyncIndexedMapper<IN, any>, opt?: TransformTapOptions): TransformTyped<IN, IN>;
9
+ export declare function transformTap<IN>(fn: AsyncIndexedMapper<IN, any>, opt?: TransformOptions): TransformTyped<IN, IN>;
10
+ /**
11
+ * Sync version of transformTap
12
+ */
13
+ export declare function transformTapSync<IN>(fn: IndexedMapper<IN, any>, opt?: TransformOptions): TransformTyped<IN, IN>;
@@ -6,13 +6,12 @@ import { Transform } from 'node:stream';
6
6
  * Can also act as a counter, since `index` is passed to `fn`
7
7
  */
8
8
  export function transformTap(fn, opt = {}) {
9
- const { logger = console } = opt;
9
+ const { logger = console, highWaterMark = 1 } = opt;
10
10
  let index = -1;
11
11
  return new Transform({
12
12
  objectMode: true,
13
- ...opt,
13
+ highWaterMark,
14
14
  async transform(chunk, _, cb) {
15
- // console.log('tap', chunk)
16
15
  try {
17
16
  await fn(chunk, ++index);
18
17
  }
@@ -20,7 +19,28 @@ export function transformTap(fn, opt = {}) {
20
19
  logger.error(err);
21
20
  // suppressed error
22
21
  }
23
- cb(null, chunk); // pass through the item
22
+ cb(null, chunk);
23
+ },
24
+ });
25
+ }
26
+ /**
27
+ * Sync version of transformTap
28
+ */
29
+ export function transformTapSync(fn, opt = {}) {
30
+ const { logger = console, highWaterMark = 1 } = opt;
31
+ let index = -1;
32
+ return new Transform({
33
+ objectMode: true,
34
+ highWaterMark,
35
+ transform(chunk, _, cb) {
36
+ try {
37
+ fn(chunk, ++index);
38
+ }
39
+ catch (err) {
40
+ logger.error(err);
41
+ // suppressed error
42
+ }
43
+ cb(null, chunk);
24
44
  },
25
45
  });
26
46
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.27.3",
4
+ "version": "15.28.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/through2-concurrent": "^2",
26
- "@naturalcycles/dev-lib": "18.4.2"
26
+ "@naturalcycles/dev-lib": "19.37.0"
27
27
  },
28
28
  "exports": {
29
29
  ".": "./dist/index.js",
@@ -5,8 +5,8 @@ export * from './ndjson/transformJsonParse.js'
5
5
  export * from './ndjson/transformToNDJson.js'
6
6
  export * from './pipeline.js'
7
7
  export * from './progressLogger.js'
8
+ export * from './readable/createReadable.js'
8
9
  export * from './readable/readableCombined.js'
9
- export * from './readable/readableCreate.js'
10
10
  export * from './readable/readableFromArray.js'
11
11
  export * from './stream.model.js'
12
12
  export * from './transform/transformChunk.js'
@@ -20,6 +20,7 @@ import { fs2 } from '../fs/fs2.js'
20
20
  import { createReadStreamAsNDJson } from './ndjson/createReadStreamAsNDJson.js'
21
21
  import { transformJsonParse } from './ndjson/transformJsonParse.js'
22
22
  import { transformToNDJson } from './ndjson/transformToNDJson.js'
23
+ import { createReadableFromAsync } from './readable/createReadable.js'
23
24
  import type {
24
25
  ReadableTyped,
25
26
  TransformOptions,
@@ -44,12 +45,12 @@ import {
44
45
  import { transformMapSync, type TransformMapSyncOptions } from './transform/transformMapSync.js'
45
46
  import { transformOffset, type TransformOffsetOptions } from './transform/transformOffset.js'
46
47
  import { transformSplitOnNewline } from './transform/transformSplit.js'
47
- import { transformTap, type TransformTapOptions } from './transform/transformTap.js'
48
+ import { transformTap, transformTapSync } from './transform/transformTap.js'
48
49
  import { transformThrottle, type TransformThrottleOptions } from './transform/transformThrottle.js'
49
50
  import { writablePushToArray } from './writable/writablePushToArray.js'
50
51
  import { writableVoid } from './writable/writableVoid.js'
51
52
 
52
- export class Pipeline<T> {
53
+ export class Pipeline<T = unknown> {
53
54
  // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
54
55
  private readonly source: Readable
55
56
  private transforms: NodeJS.ReadWriteStream[] = []
@@ -68,6 +69,15 @@ export class Pipeline<T> {
68
69
  return new Pipeline(source)
69
70
  }
70
71
 
72
+ /**
73
+ * Useful in cases when Readable is not immediately available,
74
+ * but only available after an async operation is completed.
75
+ * Implemented via a proxy Transform, which should be transparent.
76
+ */
77
+ static fromAsyncReadable<T = unknown>(fn: () => Promise<ReadableTyped<T>>): Pipeline<T> {
78
+ return new Pipeline(createReadableFromAsync(fn))
79
+ }
80
+
71
81
  static fromWeb<T>(webReadableStream: WebReadableStream<T>): Pipeline<T> {
72
82
  return new Pipeline(Readable.fromWeb(webReadableStream))
73
83
  }
@@ -211,11 +221,16 @@ export class Pipeline<T> {
211
221
  return this
212
222
  }
213
223
 
214
- tap(fn: AsyncIndexedMapper<T, any>, opt?: TransformTapOptions): this {
224
+ tap(fn: AsyncIndexedMapper<T, any>, opt?: TransformOptions): this {
215
225
  this.transforms.push(transformTap(fn, opt))
216
226
  return this
217
227
  }
218
228
 
229
+ tapSync(fn: IndexedMapper<T, any>, opt?: TransformOptions): this {
230
+ this.transforms.push(transformTapSync(fn, opt))
231
+ return this
232
+ }
233
+
219
234
  throttle(opt: TransformThrottleOptions): this {
220
235
  this.transforms.push(transformThrottle(opt))
221
236
  return this
@@ -1,4 +1,4 @@
1
- import type { ReadableOptions } from 'node:stream'
1
+ import { type ReadableOptions, Transform } from 'node:stream'
2
2
  import { Readable } from 'node:stream'
3
3
  import type { ReadableTyped } from '../stream.model.js'
4
4
 
@@ -14,7 +14,7 @@ import type { ReadableTyped } from '../stream.model.js'
14
14
  * if read() will be called AFTER everything was pushed and Readable is closed (by pushing `null`).
15
15
  * Beware of it when e.g doing unit testing! Jest prefers to hang (not exit-0).
16
16
  */
17
- export function readableCreate<T>(
17
+ export function createReadable<T>(
18
18
  items: Iterable<T> = [],
19
19
  opt?: ReadableOptions,
20
20
  onRead?: () => void, // read callback
@@ -35,9 +35,32 @@ export function readableCreate<T>(
35
35
  /**
36
36
  * Convenience type-safe wrapper around Readable.from() that infers the Type of input.
37
37
  */
38
- export function readableFrom<T>(
38
+ export function createReadableFrom<T>(
39
39
  iterable: Iterable<T> | AsyncIterable<T>,
40
40
  opt?: ReadableOptions,
41
41
  ): ReadableTyped<T> {
42
42
  return Readable.from(iterable, opt)
43
43
  }
44
+
45
+ /**
46
+ * Allows to "create Readable asynchronously".
47
+ * Implemented via a proxy Transform, which is created (and returned) eagerly,
48
+ * and later (when source Readable is created) serves as a pass-through proxy.
49
+ */
50
+ export function createReadableFromAsync<T>(fn: () => Promise<ReadableTyped<T>>): ReadableTyped<T> {
51
+ const transform = new Transform({
52
+ objectMode: true,
53
+ highWaterMark: 1,
54
+ transform: (chunk, _encoding, cb) => {
55
+ cb(null, chunk)
56
+ },
57
+ })
58
+
59
+ void fn()
60
+ .then(readable => {
61
+ readable.on('error', err => transform.destroy(err)).pipe(transform)
62
+ })
63
+ .catch(err => transform.destroy(err))
64
+
65
+ return transform
66
+ }
@@ -14,9 +14,12 @@ export interface ReadableArrayOptions {
14
14
  signal?: AbortSignal
15
15
  }
16
16
 
17
- export type ReadableMapper<IN, OUT> = (data: IN, opt?: ReadableSignalOptions) => Promisable<OUT>
17
+ export type ReadableMapper<IN, OUT = unknown> = (
18
+ data: IN,
19
+ opt?: ReadableSignalOptions,
20
+ ) => Promisable<OUT>
18
21
 
19
- export type ReadableFlatMapper<IN, OUT> = (
22
+ export type ReadableFlatMapper<IN, OUT = unknown> = (
20
23
  data: IN,
21
24
  opt?: ReadableSignalOptions,
22
25
  ) => Promisable<OUT[]>
@@ -28,7 +31,7 @@ export type ReadablePredicate<IN> = (
28
31
  opt?: ReadableSignalOptions,
29
32
  ) => boolean | Promise<boolean>
30
33
 
31
- export interface ReadableTyped<T> extends Readable {
34
+ export interface ReadableTyped<T = unknown> extends Readable {
32
35
  toArray: (opt?: ReadableSignalOptions) => Promise<T[]>
33
36
 
34
37
  map: <OUT>(mapper: ReadableMapper<T, OUT>, opt?: ReadableArrayOptions) => ReadableTyped<OUT>
@@ -49,19 +52,8 @@ export interface ReadableTyped<T> extends Readable {
49
52
  // biome-ignore lint/correctness/noUnusedVariables: ok
50
53
  export interface WritableTyped<T> extends Writable {}
51
54
 
52
- /**
53
- * Type alias that indicates that the Readable is not in objectMode,
54
- * e.g returns a binary stream (like a gzip stream).
55
- */
56
- export type ReadableBinary = Readable
57
- /**
58
- * Type alias that indicates that the Writable is not in objectMode,
59
- * e.g reads a binary stream (like a gzip stream).
60
- */
61
- export type WritableBinary = Writable
62
-
63
55
  // biome-ignore lint/correctness/noUnusedVariables: ok
64
- export interface TransformTyped<IN, OUT> extends Transform {}
56
+ export interface TransformTyped<IN = unknown, OUT = unknown> extends Transform {}
65
57
 
66
58
  export interface TransformOptions {
67
59
  /**
@@ -2,7 +2,7 @@ import { Transform } from 'node:stream'
2
2
  import { createCommonLoggerAtLevel } from '@naturalcycles/js-lib/log'
3
3
  import { type DeferredPromise, pDefer } from '@naturalcycles/js-lib/promise/pDefer.js'
4
4
  import { Pipeline } from '../pipeline.js'
5
- import { readableCreate } from '../readable/readableCreate.js'
5
+ import { createReadable } from '../readable/createReadable.js'
6
6
  import type { TransformOptions, TransformTyped } from '../stream.model.js'
7
7
 
8
8
  /**
@@ -21,11 +21,11 @@ export function transformFork<T>(
21
21
 
22
22
  let lock: DeferredPromise | undefined
23
23
 
24
- const fork = readableCreate<T>([], {}, () => {
24
+ const fork = createReadable<T>([], {}, () => {
25
25
  // `_read` is called
26
26
  if (!lock) return
27
27
  // We had a lock - let's Resume
28
- logger.log(`TransformFork: resume`)
28
+ logger.debug(`TransformFork: resume`)
29
29
  const lockCopy = lock
30
30
  lock = undefined
31
31
  lockCopy.resolve()
@@ -54,7 +54,7 @@ export function transformFork<T>(
54
54
  if (!shouldContinue && !lock) {
55
55
  // Forked pipeline indicates that we should Pause
56
56
  lock = pDefer()
57
- logger.log(`TransformFork: pause`)
57
+ logger.debug(`TransformFork: pause`)
58
58
  }
59
59
 
60
60
  // acknowledge that we've finished processing the input chunk
@@ -8,6 +8,7 @@ import {
8
8
  type AsyncPredicate,
9
9
  END,
10
10
  type PositiveInteger,
11
+ type Predicate,
11
12
  type Promisable,
12
13
  SKIP,
13
14
  type StringMap,
@@ -26,7 +27,7 @@ export interface TransformMapOptions<IN = any, OUT = IN> extends TransformOption
26
27
  * Defaults to "pass everything" (including null, undefined, etc).
27
28
  * Simpler way to exclude certain cases is to return SKIP symbol from the mapper.
28
29
  */
29
- predicate?: AsyncPredicate<OUT>
30
+ predicate?: Predicate<OUT> | AsyncPredicate<OUT>
30
31
 
31
32
  /**
32
33
  * Number of concurrently pending promises returned by `mapper`.
@@ -1,12 +1,7 @@
1
1
  import { Transform } from 'node:stream'
2
- import type { CommonLogger } from '@naturalcycles/js-lib/log'
3
- import type { AsyncIndexedMapper } from '@naturalcycles/js-lib/types'
2
+ import type { AsyncIndexedMapper, IndexedMapper } from '@naturalcycles/js-lib/types'
4
3
  import type { TransformOptions, TransformTyped } from '../stream.model.js'
5
4
 
6
- export interface TransformTapOptions extends TransformOptions {
7
- logger?: CommonLogger
8
- }
9
-
10
5
  /**
11
6
  * Similar to RxJS `tap` - allows to run a function for each stream item, without affecting the result.
12
7
  * Item is passed through to the output.
@@ -15,17 +10,15 @@ export interface TransformTapOptions extends TransformOptions {
15
10
  */
16
11
  export function transformTap<IN>(
17
12
  fn: AsyncIndexedMapper<IN, any>,
18
- opt: TransformTapOptions = {},
13
+ opt: TransformOptions = {},
19
14
  ): TransformTyped<IN, IN> {
20
- const { logger = console } = opt
15
+ const { logger = console, highWaterMark = 1 } = opt
21
16
  let index = -1
22
17
 
23
18
  return new Transform({
24
19
  objectMode: true,
25
- ...opt,
20
+ highWaterMark,
26
21
  async transform(chunk: IN, _, cb) {
27
- // console.log('tap', chunk)
28
-
29
22
  try {
30
23
  await fn(chunk, ++index)
31
24
  } catch (err) {
@@ -33,7 +26,33 @@ export function transformTap<IN>(
33
26
  // suppressed error
34
27
  }
35
28
 
36
- cb(null, chunk) // pass through the item
29
+ cb(null, chunk)
30
+ },
31
+ })
32
+ }
33
+
34
+ /**
35
+ * Sync version of transformTap
36
+ */
37
+ export function transformTapSync<IN>(
38
+ fn: IndexedMapper<IN, any>,
39
+ opt: TransformOptions = {},
40
+ ): TransformTyped<IN, IN> {
41
+ const { logger = console, highWaterMark = 1 } = opt
42
+ let index = -1
43
+
44
+ return new Transform({
45
+ objectMode: true,
46
+ highWaterMark,
47
+ transform(chunk: IN, _, cb) {
48
+ try {
49
+ fn(chunk, ++index)
50
+ } catch (err) {
51
+ logger.error(err)
52
+ // suppressed error
53
+ }
54
+
55
+ cb(null, chunk)
37
56
  },
38
57
  })
39
58
  }