@naturalcycles/nodejs-lib 12.55.1 → 12.56.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 CHANGED
@@ -47,7 +47,7 @@ import { transformToString } from './stream/transform/transformToString';
47
47
  import { BaseWorkerClass, WorkerClassInterface } from './stream/transform/worker/baseWorkerClass';
48
48
  import { transformMultiThreaded, TransformMultiThreadedOptions } from './stream/transform/worker/transformMultiThreaded';
49
49
  import { WorkerInput, WorkerOutput } from './stream/transform/worker/transformMultiThreaded.model';
50
- import { writableForEach } from './stream/writable/writableForEach';
50
+ export * from './stream/writable/writableForEach';
51
51
  import { writableFork } from './stream/writable/writableFork';
52
52
  import { writablePushToArray } from './stream/writable/writablePushToArray';
53
53
  import { writableVoid } from './stream/writable/writableVoid';
@@ -67,4 +67,4 @@ import { convert, getValidationResult, isValid, JoiValidationResult, undefinedIf
67
67
  import { sanitizeHTML, SanitizeHTMLOptions } from './validation/sanitize.util';
68
68
  import { runScript, RunScriptOptions } from './script';
69
69
  export type { RunScriptOptions, JoiValidationErrorData, JoiValidationResult, ValidationErrorItem, ExtendedJoi, SchemaTyped, AnySchema, AnySchemaTyped, ArraySchemaTyped, BooleanSchemaTyped, NumberSchemaTyped, ObjectSchemaTyped, StringSchemaTyped, IDebug, IDebugger, SlackServiceCfg, SlackMessage, SlackMessageProps, SlackApiBody, SlackMessagePrefixHook, ReadableTyped, WritableTyped, TransformTyped, PipelineFromNDJsonFileOptions, PipelineToNDJsonFileOptions, TransformJsonParseOptions, TransformToNDJsonOptions, TransformMapOptions, TransformMapSyncOptions, NDJSONStreamForEachOptions, TransformOptions, TransformLogProgressOptions, TransformMultiThreadedOptions, WorkerClassInterface, WorkerInput, WorkerOutput, TableDiffOptions, InspectAnyOptions, Got, GetGotOptions, AfterResponseHook, BeforeErrorHook, BeforeRequestHook, AjvValidationOptions, AjvSchemaCfg, AjvValidationErrorData, SanitizeHTMLOptions, };
70
- export { JoiValidationError, validate, getValidationResult, isValid, undefinedIfInvalid, convert, Joi, booleanSchema, booleanDefaultToFalseSchema, stringSchema, numberSchema, integerSchema, percentageSchema, dateStringSchema, arraySchema, binarySchema, objectSchema, oneOfSchema, anySchema, anyObjectSchema, baseDBEntitySchema, savedDBEntitySchema, idSchema, unixTimestampSchema, verSchema, emailSchema, SEM_VER_PATTERN, semVerSchema, userAgentSchema, utcOffsetSchema, ipAddressSchema, slugSchema, urlSchema, processSharedUtil, zipBuffer, gzipBuffer, unzipBuffer, gunzipBuffer, zipString, gzipString, unzipToString, gunzipToString, requireEnvKeys, requireFileToExist, LRUMemoCache, stringId, stringIdAsync, stringIdUnsafe, ALPHABET_NUMBER, ALPHABET_LOWERCASE, ALPHABET_UPPERCASE, ALPHABET_ALPHANUMERIC_LOWERCASE, ALPHABET_ALPHANUMERIC_UPPERCASE, ALPHABET_ALPHANUMERIC, md5, hash, hashAsBuffer, md5AsBuffer, stringToBase64, base64ToString, bufferToBase64, base64ToBuffer, Debug, getSecretMap, setSecretMap, loadSecretsFromEnv, loadSecretsFromJsonFile, removeSecretsFromEnv, secret, secretOptional, memoryUsage, memoryUsageFull, SlackService, slackDefaultMessagePrefixHook, readableCreate, readableFrom, readableFromArray, readableToArray, readableForEach, readableForEachSync, readableMap, readableMapToArray, _pipeline, transformBuffer, ndjsonMap, ndJsonFileRead, ndJsonFileWrite, ndjsonStreamForEach, pipelineFromNDJsonFile, pipelineToNDJsonFile, NDJsonStats, streamToNDJsonFile, transformJsonParse, bufferReviver, transformToNDJson, transformFilter, transformFilterSync, transformMap, transformMapSync, transformMapSimple, transformNoOp, writableForEach, writablePushToArray, transformSplit, transformToString, transformToArray, transformTap, transformLogProgress, transformLimit, writableVoid, writableFork, transformMultiThreaded, BaseWorkerClass, tableDiff, inspectAny, inspectAnyStringifyFn, getGot, HTTPError, TimeoutError, _chunkBuffer, Ajv, getAjv, AjvSchema, AjvValidationError, readJsonSchemas, readAjvSchemas, hasColors, sanitizeHTML, runScript, };
70
+ export { JoiValidationError, validate, getValidationResult, isValid, undefinedIfInvalid, convert, Joi, booleanSchema, booleanDefaultToFalseSchema, stringSchema, numberSchema, integerSchema, percentageSchema, dateStringSchema, arraySchema, binarySchema, objectSchema, oneOfSchema, anySchema, anyObjectSchema, baseDBEntitySchema, savedDBEntitySchema, idSchema, unixTimestampSchema, verSchema, emailSchema, SEM_VER_PATTERN, semVerSchema, userAgentSchema, utcOffsetSchema, ipAddressSchema, slugSchema, urlSchema, processSharedUtil, zipBuffer, gzipBuffer, unzipBuffer, gunzipBuffer, zipString, gzipString, unzipToString, gunzipToString, requireEnvKeys, requireFileToExist, LRUMemoCache, stringId, stringIdAsync, stringIdUnsafe, ALPHABET_NUMBER, ALPHABET_LOWERCASE, ALPHABET_UPPERCASE, ALPHABET_ALPHANUMERIC_LOWERCASE, ALPHABET_ALPHANUMERIC_UPPERCASE, ALPHABET_ALPHANUMERIC, md5, hash, hashAsBuffer, md5AsBuffer, stringToBase64, base64ToString, bufferToBase64, base64ToBuffer, Debug, getSecretMap, setSecretMap, loadSecretsFromEnv, loadSecretsFromJsonFile, removeSecretsFromEnv, secret, secretOptional, memoryUsage, memoryUsageFull, SlackService, slackDefaultMessagePrefixHook, readableCreate, readableFrom, readableFromArray, readableToArray, readableForEach, readableForEachSync, readableMap, readableMapToArray, _pipeline, transformBuffer, ndjsonMap, ndJsonFileRead, ndJsonFileWrite, ndjsonStreamForEach, pipelineFromNDJsonFile, pipelineToNDJsonFile, NDJsonStats, streamToNDJsonFile, transformJsonParse, bufferReviver, transformToNDJson, transformFilter, transformFilterSync, transformMap, transformMapSync, transformMapSimple, transformNoOp, writablePushToArray, transformSplit, transformToString, transformToArray, transformTap, transformLogProgress, transformLimit, writableVoid, writableFork, transformMultiThreaded, BaseWorkerClass, tableDiff, inspectAny, inspectAnyStringifyFn, getGot, HTTPError, TimeoutError, _chunkBuffer, Ajv, getAjv, AjvSchema, AjvValidationError, readJsonSchemas, readAjvSchemas, hasColors, sanitizeHTML, runScript, };
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ALPHABET_LOWERCASE = exports.ALPHABET_NUMBER = exports.stringIdUnsafe = exports.stringIdAsync = exports.stringId = exports.LRUMemoCache = exports.requireFileToExist = exports.requireEnvKeys = exports.gunzipToString = exports.unzipToString = exports.gzipString = exports.zipString = exports.gunzipBuffer = exports.unzipBuffer = exports.gzipBuffer = exports.zipBuffer = exports.processSharedUtil = exports.urlSchema = exports.slugSchema = exports.ipAddressSchema = exports.utcOffsetSchema = exports.userAgentSchema = exports.semVerSchema = exports.SEM_VER_PATTERN = exports.emailSchema = exports.verSchema = exports.unixTimestampSchema = exports.idSchema = exports.savedDBEntitySchema = exports.baseDBEntitySchema = exports.anyObjectSchema = exports.anySchema = exports.oneOfSchema = exports.objectSchema = exports.binarySchema = exports.arraySchema = exports.dateStringSchema = exports.percentageSchema = exports.integerSchema = exports.numberSchema = exports.stringSchema = exports.booleanDefaultToFalseSchema = exports.booleanSchema = exports.Joi = exports.convert = exports.undefinedIfInvalid = exports.isValid = exports.getValidationResult = exports.validate = exports.JoiValidationError = void 0;
4
4
  exports.transformMapSimple = exports.transformMapSync = exports.transformMap = exports.transformFilterSync = exports.transformFilter = exports.transformToNDJson = exports.bufferReviver = exports.transformJsonParse = exports.streamToNDJsonFile = exports.NDJsonStats = exports.pipelineToNDJsonFile = exports.pipelineFromNDJsonFile = exports.ndjsonStreamForEach = exports.ndJsonFileWrite = exports.ndJsonFileRead = exports.ndjsonMap = exports.transformBuffer = exports._pipeline = exports.readableMapToArray = exports.readableMap = exports.readableForEachSync = exports.readableForEach = exports.readableToArray = exports.readableFromArray = exports.readableFrom = exports.readableCreate = exports.slackDefaultMessagePrefixHook = exports.SlackService = exports.memoryUsageFull = exports.memoryUsage = exports.secretOptional = exports.secret = exports.removeSecretsFromEnv = exports.loadSecretsFromJsonFile = exports.loadSecretsFromEnv = exports.setSecretMap = exports.getSecretMap = exports.Debug = exports.base64ToBuffer = exports.bufferToBase64 = exports.base64ToString = exports.stringToBase64 = exports.md5AsBuffer = exports.hashAsBuffer = exports.hash = exports.md5 = exports.ALPHABET_ALPHANUMERIC = exports.ALPHABET_ALPHANUMERIC_UPPERCASE = exports.ALPHABET_ALPHANUMERIC_LOWERCASE = exports.ALPHABET_UPPERCASE = void 0;
5
- exports.runScript = exports.sanitizeHTML = exports.hasColors = exports.readAjvSchemas = exports.readJsonSchemas = exports.AjvValidationError = exports.AjvSchema = exports.getAjv = exports.Ajv = exports._chunkBuffer = exports.TimeoutError = exports.HTTPError = exports.getGot = exports.inspectAnyStringifyFn = exports.inspectAny = exports.tableDiff = exports.BaseWorkerClass = exports.transformMultiThreaded = exports.writableFork = exports.writableVoid = exports.transformLimit = exports.transformLogProgress = exports.transformTap = exports.transformToArray = exports.transformToString = exports.transformSplit = exports.writablePushToArray = exports.writableForEach = exports.transformNoOp = void 0;
5
+ exports.runScript = exports.sanitizeHTML = exports.hasColors = exports.readAjvSchemas = exports.readJsonSchemas = exports.AjvValidationError = exports.AjvSchema = exports.getAjv = exports.Ajv = exports._chunkBuffer = exports.TimeoutError = exports.HTTPError = exports.getGot = exports.inspectAnyStringifyFn = exports.inspectAny = exports.tableDiff = exports.BaseWorkerClass = exports.transformMultiThreaded = exports.writableFork = exports.writableVoid = exports.transformLimit = exports.transformLogProgress = exports.transformTap = exports.transformToArray = exports.transformToString = exports.transformSplit = exports.writablePushToArray = exports.transformNoOp = void 0;
6
+ const tslib_1 = require("tslib");
6
7
  const ajv_1 = require("ajv");
7
8
  exports.Ajv = ajv_1.default;
8
9
  const got_1 = require("got");
@@ -118,8 +119,7 @@ const baseWorkerClass_1 = require("./stream/transform/worker/baseWorkerClass");
118
119
  Object.defineProperty(exports, "BaseWorkerClass", { enumerable: true, get: function () { return baseWorkerClass_1.BaseWorkerClass; } });
119
120
  const transformMultiThreaded_1 = require("./stream/transform/worker/transformMultiThreaded");
120
121
  Object.defineProperty(exports, "transformMultiThreaded", { enumerable: true, get: function () { return transformMultiThreaded_1.transformMultiThreaded; } });
121
- const writableForEach_1 = require("./stream/writable/writableForEach");
122
- Object.defineProperty(exports, "writableForEach", { enumerable: true, get: function () { return writableForEach_1.writableForEach; } });
122
+ (0, tslib_1.__exportStar)(require("./stream/writable/writableForEach"), exports);
123
123
  const writableFork_1 = require("./stream/writable/writableFork");
124
124
  Object.defineProperty(exports, "writableFork", { enumerable: true, get: function () { return writableFork_1.writableFork; } });
125
125
  const writablePushToArray_1 = require("./stream/writable/writablePushToArray");
@@ -8,7 +8,7 @@ const pipelineFromNDJsonFile_1 = require("./pipelineFromNDJsonFile");
8
8
  */
9
9
  async function ndJsonFileRead(opt) {
10
10
  const res = [];
11
- await (0, pipelineFromNDJsonFile_1.pipelineFromNDJsonFile)([(0, __1.writableForEach)(r => void res.push(r))], opt);
11
+ await (0, pipelineFromNDJsonFile_1.pipelineFromNDJsonFile)([(0, __1.writablePushToArray)(res)], opt);
12
12
  return res;
13
13
  }
14
14
  exports.ndJsonFileRead = ndJsonFileRead;
@@ -14,4 +14,4 @@ export declare function notNullishPredicate(item: any): boolean;
14
14
  *
15
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
16
  */
17
- export declare function transformMap2<IN = any, OUT = IN>(mapper: AsyncMapper<IN, OUT>, opt?: TransformMapOptions<IN, OUT>): TransformTyped<IN, OUT>;
17
+ export declare function transformMapLegacy<IN = any, OUT = IN>(mapper: AsyncMapper<IN, OUT>, opt?: TransformMapOptions<IN, OUT>): TransformTyped<IN, OUT>;
@@ -0,0 +1,94 @@
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,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.transformMap = exports.notNullishPredicate = void 0;
4
+ const stream_1 = require("stream");
4
5
  const js_lib_1 = require("@naturalcycles/js-lib");
5
- const through2Concurrent = require("through2-concurrent");
6
6
  const colors_1 = require("../../colors");
7
7
  function notNullishPredicate(item) {
8
8
  return item !== undefined && item !== null;
@@ -26,11 +26,17 @@ function transformMap(mapper, opt = {}) {
26
26
  let isRejected = false;
27
27
  let errors = 0;
28
28
  const collectedErrors = []; // only used if errorMode == THROW_AGGREGATED
29
- return through2Concurrent.obj({
30
- maxConcurrency: concurrency,
31
- // autoDestroy: true,
29
+ const q = new js_lib_1.PQueue({
30
+ concurrency,
31
+ resolveOn: 'start',
32
+ // debug: true,
33
+ });
34
+ return new stream_1.Transform({
35
+ objectMode: true,
32
36
  async final(cb) {
33
- // console.log('transformMap final')
37
+ // console.log('transformMap final', {index}, q.inFlight, q.queueSize)
38
+ // wait for the current inFlight jobs to complete and push their results
39
+ await q.onIdle();
34
40
  logErrorStats(logger, true);
35
41
  await beforeFinal?.(); // call beforeFinal if defined
36
42
  if (collectedErrors.length) {
@@ -42,48 +48,46 @@ function transformMap(mapper, opt = {}) {
42
48
  cb();
43
49
  }
44
50
  },
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) {
51
+ async transform(chunk, _encoding, cb) {
52
+ index++;
53
+ // console.log('transform', {index})
54
+ // Stop processing if THROW_IMMEDIATELY mode is used
55
+ if (isRejected && errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY)
56
+ return cb();
57
+ // It resolves when it is successfully STARTED execution.
58
+ // If it's queued instead - it'll wait and resolve only upon START.
59
+ await q.push(async () => {
71
60
  try {
72
- onError(err, chunk);
61
+ 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)
62
+ const res = await mapper(chunk, currentIndex);
63
+ const passedResults = await (0, js_lib_1.pFilter)(flattenArrayOutput && Array.isArray(res) ? res : [res], async (r) => await predicate(r, currentIndex));
64
+ passedResults.forEach(r => this.push(r));
73
65
  }
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
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 as Error)
80
+ return this.emit('error', err);
81
+ }
82
+ if (errorMode === js_lib_1.ErrorMode.THROW_AGGREGATED) {
83
+ collectedErrors.push(err);
84
+ }
85
+ }
86
+ });
87
+ // Resolved, which means it STARTED processing
88
+ // This means we can take more load
85
89
  cb();
86
- }
90
+ },
87
91
  });
88
92
  function logErrorStats(logger, final = false) {
89
93
  if (!errors)
@@ -1,7 +1,11 @@
1
- import { AsyncMapper } from '@naturalcycles/js-lib';
1
+ import { AsyncMapper, Mapper } from '@naturalcycles/js-lib';
2
2
  import { TransformMapOptions } from '../..';
3
3
  import { WritableTyped } from '../stream.model';
4
4
  /**
5
5
  * Just an alias to transformMap that declares OUT as void.
6
6
  */
7
7
  export declare function writableForEach<IN = any>(mapper: AsyncMapper<IN, void>, opt?: TransformMapOptions<IN, void>): WritableTyped<IN>;
8
+ /**
9
+ * Just an alias to transformMap that declares OUT as void.
10
+ */
11
+ export declare function writableForEachSync<IN = any>(mapper: Mapper<IN, void>, opt?: TransformMapOptions<IN, void>): WritableTyped<IN>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writableForEach = void 0;
3
+ exports.writableForEachSync = exports.writableForEach = void 0;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
5
  const __1 = require("../..");
6
6
  /**
@@ -10,3 +10,10 @@ function writableForEach(mapper, opt = {}) {
10
10
  return (0, __1.transformMap)(mapper, { ...opt, predicate: js_lib_1._passNothingPredicate });
11
11
  }
12
12
  exports.writableForEach = writableForEach;
13
+ /**
14
+ * Just an alias to transformMap that declares OUT as void.
15
+ */
16
+ function writableForEachSync(mapper, opt = {}) {
17
+ return (0, __1.transformMapSync)(mapper, { ...opt, predicate: js_lib_1._passNothingPredicate });
18
+ }
19
+ exports.writableForEachSync = writableForEachSync;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
- "version": "12.55.1",
3
+ "version": "12.56.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "docs-serve": "vuepress dev docs",
package/src/index.ts CHANGED
@@ -104,7 +104,7 @@ import {
104
104
  TransformMultiThreadedOptions,
105
105
  } from './stream/transform/worker/transformMultiThreaded'
106
106
  import { WorkerInput, WorkerOutput } from './stream/transform/worker/transformMultiThreaded.model'
107
- import { writableForEach } from './stream/writable/writableForEach'
107
+ export * from './stream/writable/writableForEach'
108
108
  import { writableFork } from './stream/writable/writableFork'
109
109
  import { writablePushToArray } from './stream/writable/writablePushToArray'
110
110
  import { writableVoid } from './stream/writable/writableVoid'
@@ -327,7 +327,6 @@ export {
327
327
  transformMapSync,
328
328
  transformMapSimple,
329
329
  transformNoOp,
330
- writableForEach,
331
330
  writablePushToArray,
332
331
  transformSplit,
333
332
  transformToString,
@@ -1,4 +1,4 @@
1
- import { writableForEach } from '../..'
1
+ import { writablePushToArray } from '../..'
2
2
  import { pipelineFromNDJsonFile, PipelineFromNDJsonFileOptions } from './pipelineFromNDJsonFile'
3
3
 
4
4
  /**
@@ -9,7 +9,7 @@ export async function ndJsonFileRead<OUT = any>(
9
9
  ): Promise<OUT[]> {
10
10
  const res: OUT[] = []
11
11
 
12
- await pipelineFromNDJsonFile([writableForEach(r => void res.push(r))], opt)
12
+ await pipelineFromNDJsonFile([writablePushToArray(res)], opt)
13
13
 
14
14
  return res
15
15
  }
@@ -0,0 +1,133 @@
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
+ }
@@ -6,8 +6,8 @@ import {
6
6
  CommonLogger,
7
7
  ErrorMode,
8
8
  pFilter,
9
+ PQueue,
9
10
  } from '@naturalcycles/js-lib'
10
- import through2Concurrent = require('through2-concurrent')
11
11
  import { yellow } from '../../colors'
12
12
  import { TransformTyped } from '../stream.model'
13
13
 
@@ -98,83 +98,84 @@ export function transformMap<IN = any, OUT = IN>(
98
98
  let errors = 0
99
99
  const collectedErrors: Error[] = [] // only used if errorMode == THROW_AGGREGATED
100
100
 
101
- return through2Concurrent.obj(
102
- {
103
- maxConcurrency: concurrency,
104
- // autoDestroy: true,
105
- async final(cb) {
106
- // console.log('transformMap final')
101
+ const q = new PQueue({
102
+ concurrency,
103
+ resolveOn: 'start',
104
+ // debug: true,
105
+ })
107
106
 
108
- logErrorStats(logger, true)
107
+ return new Transform({
108
+ objectMode: true,
109
109
 
110
- await beforeFinal?.() // call beforeFinal if defined
110
+ async final(cb) {
111
+ // console.log('transformMap final', {index}, q.inFlight, q.queueSize)
111
112
 
112
- if (collectedErrors.length) {
113
- // emit Aggregated error
114
- cb(new AggregatedError(collectedErrors))
115
- } else {
116
- // emit no error
117
- cb()
118
- }
119
- },
120
- },
121
- async function transformMapFn(
122
- this: Transform,
123
- chunk: IN,
124
- _encoding: any,
125
- cb: (...args: any[]) => any,
126
- ) {
127
- index++
128
- // console.log({chunk, _encoding})
113
+ // wait for the current inFlight jobs to complete and push their results
114
+ await q.onIdle()
129
115
 
130
- // Stop processing if THROW_IMMEDIATELY mode is used
131
- if (isRejected && errorMode === ErrorMode.THROW_IMMEDIATELY) return cb()
116
+ logErrorStats(logger, true)
132
117
 
133
- try {
134
- 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)
135
- const res = await mapper(chunk, currentIndex)
136
- const passedResults = await pFilter(
137
- flattenArrayOutput && Array.isArray(res) ? res : [res],
138
- async r => await predicate(r, currentIndex),
139
- )
140
-
141
- if (passedResults.length === 0) {
142
- cb() // 0 results
143
- } else {
144
- passedResults.forEach(r => {
145
- this.push(r)
146
- // cb(null, r)
147
- })
148
- cb() // done processing
149
- }
150
- } catch (err) {
151
- logger.error(err)
152
-
153
- errors++
118
+ await beforeFinal?.() // call beforeFinal if defined
154
119
 
155
- logErrorStats(logger)
120
+ if (collectedErrors.length) {
121
+ // emit Aggregated error
122
+ cb(new AggregatedError(collectedErrors))
123
+ } else {
124
+ // emit no error
125
+ cb()
126
+ }
127
+ },
156
128
 
157
- if (onError) {
158
- try {
159
- onError(err, chunk)
160
- } catch {}
161
- }
129
+ async transform(this: Transform, chunk: IN, _encoding, cb) {
130
+ index++
131
+ // console.log('transform', {index})
162
132
 
163
- if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
164
- isRejected = true
165
- // Emit error immediately
166
- return cb(err)
167
- }
133
+ // Stop processing if THROW_IMMEDIATELY mode is used
134
+ if (isRejected && errorMode === ErrorMode.THROW_IMMEDIATELY) return cb()
168
135
 
169
- if (errorMode === ErrorMode.THROW_AGGREGATED) {
170
- collectedErrors.push(err as Error)
136
+ // It resolves when it is successfully STARTED execution.
137
+ // If it's queued instead - it'll wait and resolve only upon START.
138
+ await q.push(async () => {
139
+ try {
140
+ 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)
141
+ const res = await mapper(chunk, currentIndex)
142
+ const passedResults = await pFilter(
143
+ flattenArrayOutput && Array.isArray(res) ? res : [res],
144
+ async r => await predicate(r, currentIndex),
145
+ )
146
+
147
+ passedResults.forEach(r => this.push(r))
148
+ } catch (err) {
149
+ logger.error(err)
150
+
151
+ errors++
152
+
153
+ logErrorStats(logger)
154
+
155
+ if (onError) {
156
+ try {
157
+ onError(err, chunk)
158
+ } catch {}
159
+ }
160
+
161
+ if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
162
+ isRejected = true
163
+ // Emit error immediately
164
+ // return cb(err as Error)
165
+ return this.emit('error', err as Error)
166
+ }
167
+
168
+ if (errorMode === ErrorMode.THROW_AGGREGATED) {
169
+ collectedErrors.push(err as Error)
170
+ }
171
171
  }
172
+ })
172
173
 
173
- // Tell input stream that we're done processing, but emit nothing to output - not error nor result
174
- cb()
175
- }
174
+ // Resolved, which means it STARTED processing
175
+ // This means we can take more load
176
+ cb()
176
177
  },
177
- )
178
+ })
178
179
 
179
180
  function logErrorStats(logger: CommonLogger, final = false): void {
180
181
  if (!errors) return
@@ -1,5 +1,5 @@
1
- import { AsyncMapper, _passNothingPredicate } from '@naturalcycles/js-lib'
2
- import { transformMap, TransformMapOptions } from '../..'
1
+ import { AsyncMapper, _passNothingPredicate, Mapper } from '@naturalcycles/js-lib'
2
+ import { transformMap, TransformMapOptions, transformMapSync } from '../..'
3
3
  import { WritableTyped } from '../stream.model'
4
4
 
5
5
  /**
@@ -11,3 +11,13 @@ export function writableForEach<IN = any>(
11
11
  ): WritableTyped<IN> {
12
12
  return transformMap<IN, void>(mapper, { ...opt, predicate: _passNothingPredicate })
13
13
  }
14
+
15
+ /**
16
+ * Just an alias to transformMap that declares OUT as void.
17
+ */
18
+ export function writableForEachSync<IN = any>(
19
+ mapper: Mapper<IN, void>,
20
+ opt: TransformMapOptions<IN, void> = {},
21
+ ): WritableTyped<IN> {
22
+ return transformMapSync<IN, void>(mapper, { ...opt, predicate: _passNothingPredicate })
23
+ }
@@ -1,98 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.transformMap2 = exports.notNullishPredicate = void 0;
4
- const stream_1 = require("stream");
5
- const js_lib_1 = require("@naturalcycles/js-lib");
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 transformMap2(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
- const q = new js_lib_1.PQueue({
30
- concurrency,
31
- resolveOn: 'start',
32
- // debug: true,
33
- });
34
- return new stream_1.Transform({
35
- objectMode: true,
36
- async final(cb) {
37
- // console.log('transformMap final', {index}, q.inFlight, q.queueSize)
38
- // wait for the current inFlight jobs to complete and push their results
39
- await q.onIdle();
40
- logErrorStats(logger, true);
41
- await beforeFinal?.(); // call beforeFinal if defined
42
- if (collectedErrors.length) {
43
- // emit Aggregated error
44
- cb(new js_lib_1.AggregatedError(collectedErrors));
45
- }
46
- else {
47
- // emit no error
48
- cb();
49
- }
50
- },
51
- async transform(chunk, _encoding, cb) {
52
- index++;
53
- // console.log('transform', {index})
54
- // Stop processing if THROW_IMMEDIATELY mode is used
55
- if (isRejected && errorMode === js_lib_1.ErrorMode.THROW_IMMEDIATELY)
56
- return cb();
57
- // It resolves when it is successfully STARTED execution.
58
- // If it's queued instead - it'll wait and resolve only upon START.
59
- await q.push(async () => {
60
- try {
61
- 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)
62
- const res = await mapper(chunk, currentIndex);
63
- const passedResults = await (0, js_lib_1.pFilter)(flattenArrayOutput && Array.isArray(res) ? res : [res], async (r) => await predicate(r, currentIndex));
64
- passedResults.forEach(r => this.push(r));
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 as Error)
80
- return this.emit('error', err);
81
- }
82
- if (errorMode === js_lib_1.ErrorMode.THROW_AGGREGATED) {
83
- collectedErrors.push(err);
84
- }
85
- }
86
- });
87
- // Resolved, which means it STARTED processing
88
- // This means we can take more load
89
- cb();
90
- },
91
- });
92
- function logErrorStats(logger, final = false) {
93
- if (!errors)
94
- return;
95
- logger.log(`${metric} ${final ? 'final ' : ''}errors: ${(0, colors_1.yellow)(errors)}`);
96
- }
97
- }
98
- exports.transformMap2 = transformMap2;
@@ -1,134 +0,0 @@
1
- import { Transform } from 'stream'
2
- import {
3
- AggregatedError,
4
- AsyncMapper,
5
- CommonLogger,
6
- ErrorMode,
7
- pFilter,
8
- PQueue,
9
- } from '@naturalcycles/js-lib'
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 transformMap2<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
- const q = new PQueue({
51
- concurrency,
52
- resolveOn: 'start',
53
- // debug: true,
54
- })
55
-
56
- return new Transform({
57
- objectMode: true,
58
-
59
- async final(cb) {
60
- // console.log('transformMap final', {index}, q.inFlight, q.queueSize)
61
-
62
- // wait for the current inFlight jobs to complete and push their results
63
- await q.onIdle()
64
-
65
- logErrorStats(logger, true)
66
-
67
- await beforeFinal?.() // call beforeFinal if defined
68
-
69
- if (collectedErrors.length) {
70
- // emit Aggregated error
71
- cb(new AggregatedError(collectedErrors))
72
- } else {
73
- // emit no error
74
- cb()
75
- }
76
- },
77
-
78
- async transform(this: Transform, chunk: IN, _encoding, cb) {
79
- index++
80
- // console.log('transform', {index})
81
-
82
- // Stop processing if THROW_IMMEDIATELY mode is used
83
- if (isRejected && errorMode === ErrorMode.THROW_IMMEDIATELY) return cb()
84
-
85
- // It resolves when it is successfully STARTED execution.
86
- // If it's queued instead - it'll wait and resolve only upon START.
87
- await q.push(async () => {
88
- try {
89
- 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)
90
- const res = await mapper(chunk, currentIndex)
91
- const passedResults = await pFilter(
92
- flattenArrayOutput && Array.isArray(res) ? res : [res],
93
- async r => await predicate(r, currentIndex),
94
- )
95
-
96
- passedResults.forEach(r => this.push(r))
97
- } catch (err) {
98
- logger.error(err)
99
-
100
- errors++
101
-
102
- logErrorStats(logger)
103
-
104
- if (onError) {
105
- try {
106
- onError(err, chunk)
107
- } catch {}
108
- }
109
-
110
- if (errorMode === ErrorMode.THROW_IMMEDIATELY) {
111
- isRejected = true
112
- // Emit error immediately
113
- // return cb(err as Error)
114
- return this.emit('error', err as Error)
115
- }
116
-
117
- if (errorMode === ErrorMode.THROW_AGGREGATED) {
118
- collectedErrors.push(err as Error)
119
- }
120
- }
121
- })
122
-
123
- // Resolved, which means it STARTED processing
124
- // This means we can take more load
125
- cb()
126
- },
127
- })
128
-
129
- function logErrorStats(logger: CommonLogger, final = false): void {
130
- if (!errors) return
131
-
132
- logger.log(`${metric} ${final ? 'final ' : ''}errors: ${yellow(errors)}`)
133
- }
134
- }