@naturalcycles/nodejs-lib 15.65.2 → 15.66.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.
@@ -1,16 +1,16 @@
1
1
  import type { ReadableTyped } from '../stream.model.js';
2
2
  /**
3
3
  Returns a Readable of [already parsed] NDJSON objects.
4
-
4
+
5
5
  Replaces a list of operations:
6
6
  - requireFileToExist(inputPath)
7
7
  - fs.createReadStream
8
8
  - createUnzip (only if path ends with '.gz')
9
9
  - transformSplitOnNewline
10
10
  - transformJsonParse
11
-
11
+
12
12
  To add a Limit or Offset: just add .take() or .drop(), example:
13
-
13
+
14
14
  createReadStreamAsNDJson().take(100)
15
15
  */
16
16
  export declare function createReadStreamAsNDJson<ROW = any>(inputPath: string): ReadableTyped<ROW>;
@@ -1,18 +1,18 @@
1
- import { createUnzip } from 'node:zlib';
1
+ import { createUnzip, createZstdDecompress } from 'node:zlib';
2
2
  import { fs2 } from '../../fs/fs2.js';
3
3
  import { transformSplitOnNewline } from '../transform/transformSplit.js';
4
4
  /**
5
5
  Returns a Readable of [already parsed] NDJSON objects.
6
-
6
+
7
7
  Replaces a list of operations:
8
8
  - requireFileToExist(inputPath)
9
9
  - fs.createReadStream
10
10
  - createUnzip (only if path ends with '.gz')
11
11
  - transformSplitOnNewline
12
12
  - transformJsonParse
13
-
13
+
14
14
  To add a Limit or Offset: just add .take() or .drop(), example:
15
-
15
+
16
16
  createReadStreamAsNDJson().take(100)
17
17
  */
18
18
  export function createReadStreamAsNDJson(inputPath) {
@@ -27,6 +27,11 @@ export function createReadStreamAsNDJson(inputPath) {
27
27
  chunkSize: 64 * 1024, // speedup from ~3200 to 3800 rps!
28
28
  }));
29
29
  }
30
+ else if (inputPath.endsWith('.zst')) {
31
+ stream = stream.pipe(createZstdDecompress({
32
+ chunkSize: 64 * 1024, // todo: test it
33
+ }));
34
+ }
30
35
  return stream.pipe(transformSplitOnNewline()).map(line => JSON.parse(line));
31
36
  // For some crazy reason .map is much faster than transformJsonParse!
32
37
  // ~5000 vs ~4000 rps !!!
@@ -1,6 +1,6 @@
1
1
  import { type Transform } from 'node:stream';
2
2
  import type { ReadableStream as WebReadableStream } from 'node:stream/web';
3
- import { type ZlibOptions } from 'node:zlib';
3
+ import { type ZlibOptions, type ZstdOptions } from 'node:zlib';
4
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';
5
5
  import type { ReadableTyped, TransformOptions, TransformTyped, WritableTyped } from './stream.model.js';
6
6
  import { type TransformLogProgressOptions } from './transform/transformLogProgress.js';
