@naturalcycles/nodejs-lib 15.71.0 → 15.72.1

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.
@@ -129,7 +129,7 @@ class Exec2 {
129
129
  * log: true
130
130
  */
131
131
  async spawnAsync(cmd, opt = {}) {
132
- const { shell = true, cwd, env, passProcessEnv = true, forceColor = hasColors } = opt;
132
+ const { shell = true, cwd, env, passProcessEnv = true, forceColor = hasColors, stdio = 'inherit', } = opt;
133
133
  opt.log ??= true; // by default log should be true, as we are printing the output
134
134
  opt.logStart ??= opt.log;
135
135
  opt.logFinish ??= opt.log;
@@ -139,7 +139,7 @@ class Exec2 {
139
139
  const p = spawn(cmd, opt.args || [], {
140
140
  shell,
141
141
  cwd,
142
- stdio: 'inherit',
142
+ stdio,
143
143
  env: {
144
144
  ...(passProcessEnv ? process.env : {}),
145
145
  ...(forceColor ? { FORCE_COLOR: '1' } : {}),
@@ -43,11 +43,12 @@ class ProcessUtil {
43
43
  };
44
44
  }
45
45
  cpuInfo() {
46
- const c = os.cpus()[0];
46
+ const cpus = os.cpus();
47
+ const c = cpus[0];
47
48
  return {
48
- count: os.cpus().length,
49
- model: c.model,
50
- speed: c.speed,
49
+ count: cpus.length,
50
+ model: c?.model || '',
51
+ speed: c?.speed || 0,
51
52
  };
52
53
  }
53
54
  async cpuPercent(ms) {
@@ -61,6 +62,10 @@ class ProcessUtil {
61
62
  const endTotal = stats2.total;
62
63
  const idle = endIdle - startIdle;
63
64
  const total = endTotal - startTotal;
65
+ if (!total) {
66
+ resolve(0);
67
+ return;
68
+ }
64
69
  const perc = idle / total;
65
70
  resolve(Math.round((1 - perc) * 100));
66
71
  }, ms);
@@ -1,7 +1,7 @@
1
1
  import { type Transform } from 'node:stream';
2
2
  import type { ReadableStream as WebReadableStream } from 'node:stream/web';
3
3
  import { type ZlibOptions, type ZstdOptions } from 'node:zlib';
4
- import { type AbortableAsyncMapper, type AsyncIndexedMapper, type AsyncPredicate, type END, type IndexedMapper, type NonNegativeInteger, type PositiveInteger, type Predicate, type SKIP } from '@naturalcycles/js-lib/types';
4
+ import { type AbortableAsyncMapper, type AsyncIndexedMapper, type AsyncPredicate, type END, type IndexedMapper, type Integer, type NonNegativeInteger, type PositiveInteger, type Predicate, type SKIP } from '@naturalcycles/js-lib/types';
5
5
  import type { ReadableTyped, TransformOptions, TransformTyped, WritableTyped } from './stream.model.js';
6
6
  import { type TransformLogProgressOptions } from './transform/transformLogProgress.js';
7
7
  import { type TransformMapOptions } from './transform/transformMap.js';
@@ -97,11 +97,19 @@ export declare class Pipeline<T = unknown> {
97
97
  parseJson<TO = unknown>(this: Pipeline<Buffer> | Pipeline<Uint8Array> | Pipeline<string>): Pipeline<TO>;
98
98
  gzip(this: Pipeline<Uint8Array>, opt?: ZlibOptions): Pipeline<Uint8Array>;
99
99
  gunzip(this: Pipeline<Uint8Array>, opt?: ZlibOptions): Pipeline<Uint8Array>;
100
- zstdCompress(this: Pipeline<Uint8Array>, opt?: ZstdOptions): Pipeline<Uint8Array>;
100
+ zstdCompress(this: Pipeline<Uint8Array>, level?: Integer, // defaults to 3
101
+ opt?: ZstdOptions): Pipeline<Uint8Array>;
101
102
  zstdDecompress(this: Pipeline<Uint8Array>, opt?: ZstdOptions): Pipeline<Uint8Array>;
102
103
  toArray(opt?: TransformOptions): Promise<T[]>;
103
104
  toFile(outputFilePath: string): Promise<void>;
104
- toNDJsonFile(outputFilePath: string): Promise<void>;
105
+ /**
106
+ * level corresponds to zstd compression level (if filename ends with .zst),
107
+ * or gzip compression level (if filename ends with .gz).
108
+ * Default levels are:
109
+ * gzip: 6
110
+ * zlib: 3 (optimized for throughput, not size, may be larger than gzip at its default level)
111
+ */
112
+ toNDJsonFile(outputFilePath: string, level?: Integer): Promise<void>;
105
113
  to(destination: WritableTyped<T>): Promise<void>;
106
114
  forEach(fn: AsyncIndexedMapper<T, void>, opt?: TransformMapOptions<T, void> & TransformLogProgressOptions<T>): Promise<void>;
107
115
  forEachSync(fn: IndexedMapper<T, void>, opt?: TransformMapSyncOptions<T, void> & TransformLogProgressOptions<T>): Promise<void>;
@@ -4,6 +4,7 @@ import { createGzip, createUnzip, createZstdCompress, createZstdDecompress, } fr
4
4
  import { createAbortableSignal } from '@naturalcycles/js-lib';
5
5
  import { _passthroughPredicate, } from '@naturalcycles/js-lib/types';
6
6
  import { fs2 } from '../fs/fs2.js';
7
+ import { zstdLevelToOptions } from '../zip/zip.util.js';
7
8
  import { createReadStreamAsNDJson } from './ndjson/createReadStreamAsNDJson.js';
8
9
  import { transformJsonParse } from './ndjson/transformJsonParse.js';
9
10
  import { transformToNDJson } from './ndjson/transformToNDJson.js';
@@ -253,11 +254,9 @@ export class Pipeline {
253
254
  this.objectMode = false;
254
255
  return this;
255
256
  }
256
- zstdCompress(opt) {
257
- this.transforms.push(createZstdCompress({
258
- // chunkSize: 64 * 1024, // no observed speedup
259
- ...opt,
260
- }));
257
+ zstdCompress(level, // defaults to 3
258
+ opt) {
259
+ this.transforms.push(createZstdCompress(zstdLevelToOptions(level, opt)));
261
260
  this.objectMode = false;
262
261
  return this;
263
262
  }
@@ -280,18 +279,24 @@ export class Pipeline {
280
279
  this.destination = fs2.createWriteStream(outputFilePath);
281
280
  await this.run();
282
281
  }
283
- async toNDJsonFile(outputFilePath) {
282
+ /**
283
+ * level corresponds to zstd compression level (if filename ends with .zst),
284
+ * or gzip compression level (if filename ends with .gz).
285
+ * Default levels are:
286
+ * gzip: 6
287
+ * zlib: 3 (optimized for throughput, not size, may be larger than gzip at its default level)
288
+ */
289
+ async toNDJsonFile(outputFilePath, level) {
284
290
  fs2.ensureFile(outputFilePath);
285
291
  this.transforms.push(transformToNDJson());
286
292
  if (outputFilePath.endsWith('.gz')) {
287
293
  this.transforms.push(createGzip({
288
- // chunkSize: 64 * 1024, // no observed speedup
294
+ level,
295
+ // chunkSize: 64 * 1024, // no observed speedup
289
296
  }));
290
297
  }
291
298
  else if (outputFilePath.endsWith('.zst')) {
292
- this.transforms.push(createZstdCompress({
293
- // chunkSize: 64 * 1024, // no observed speedup
294
- }));
299
+ this.transforms.push(createZstdCompress(zstdLevelToOptions(level)));
295
300
  }
296
301
  this.destination = fs2.createWriteStream(outputFilePath, {
297
302
  // highWaterMark: 64 * 1024, // no observed speedup
@@ -34,4 +34,5 @@ export interface BaseWorkerData {
34
34
  * @default 1000
35
35
  */
36
36
  logEvery: number;
37
+ silent?: boolean;
37
38
  }
@@ -1,4 +1,5 @@
1
1
  import type { ZlibOptions, ZstdOptions } from 'node:zlib';
2
+ import type { Integer } from '@naturalcycles/js-lib/types';
2
3
  export declare function decompressZstdOrInflateToString(buf: Buffer): Promise<string>;
3
4
  /**
4
5
  * Detects if Buffer is zstd-compressed.
@@ -29,7 +30,9 @@ export declare function gunzipBuffer(buf: Buffer, options?: ZlibOptions): Promis
29
30
  */
30
31
  export declare function gzipString(s: string, options?: ZlibOptions): Promise<Buffer<ArrayBuffer>>;
31
32
  export declare function gunzipToString(buf: Buffer, options?: ZlibOptions): Promise<string>;
32
- export declare function zstdCompress(input: Buffer | string, options?: ZstdOptions): Promise<Buffer<ArrayBuffer>>;
33
+ export declare function zstdCompress(input: Buffer | string, level?: Integer, // defaults to 3
34
+ options?: ZstdOptions): Promise<Buffer<ArrayBuffer>>;
35
+ export declare function zstdLevelToOptions(level: Integer | undefined, opt?: ZstdOptions): ZstdOptions;
33
36
  export declare function zstdDecompressToString(input: Buffer, options?: ZstdOptions): Promise<string>;
34
37
  export declare function zstdDecompress(input: Buffer, options?: ZstdOptions): Promise<Buffer<ArrayBuffer>>;
35
38
  export declare function isZstdBuffer(input: Buffer): boolean;
@@ -59,8 +59,20 @@ export async function gzipString(s, options) {
59
59
  export async function gunzipToString(buf, options) {
60
60
  return (await gunzipBuffer(buf, options)).toString();
61
61
  }
62
- export async function zstdCompress(input, options = {}) {
63
- return await zstdCompressAsync(input, options);
62
+ export async function zstdCompress(input, level, // defaults to 3
63
+ options = {}) {
64
+ return await zstdCompressAsync(input, zstdLevelToOptions(level, options));
65
+ }
66
+ export function zstdLevelToOptions(level, opt = {}) {
67
+ if (!level)
68
+ return opt;
69
+ return {
70
+ ...opt,
71
+ params: {
72
+ ...opt.params,
73
+ [zlib.constants.ZSTD_c_compressionLevel]: level,
74
+ },
75
+ };
64
76
  }
65
77
  export async function zstdDecompressToString(input, options = {}) {
66
78
  return (await zstdDecompressAsync(input, options)).toString();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.71.0",
4
+ "version": "15.72.1",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -19,7 +19,7 @@
19
19
  "yargs": "^18"
20
20
  },
21
21
  "devDependencies": {
22
- "@naturalcycles/dev-lib": "18.4.2"
22
+ "@naturalcycles/dev-lib": "20.20.0"
23
23
  },
24
24
  "exports": {
25
25
  ".": "./dist/index.js",
@@ -148,7 +148,14 @@ class Exec2 {
148
148
  * log: true
149
149
  */
150
150
  async spawnAsync(cmd: string, opt: SpawnOptions = {}): Promise<void> {
151
- const { shell = true, cwd, env, passProcessEnv = true, forceColor = hasColors } = opt
151
+ const {
152
+ shell = true,
153
+ cwd,
154
+ env,
155
+ passProcessEnv = true,
156
+ forceColor = hasColors,
157
+ stdio = 'inherit',
158
+ } = opt
152
159
  opt.log ??= true // by default log should be true, as we are printing the output
153
160
  opt.logStart ??= opt.log
154
161
  opt.logFinish ??= opt.log
@@ -159,7 +166,7 @@ class Exec2 {
159
166
  const p = spawn(cmd, opt.args || [], {
160
167
  shell,
161
168
  cwd,
162
- stdio: 'inherit',
169
+ stdio,
163
170
  env: {
164
171
  ...(passProcessEnv ? process.env : {}),
165
172
  ...(forceColor ? { FORCE_COLOR: '1' } : {}),
@@ -64,11 +64,12 @@ class ProcessUtil {
64
64
  }
65
65
 
66
66
  cpuInfo(): { count: number; model: string; speed: number } {
67
- const c = os.cpus()[0]!
67
+ const cpus = os.cpus()
68
+ const c = cpus[0]
68
69
  return {
69
- count: os.cpus().length,
70
- model: c.model,
71
- speed: c.speed,
70
+ count: cpus.length,
71
+ model: c?.model || '',
72
+ speed: c?.speed || 0,
72
73
  }
73
74
  }
74
75
 
@@ -85,8 +86,12 @@ class ProcessUtil {
85
86
 
86
87
  const idle = endIdle - startIdle
87
88
  const total = endTotal - startTotal
88
- const perc = idle / total
89
+ if (!total) {
90
+ resolve(0)
91
+ return
92
+ }
89
93
 
94
+ const perc = idle / total
90
95
  resolve(Math.round((1 - perc) * 100))
91
96
  }, ms)
92
97
  })
@@ -24,6 +24,7 @@ import {
24
24
  type SKIP,
25
25
  } from '@naturalcycles/js-lib/types'
26
26
  import { fs2 } from '../fs/fs2.js'
27
+ import { zstdLevelToOptions } from '../zip/zip.util.js'
27
28
  import { createReadStreamAsNDJson } from './ndjson/createReadStreamAsNDJson.js'
28
29
  import { transformJsonParse } from './ndjson/transformJsonParse.js'
29
30
  import { transformToNDJson } from './ndjson/transformToNDJson.js'
@@ -349,13 +350,12 @@ export class Pipeline<T = unknown> {
349
350
  return this as any
350
351
  }
351
352
 
352
- zstdCompress(this: Pipeline<Uint8Array>, opt?: ZstdOptions): Pipeline<Uint8Array> {
353
- this.transforms.push(
354
- createZstdCompress({
355
- // chunkSize: 64 * 1024, // no observed speedup
356
- ...opt,
357
- }),
358
- )
353
+ zstdCompress(
354
+ this: Pipeline<Uint8Array>,
355
+ level?: Integer, // defaults to 3
356
+ opt?: ZstdOptions,
357
+ ): Pipeline<Uint8Array> {
358
+ this.transforms.push(createZstdCompress(zstdLevelToOptions(level, opt)))
359
359
  this.objectMode = false
360
360
  return this as any
361
361
  }
@@ -384,21 +384,25 @@ export class Pipeline<T = unknown> {
384
384
  await this.run()
385
385
  }
386
386
 
387
- async toNDJsonFile(outputFilePath: string): Promise<void> {
387
+ /**
388
+ * level corresponds to zstd compression level (if filename ends with .zst),
389
+ * or gzip compression level (if filename ends with .gz).
390
+ * Default levels are:
391
+ * gzip: 6
392
+ * zlib: 3 (optimized for throughput, not size, may be larger than gzip at its default level)
393
+ */
394
+ async toNDJsonFile(outputFilePath: string, level?: Integer): Promise<void> {
388
395
  fs2.ensureFile(outputFilePath)
389
396
  this.transforms.push(transformToNDJson())
390
397
  if (outputFilePath.endsWith('.gz')) {
391
398
  this.transforms.push(
392
399
  createGzip({
400
+ level,
393
401
  // chunkSize: 64 * 1024, // no observed speedup
394
402
  }),
395
403
  )
396
404
  } else if (outputFilePath.endsWith('.zst')) {
397
- this.transforms.push(
398
- createZstdCompress({
399
- // chunkSize: 64 * 1024, // no observed speedup
400
- }),
401
- )
405
+ this.transforms.push(createZstdCompress(zstdLevelToOptions(level)))
402
406
  }
403
407
  this.destination = fs2.createWriteStream(outputFilePath, {
404
408
  // highWaterMark: 64 * 1024, // no observed speedup
@@ -42,4 +42,6 @@ export interface BaseWorkerData {
42
42
  * @default 1000
43
43
  */
44
44
  logEvery: number
45
+
46
+ silent?: boolean
45
47
  }
@@ -1,7 +1,7 @@
1
1
  const started = Date.now()
2
2
  import { workerData, parentPort } from 'node:worker_threads'
3
3
  import { inspect } from 'node:util'
4
- const { workerFile, workerIndex, logEvery = 1000, metric = 'worker' } = workerData || {}
4
+ const { workerFile, workerIndex, logEvery = 1000, metric = 'worker', silent } = workerData || {}
5
5
 
6
6
  if (!workerFile) {
7
7
  throw new Error('workerData.workerFile is required!')
@@ -17,7 +17,7 @@ try {
17
17
  const { WorkerClass } = await import(workerFile)
18
18
  const worker = new WorkerClass(workerData)
19
19
 
20
- console.log(`${metric}#${workerIndex} loaded in ${Date.now() - started} ms`)
20
+ log(`${metric}#${workerIndex} loaded in ${Date.now() - started} ms`)
21
21
 
22
22
  let errors = 0
23
23
  let processed = 0
@@ -52,7 +52,7 @@ parentPort.on('message', async msg => {
52
52
  })
53
53
 
54
54
  errors++
55
- console.log(`${metric}#${workerIndex} errors: ${errors}`)
55
+ log(`${metric}#${workerIndex} errors: ${errors}`)
56
56
  }
57
57
  })
58
58
 
@@ -64,7 +64,7 @@ const inspectOpt = {
64
64
  function logStats(final) {
65
65
  const { rss, heapUsed, heapTotal, external } = process.memoryUsage()
66
66
 
67
- console.log(
67
+ log(
68
68
  inspect(
69
69
  {
70
70
  [`${metric}${workerIndex}`]: processed,
@@ -83,3 +83,8 @@ function logStats(final) {
83
83
  function mb(b) {
84
84
  return Math.round(b / (1024 * 1024))
85
85
  }
86
+
87
+ function log(...args) {
88
+ if (silent) return
89
+ console.log(...args)
90
+ }
@@ -1,6 +1,7 @@
1
1
  import { promisify } from 'node:util'
2
2
  import type { ZlibOptions, ZstdOptions } from 'node:zlib'
3
3
  import zlib from 'node:zlib'
4
+ import type { Integer } from '@naturalcycles/js-lib/types'
4
5
 
5
6
  const deflate = promisify(zlib.deflate.bind(zlib))
6
7
  const inflate = promisify(zlib.inflate.bind(zlib))
@@ -89,9 +90,22 @@ export async function gunzipToString(buf: Buffer, options?: ZlibOptions): Promis
89
90
 
90
91
  export async function zstdCompress(
91
92
  input: Buffer | string,
93
+ level?: Integer, // defaults to 3
92
94
  options: ZstdOptions = {},
93
95
  ): Promise<Buffer<ArrayBuffer>> {
94
- return await zstdCompressAsync(input, options)
96
+ return await zstdCompressAsync(input, zstdLevelToOptions(level, options))
97
+ }
98
+
99
+ export function zstdLevelToOptions(level: Integer | undefined, opt: ZstdOptions = {}): ZstdOptions {
100
+ if (!level) return opt
101
+
102
+ return {
103
+ ...opt,
104
+ params: {
105
+ ...opt.params,
106
+ [zlib.constants.ZSTD_c_compressionLevel]: level,
107
+ },
108
+ }
95
109
  }
96
110
 
97
111
  export async function zstdDecompressToString(