@@ -92,6 +92,8 @@ export declare class Pipeline<T = unknown> {
92
92
  parseJson<TO = unknown>(this: Pipeline<Buffer> | Pipeline<Uint8Array> | Pipeline<string>): Pipeline<TO>;
93
93
  gzip(this: Pipeline<Uint8Array>, opt?: ZlibOptions): Pipeline<Uint8Array>;
94
94
  gunzip(this: Pipeline<Uint8Array>, opt?: ZlibOptions): Pipeline<Uint8Array>;
95
+ zstdCompress(this: Pipeline<Uint8Array>, opt?: ZstdOptions): Pipeline<Uint8Array>;
96
+ zstdDecompress(this: Pipeline<Uint8Array>, opt?: ZstdOptions): Pipeline<Uint8Array>;
95
97
  toArray(opt?: TransformOptions): Promise<T[]>;
96
98
  toFile(outputFilePath: string): Promise<void>;
97
99
  toNDJsonFile(outputFilePath: string): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  import { Readable } from 'node:stream';
2
2
  import { pipeline } from 'node:stream/promises';
3
- import { createGzip, createUnzip } from 'node:zlib';
3
+ import { createGzip, createUnzip, createZstdCompress, createZstdDecompress, } from 'node:zlib';
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';
@@ -25,12 +25,10 @@ import { transformThrottle } from './transform/transformThrottle.js';
25
25
  import { writablePushToArray } from './writable/writablePushToArray.js';
26
26
  import { writableVoid } from './writable/writableVoid.js';
27
27
  export class Pipeline {
28
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
29
28
  source;
30
29
  transforms = [];
31
30
  destination;
32
31
  readableLimit;
33
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
34
32
  objectMode;
35
33
  abortableSignal = createAbortableSignal();
36
34
  constructor(source, objectMode = true) {
@@ -247,6 +245,22 @@ export class Pipeline {
247
245
  this.objectMode = false;
248
246
  return this;
249
247
  }
248
+ zstdCompress(opt) {
249
+ this.transforms.push(createZstdCompress({
250
+ // chunkSize: 64 * 1024, // no observed speedup
251
+ ...opt,
252
+ }));
253
+ this.objectMode = false;
254
+ return this;
255
+ }
256
+ zstdDecompress(opt) {
257
+ this.transforms.push(createZstdDecompress({
258
+ chunkSize: 64 * 1024, // todo: test it
259
+ ...opt,
260
+ }));
261
+ this.objectMode = false;
262
+ return this;
263
+ }
250
264
  async toArray(opt) {
251
265
  const arr = [];
252
266
  this.destination = writablePushToArray(arr, opt);
@@ -266,6 +280,11 @@ export class Pipeline {
266
280
  // chunkSize: 64 * 1024, // no observed speedup
267
281
  }));
268
282
  }
283
+ else if (outputFilePath.endsWith('.zst')) {
284
+ this.transforms.push(createZstdCompress({
285
+ // chunkSize: 64 * 1024, // no observed speedup
286
+ }));
287
+ }
269
288
  this.destination = fs2.createWriteStream(outputFilePath, {
270
289
  // highWaterMark: 64 * 1024, // no observed speedup
271
290
  });
@@ -32,11 +32,8 @@ export class ReadableCombined extends Readable {
32
32
  * If not defined - we are in Flowing mode, no limits in data flow.
33
33
  */
34
34
  lock;
35
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
36
35
  countIn = 0;
37
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
38
36
  countOut = 0;
39
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
40
37
  countReads = 0;
41
38
  async run() {
42
39
  const { logger } = this;
@@ -428,10 +428,21 @@ export function createAjv(opt) {
428
428
  modifying: false,
429
429
  errors: true,
430
430
  schemaType: 'number',
431
- validate: function validate(minProperties, data, _schema, _ctx) {
431
+ validate: function validate(minProperties, data, _schema, ctx) {
432
432
  if (typeof data !== 'object')
433
433
  return true;
434
- return Object.getOwnPropertyNames(data).length >= minProperties;
434
+ const numberOfProperties = Object.getOwnPropertyNames(data).length;
435
+ const isValid = numberOfProperties >= minProperties;
436
+ if (!isValid) {
437
+ ;
438
+ validate.errors = [
439
+ {
440
+ instancePath: ctx?.instancePath ?? '',
441
+ message: `must NOT have fewer than ${minProperties} properties`,
442
+ },
443
+ ];
444
+ }
445
+ return isValid;
435
446
  },
436
447
  });
437
448
  return ajv;
@@ -1,4 +1,4 @@
1
- import type { ZlibOptions } from 'node:zlib';
1
+ import type { ZlibOptions, ZstdOptions } from 'node:zlib';
2
2
  /**
3
3
  * deflateBuffer uses `deflate`.
4
4
  * It's 9 bytes shorter than `gzip`.
@@ -23,3 +23,6 @@ export declare function gunzipBuffer(buf: Buffer, options?: ZlibOptions): Promis
23
23
  */
24
24
  export declare function gzipString(s: string, options?: ZlibOptions): Promise<Buffer<ArrayBuffer>>;
25
25
  export declare function gunzipToString(buf: Buffer, options?: ZlibOptions): Promise<string>;
26
+ export declare function zstdCompress(input: Buffer | string, options?: ZstdOptions): Promise<Buffer<ArrayBuffer>>;
27
+ export declare function zstdDecompressToString(input: Buffer, options?: ZstdOptions): Promise<string>;
28
+ export declare function zstdDecompress(input: Buffer, options?: ZstdOptions): Promise<Buffer<ArrayBuffer>>;
@@ -4,15 +4,17 @@ const deflate = promisify(zlib.deflate.bind(zlib));
4
4
  const inflate = promisify(zlib.inflate.bind(zlib));
5
5
  const gzip = promisify(zlib.gzip.bind(zlib));
6
6
  const gunzip = promisify(zlib.gunzip.bind(zlib));
7
+ const zstdCompressAsync = promisify(zlib.zstdCompress.bind(zlib));
8
+ const zstdDecompressAsync = promisify(zlib.zstdDecompress.bind(zlib));
7
9
  /**
8
10
  * deflateBuffer uses `deflate`.
9
11
  * It's 9 bytes shorter than `gzip`.
10
12
  */
11
13
  export async function deflateBuffer(buf, options = {}) {
12
- return (await deflate(buf, options));
14
+ return await deflate(buf, options);
13
15
  }
14
16
  export async function inflateBuffer(buf, options = {}) {
15
- return (await inflate(buf, options));
17
+ return await inflate(buf, options);
16
18
  }
17
19
  /**
18
20
  * deflateString uses `deflate`.
@@ -29,10 +31,10 @@ export async function inflateToString(buf, options) {
29
31
  * It's 9 bytes longer than `deflate`.
30
32
  */
31
33
  export async function gzipBuffer(buf, options = {}) {
32
- return (await gzip(buf, options));
34
+ return await gzip(buf, options);
33
35
  }
34
36
  export async function gunzipBuffer(buf, options = {}) {
35
- return (await gunzip(buf, options));
37
+ return await gunzip(buf, options);
36
38
  }
37
39
  /**
38
40
  * gzipString uses `gzip`.
@@ -44,3 +46,12 @@ export async function gzipString(s, options) {
44
46
  export async function gunzipToString(buf, options) {
45
47
  return (await gunzipBuffer(buf, options)).toString();
46
48
  }
49
+ export async function zstdCompress(input, options = {}) {
50
+ return await zstdCompressAsync(input, options);
51
+ }
52
+ export async function zstdDecompressToString(input, options = {}) {
53
+ return (await zstdDecompressAsync(input, options)).toString();
54
+ }
55
+ export async function zstdDecompress(input, options = {}) {
56
+ return await zstdDecompressAsync(input, options);
57
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/nodejs-lib",
3
3
  "type": "module",
4
- "version": "15.65.2",
4
+ "version": "15.66.0",
5
5
  "dependencies": {
6
6
  "@naturalcycles/js-lib": "^15",
7
7
  "@types/js-yaml": "^4",
@@ -1,20 +1,20 @@
1
- import { createUnzip } from 'node:zlib'
1
+ import { createUnzip, createZstdDecompress } from 'node:zlib'
2
2
  import { fs2 } from '../../fs/fs2.js'
3
3
  import type { ReadableTyped } from '../stream.model.js'
4
4
  import { transformSplitOnNewline } from '../transform/transformSplit.js'
5
5
 
6
6
  /**
7
7
  Returns a Readable of [already parsed] NDJSON objects.
8
-
8
+
9
9
  Replaces a list of operations:
10
10
  - requireFileToExist(inputPath)
11
11
  - fs.createReadStream
12
12
  - createUnzip (only if path ends with '.gz')
13
13
  - transformSplitOnNewline
14
14
  - transformJsonParse
15
-
15
+
16
16
  To add a Limit or Offset: just add .take() or .drop(), example:
17
-
17
+
18
18
  createReadStreamAsNDJson().take(100)
19
19
  */
20
20
 
@@ -33,6 +33,12 @@ export function createReadStreamAsNDJson<ROW = any>(inputPath: string): Readable
33
33
  chunkSize: 64 * 1024, // speedup from ~3200 to 3800 rps!
34
34
  }),
35
35
  )
36
+ } else if (inputPath.endsWith('.zst')) {
37
+ stream = stream.pipe(
38
+ createZstdDecompress({
39
+ chunkSize: 64 * 1024, // todo: test it
40
+ }),
41
+ )
36
42
  }
37
43
 
38
44
  return stream.pipe(transformSplitOnNewline()).map(line => JSON.parse(line))
@@ -1,7 +1,14 @@
1
1
  import { Readable, type Transform } from 'node:stream'
2
2
  import { pipeline } from 'node:stream/promises'
3
3
  import type { ReadableStream as WebReadableStream } from 'node:stream/web'
4
- import { createGzip, createUnzip, type ZlibOptions } from 'node:zlib'
4
+ import {
5
+ createGzip,
6
+ createUnzip,
7
+ createZstdCompress,
8
+ createZstdDecompress,
9
+ type ZlibOptions,
10
+ type ZstdOptions,
11
+ } from 'node:zlib'
5
12
  import { createAbortableSignal } from '@naturalcycles/js-lib'
6
13
  import {
7
14
  _passthroughPredicate,
@@ -51,12 +58,11 @@ import { writablePushToArray } from './writable/writablePushToArray.js'
51
58
  import { writableVoid } from './writable/writableVoid.js'
52
59
 
53
60
  export class Pipeline<T = unknown> {
54
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
55
61
  private readonly source: Readable
56
62
  private transforms: NodeJS.ReadWriteStream[] = []
57
63
  private destination?: NodeJS.WritableStream
58
64
  private readableLimit?: Integer
59
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
65
+
60
66
  private objectMode: boolean
61
67
  private abortableSignal = createAbortableSignal()
62
68
 
@@ -334,6 +340,28 @@ export class Pipeline<T = unknown> {
334
340
  return this as any
335
341
  }
336
342
 
343
+ zstdCompress(this: Pipeline<Uint8Array>, opt?: ZstdOptions): Pipeline<Uint8Array> {
344
+ this.transforms.push(
345
+ createZstdCompress({
346
+ // chunkSize: 64 * 1024, // no observed speedup
347
+ ...opt,
348
+ }),
349
+ )
350
+ this.objectMode = false
351
+ return this as any
352
+ }
353
+
354
+ zstdDecompress(this: Pipeline<Uint8Array>, opt?: ZstdOptions): Pipeline<Uint8Array> {
355
+ this.transforms.push(
356
+ createZstdDecompress({
357
+ chunkSize: 64 * 1024, // todo: test it
358
+ ...opt,
359
+ }),
360
+ )
361
+ this.objectMode = false
362
+ return this as any
363
+ }
364
+
337
365
  async toArray(opt?: TransformOptions): Promise<T[]> {
338
366
  const arr: T[] = []
339
367
  this.destination = writablePushToArray(arr, opt)
@@ -356,6 +384,12 @@ export class Pipeline<T = unknown> {
356
384
  // chunkSize: 64 * 1024, // no observed speedup
357
385
  }),
358
386
  )
387
+ } else if (outputFilePath.endsWith('.zst')) {
388
+ this.transforms.push(
389
+ createZstdCompress({
390
+ // chunkSize: 64 * 1024, // no observed speedup
391
+ }),
392
+ )
359
393
  }
360
394
  this.destination = fs2.createWriteStream(outputFilePath, {
361
395
  // highWaterMark: 64 * 1024, // no observed speedup
@@ -40,11 +40,10 @@ export class ReadableCombined<T> extends Readable implements ReadableTyped<T> {
40
40
  */
41
41
  private lock?: DeferredPromise
42
42
 
43
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
44
43
  private countIn = 0
45
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
44
+
46
45
  private countOut = 0
47
- // biome-ignore lint/correctness/noUnusedPrivateClassMembers: ok
46
+
48
47
  private countReads = 0
49
48
 
50
49
  private async run(): Promise<void> {
@@ -75,7 +75,7 @@ export function transformMultiThreaded<IN, OUT>(
75
75
 
76
76
  worker.on('error', err => {
77
77
  console.error(`Worker ${workerIndex} error`, err)
78
- workerDonePromises[workerIndex]!.reject(err)
78
+ workerDonePromises[workerIndex]!.reject(err as Error)
79
79
  })
80
80
 
81
81
  worker.on('exit', _exitCode => {
@@ -492,9 +492,21 @@ export function createAjv(opt?: Options): Ajv2020 {
492
492
  modifying: false,
493
493
  errors: true,
494
494
  schemaType: 'number',
495
- validate: function validate(minProperties: number, data: AnyObject, _schema, _ctx) {
495
+ validate: function validate(minProperties: number, data: AnyObject, _schema, ctx) {
496
496
  if (typeof data !== 'object') return true
497
- return Object.getOwnPropertyNames(data).length >= minProperties
497
+
498
+ const numberOfProperties = Object.getOwnPropertyNames(data).length
499
+ const isValid = numberOfProperties >= minProperties
500
+ if (!isValid) {
501
+ ;(validate as any).errors = [
502
+ {
503
+ instancePath: ctx?.instancePath ?? '',
504
+ message: `must NOT have fewer than ${minProperties} properties`,
505
+ },
506
+ ]
507
+ }
508
+
509
+ return isValid
498
510
  },
499
511
  })
500
512
 
@@ -1,11 +1,13 @@
1
1
  import { promisify } from 'node:util'
2
- import type { ZlibOptions } from 'node:zlib'
2
+ import type { ZlibOptions, ZstdOptions } from 'node:zlib'
3
3
  import zlib from 'node:zlib'
4
4
 
5
5
  const deflate = promisify(zlib.deflate.bind(zlib))
6
6
  const inflate = promisify(zlib.inflate.bind(zlib))
7
7
  const gzip = promisify(zlib.gzip.bind(zlib))
8
8
  const gunzip = promisify(zlib.gunzip.bind(zlib))
9
+ const zstdCompressAsync = promisify(zlib.zstdCompress.bind(zlib))
10
+ const zstdDecompressAsync = promisify(zlib.zstdDecompress.bind(zlib))
9
11
 
10
12
  /**
11
13
  * deflateBuffer uses `deflate`.
@@ -15,14 +17,14 @@ export async function deflateBuffer(
15
17
  buf: Buffer,
16
18
  options: ZlibOptions = {},
17
19
  ): Promise<Buffer<ArrayBuffer>> {
18
- return (await deflate(buf, options)) as Buffer<ArrayBuffer>
20
+ return await deflate(buf, options)
19
21
  }
20
22
 
21
23
  export async function inflateBuffer(
22
24
  buf: Buffer,
23
25
  options: ZlibOptions = {},
24
26
  ): Promise<Buffer<ArrayBuffer>> {
25
- return (await inflate(buf, options)) as Buffer<ArrayBuffer>
27
+ return await inflate(buf, options)
26
28
  }
27
29
 
28
30
  /**
@@ -48,14 +50,14 @@ export async function gzipBuffer(
48
50
  buf: Buffer,
49
51
  options: ZlibOptions = {},
50
52
  ): Promise<Buffer<ArrayBuffer>> {
51
- return (await gzip(buf, options)) as Buffer<ArrayBuffer>
53
+ return await gzip(buf, options)
52
54
  }
53
55
 
54
56
  export async function gunzipBuffer(
55
57
  buf: Buffer,
56
58
  options: ZlibOptions = {},
57
59
  ): Promise<Buffer<ArrayBuffer>> {
58
- return (await gunzip(buf, options)) as Buffer<ArrayBuffer>
60
+ return await gunzip(buf, options)
59
61
  }
60
62
 
61
63
  /**
@@ -69,3 +71,24 @@ export async function gzipString(s: string, options?: ZlibOptions): Promise<Buff
69
71
  export async function gunzipToString(buf: Buffer, options?: ZlibOptions): Promise<string> {
70
72
  return (await gunzipBuffer(buf, options)).toString()
71
73
  }
74
+
75
+ export async function zstdCompress(
76
+ input: Buffer | string,
77
+ options: ZstdOptions = {},
78
+ ): Promise<Buffer<ArrayBuffer>> {
79
+ return await zstdCompressAsync(input, options)
80
+ }
81
+
82
+ export async function zstdDecompressToString(
83
+ input: Buffer,
84
+ options: ZstdOptions = {},
85
+ ): Promise<string> {
86
+ return (await zstdDecompressAsync(input, options)).toString()
87
+ }
88
+
89
+ export async function zstdDecompress(
90
+ input: Buffer,
91
+ options: ZstdOptions = {},
92
+ ): Promise<Buffer<ArrayBuffer>> {
93
+ return await zstdDecompressAsync(input, options)
94
+ }