@naturalcycles/nodejs-lib 15.130.1 → 15.132.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.
Files changed (37) hide show
  1. package/dist/fs/fs2.d.ts +8 -8
  2. package/dist/fs/fs2.js +6 -8
  3. package/dist/index.d.ts +0 -1
  4. package/dist/index.js +0 -1
  5. package/dist/secret/secrets-encrypt.util.js +4 -7
  6. package/dist/security/crypto.util.d.ts +6 -6
  7. package/dist/security/hash.util.d.ts +12 -12
  8. package/dist/security/hash.util.js +12 -4
  9. package/dist/stream/index.d.ts +0 -1
  10. package/dist/stream/index.js +0 -1
  11. package/dist/stream/pipeline.d.ts +2 -7
  12. package/dist/stream/pipeline.js +0 -8
  13. package/dist/stream/transform/transformChunk.d.ts +2 -2
  14. package/dist/stream/transform/transformChunk.js +1 -0
  15. package/dist/validation/timezones.js +2 -2
  16. package/dist/zip/zip2.d.ts +25 -25
  17. package/dist/zip/zip2.js +2 -2
  18. package/dist/zip/zipWriter.d.ts +3 -3
  19. package/dist/zip/zipWriter.js +1 -1
  20. package/package.json +2 -2
  21. package/src/fs/fs2.ts +12 -15
  22. package/src/index.ts +0 -1
  23. package/src/secret/secrets-encrypt.util.ts +4 -7
  24. package/src/security/crypto.util.ts +19 -7
  25. package/src/security/hash.util.ts +25 -16
  26. package/src/stream/index.ts +0 -1
  27. package/src/stream/pipeline.ts +2 -11
  28. package/src/stream/transform/transformChunk.ts +3 -2
  29. package/src/validation/timezones.ts +2 -2
  30. package/src/zip/zip2.ts +30 -28
  31. package/src/zip/zipWriter.ts +9 -5
  32. package/dist/buffer/buffer.util.d.ts +0 -4
  33. package/dist/buffer/buffer.util.js +0 -10
  34. package/dist/stream/transform/transformWarmup.d.ts +0 -24
  35. package/dist/stream/transform/transformWarmup.js +0 -79
  36. package/src/buffer/buffer.util.ts +0 -12
  37. package/src/stream/transform/transformWarmup.ts +0 -105
@@ -8,7 +8,10 @@ const algorithm = 'aes-256-cbc'
8
8
  /**
9
9
  * Using aes-256-cbc.
10
10
  */
11
- export function encryptRandomIVBuffer(input: Buffer, secretKeyBuffer: Buffer): Buffer {
11
+ export function encryptRandomIVBuffer(
12
+ input: Uint8Array,
13
+ secretKeyBuffer: Uint8Array,
14
+ ): Buffer<ArrayBuffer> {
12
15
  // sha256 to match aes-256 key length
13
16
  const key = sha256AsBuffer(secretKeyBuffer)
14
17
 
@@ -22,7 +25,10 @@ export function encryptRandomIVBuffer(input: Buffer, secretKeyBuffer: Buffer): B
22
25
  /**
23
26
  * Using aes-256-cbc.
24
27
  */
25
- export function decryptRandomIVBuffer(input: Buffer, secretKeyBuffer: Buffer): Buffer {
28
+ export function decryptRandomIVBuffer(
29
+ input: Uint8Array,
30
+ secretKeyBuffer: Uint8Array,
31
+ ): Buffer<ArrayBuffer> {
26
32
  // sha256 to match aes-256 key length
27
33
  const key = sha256AsBuffer(secretKeyBuffer)
28
34
 
@@ -39,7 +45,10 @@ export function decryptRandomIVBuffer(input: Buffer, secretKeyBuffer: Buffer): B
39
45
  * Decrypts all object values (base64 strings).
40
46
  * Returns object with decrypted values (utf8 strings).
41
47
  */
42
- export function decryptObject(obj: StringMap<Base64String>, secretKeyBuffer: Buffer): StringMap {
48
+ export function decryptObject(
49
+ obj: StringMap<Base64String>,
50
+ secretKeyBuffer: Uint8Array,
51
+ ): StringMap {
43
52
  const { key, iv } = getCryptoParams(secretKeyBuffer)
44
53
 
45
54
  const r: StringMap = {}
@@ -54,7 +63,10 @@ export function decryptObject(obj: StringMap<Base64String>, secretKeyBuffer: Buf
54
63
  * Encrypts all object values (utf8 strings).
55
64
  * Returns object with encrypted values (base64 strings).
56
65
  */
57
- export function encryptObject(obj: StringMap, secretKeyBuffer: Buffer): StringMap<Base64String> {
66
+ export function encryptObject(
67
+ obj: StringMap,
68
+ secretKeyBuffer: Uint8Array,
69
+ ): StringMap<Base64String> {
58
70
  const { key, iv } = getCryptoParams(secretKeyBuffer)
59
71
 
60
72
  const r: StringMap = {}
@@ -71,7 +83,7 @@ export function encryptObject(obj: StringMap, secretKeyBuffer: Buffer): StringMa
71
83
  * Input is base64 string.
72
84
  * Output is utf8 string.
73
85
  */
74
- export function decryptString(str: Base64String, secretKeyBuffer: Buffer): string {
86
+ export function decryptString(str: Base64String, secretKeyBuffer: Uint8Array): string {
75
87
  const { key, iv } = getCryptoParams(secretKeyBuffer)
76
88
  const decipher = crypto.createDecipheriv(algorithm, key, iv)
77
89
  return decipher.update(str, 'base64', 'utf8') + decipher.final('utf8')
@@ -83,13 +95,13 @@ export function decryptString(str: Base64String, secretKeyBuffer: Buffer): strin
83
95
  * Input is utf8 string.
84
96
  * Output is base64 string.
85
97
  */
86
- export function encryptString(str: string, secretKeyBuffer: Buffer): Base64String {
98
+ export function encryptString(str: string, secretKeyBuffer: Uint8Array): Base64String {
87
99
  const { key, iv } = getCryptoParams(secretKeyBuffer)
88
100
  const cipher = crypto.createCipheriv(algorithm, key, iv)
89
101
  return cipher.update(str, 'utf8', 'base64') + cipher.final('base64')
90
102
  }
91
103
 
92
- function getCryptoParams(secretKeyBuffer: Buffer): { key: Buffer; iv: Buffer } {
104
+ function getCryptoParams(secretKeyBuffer: Uint8Array): { key: Buffer; iv: Buffer } {
93
105
  const key = sha256AsBuffer(secretKeyBuffer)
94
106
  const iv = md5AsBuffer(Buffer.concat([secretKeyBuffer, key]))
95
107
  return { key, iv }
@@ -2,40 +2,45 @@ import type { BinaryToTextEncoding } from 'node:crypto'
2
2
  import { hash as cryptoHash } from 'node:crypto'
3
3
  import type { Base64String, Base64UrlString } from '@naturalcycles/js-lib/types'
4
4
 
5
- export function md5(s: string | Buffer, outputEncoding: BinaryToTextEncoding = 'hex'): string {
5
+ export function md5(s: string | Uint8Array, outputEncoding: BinaryToTextEncoding = 'hex'): string {
6
6
  return hash(s, 'md5', outputEncoding)
7
7
  }
8
8
 
9
- export function md5AsBuffer(s: string | Buffer): Buffer {
9
+ export function md5AsBuffer(s: string | Uint8Array): Buffer<ArrayBuffer> {
10
10
  return hashAsBuffer(s, 'md5')
11
11
  }
12
12
 
13
- export function sha256(s: string | Buffer, outputEncoding: BinaryToTextEncoding = 'hex'): string {
13
+ export function sha256(
14
+ s: string | Uint8Array,
15
+ outputEncoding: BinaryToTextEncoding = 'hex',
16
+ ): string {
14
17
  return hash(s, 'sha256', outputEncoding)
15
18
  }
16
19
 
17
- export function sha256AsBuffer(s: string | Buffer): Buffer {
20
+ export function sha256AsBuffer(s: string | Uint8Array): Buffer<ArrayBuffer> {
18
21
  return hashAsBuffer(s, 'sha256')
19
22
  }
20
23
 
21
24
  export function hash(
22
- s: string | Buffer,
25
+ s: string | Uint8Array,
23
26
  algorithm: string,
24
27
  outputEncoding: BinaryToTextEncoding = 'hex',
25
28
  ): string {
26
29
  return cryptoHash(algorithm, s, outputEncoding)
27
30
  }
28
31
 
29
- export function hashAsBuffer(s: string | Buffer, algorithm: string): Buffer {
32
+ export function hashAsBuffer(s: string | Uint8Array, algorithm: string): Buffer<ArrayBuffer> {
30
33
  return cryptoHash(algorithm, s, 'buffer')
31
34
  }
32
35
 
33
- export function base64(s: string | Buffer): Base64String {
34
- return (typeof s === 'string' ? Buffer.from(s) : s).toString('base64')
36
+ export function base64(s: string | Uint8Array): Base64String {
37
+ if (typeof s === 'string') return Buffer.from(s).toString('base64')
38
+ return bufferToBase64(s)
35
39
  }
36
40
 
37
- export function base64Url(s: string | Buffer): Base64UrlString {
38
- return (typeof s === 'string' ? Buffer.from(s) : s).toString('base64url')
41
+ export function base64Url(s: string | Uint8Array): Base64UrlString {
42
+ if (typeof s === 'string') return Buffer.from(s).toString('base64url')
43
+ return bufferToBase64Url(s)
39
44
  }
40
45
 
41
46
  export function base64ToString(strBase64: Base64String): string {
@@ -46,11 +51,11 @@ export function base64UrlToString(strBase64Url: Base64UrlString): string {
46
51
  return Buffer.from(strBase64Url, 'base64url').toString('utf8')
47
52
  }
48
53
 
49
- export function base64ToBuffer(strBase64: Base64String): Buffer {
54
+ export function base64ToBuffer(strBase64: Base64String): Buffer<ArrayBuffer> {
50
55
  return Buffer.from(strBase64, 'base64')
51
56
  }
52
57
 
53
- export function base64UrlToBuffer(strBase64Url: Base64UrlString): Buffer {
58
+ export function base64UrlToBuffer(strBase64Url: Base64UrlString): Buffer<ArrayBuffer> {
54
59
  return Buffer.from(strBase64Url, 'base64url')
55
60
  }
56
61
 
@@ -62,10 +67,14 @@ export function stringToBase64Url(s: string): Base64UrlString {
62
67
  return Buffer.from(s, 'utf8').toString('base64url')
63
68
  }
64
69
 
65
- export function bufferToBase64(b: Buffer): Base64String {
66
- return b.toString('base64')
70
+ // Buffer#toString is faster than Uint8Array#toBase64 on small inputs (~1.3-1.7x up to ~512 bytes,
71
+ // measured on Node 26), hence the Buffer fast-path. On larger inputs they're on par.
72
+ export function bufferToBase64(b: Uint8Array): Base64String {
73
+ return Buffer.isBuffer(b) ? b.toString('base64') : b.toBase64()
67
74
  }
68
75
 
69
- export function bufferToBase64Url(b: Buffer): Base64UrlString {
70
- return b.toString('base64url')
76
+ export function bufferToBase64Url(b: Uint8Array): Base64UrlString {
77
+ return Buffer.isBuffer(b)
78
+ ? b.toString('base64url')
79
+ : b.toBase64({ alphabet: 'base64url', omitPadding: true })
71
80
  }
@@ -24,7 +24,6 @@ export * from './transform/transformSplit.js'
24
24
  export * from './transform/transformTap.js'
25
25
  export * from './transform/transformThrottle.js'
26
26
  export * from './transform/transformThrottleByRSS.js'
27
- export * from './transform/transformWarmup.js'
28
27
  export * from './transform/worker/baseWorkerClass.js'
29
28
  export * from './transform/worker/transformMultiThreaded.js'
30
29
  export type * from './transform/worker/transformMultiThreaded.model.js'
@@ -14,6 +14,7 @@ import type {
14
14
  END,
15
15
  IndexedMapper,
16
16
  Integer,
17
+ NonEmptyArray,
17
18
  NonNegativeInteger,
18
19
  PositiveInteger,
19
20
  Predicate,
@@ -54,8 +55,6 @@ import { transformThrottle } from './transform/transformThrottle.js'
54
55
  import type { TransformThrottleOptions } from './transform/transformThrottle.js'
55
56
  import { transformThrottleByRSS } from './transform/transformThrottleByRSS.js'
56
57
  import type { TransformThrottleByRSSOptions } from './transform/transformThrottleByRSS.js'
57
- import { transformWarmup } from './transform/transformWarmup.js'
58
- import type { TransformWarmupOptions } from './transform/transformWarmup.js'
59
58
  import { writablePushToArray } from './writable/writablePushToArray.js'
60
59
  import { writableVoid } from './writable/writableVoid.js'
61
60
 
@@ -164,7 +163,7 @@ export class Pipeline<T = unknown> {
164
163
  return this
165
164
  }
166
165
 
167
- chunk(chunkSize: PositiveInteger, opt?: TransformOptions): Pipeline<T[]> {
166
+ chunk(chunkSize: PositiveInteger, opt?: TransformOptions): Pipeline<NonEmptyArray<T>> {
168
167
  this.transforms.push(transformChunk(chunkSize, opt))
169
168
  return this as any
170
169
  }
@@ -257,14 +256,6 @@ export class Pipeline<T = unknown> {
257
256
  return this
258
257
  }
259
258
 
260
- /**
261
- * @experimental to be removed after transformMap2 is stable
262
- */
263
- warmup(opt: TransformWarmupOptions): this {
264
- this.transforms.push(transformWarmup(opt))
265
- return this
266
- }
267
-
268
259
  transform<TO>(transform: TransformTyped<T, TO>): Pipeline<TO> {
269
260
  this.transforms.push(transform)
270
261
  return this as any
@@ -1,5 +1,5 @@
1
1
  import { Transform } from 'node:stream'
2
- import type { PositiveInteger } from '@naturalcycles/js-lib/types'
2
+ import type { NonEmptyArray, PositiveInteger } from '@naturalcycles/js-lib/types'
3
3
  import type { TransformOptions, TransformTyped } from '../stream.model.js'
4
4
 
5
5
  /**
@@ -13,7 +13,8 @@ import type { TransformOptions, TransformTyped } from '../stream.model.js'
13
13
  export function transformChunk<IN = any>(
14
14
  chunkSize: PositiveInteger,
15
15
  opt?: TransformOptions,
16
- ): TransformTyped<IN, IN[]> {
16
+ ): TransformTyped<IN, NonEmptyArray<IN>> {
17
+ // Only non-empty buffers are emitted
17
18
  let buf: IN[] = []
18
19
 
19
20
  return new Transform({
@@ -1,4 +1,5 @@
1
1
  import { _uniq } from '@naturalcycles/js-lib/array'
2
+ import { Intl2 } from '@naturalcycles/js-lib/intl'
2
3
  import type { IANATimezone } from '@naturalcycles/js-lib/types'
3
4
 
4
5
  // List extracted from Wikipedia (https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
@@ -603,8 +604,7 @@ const TIMEZONES_FROM_WIKI = [
603
604
  'Zulu',
604
605
  ] as IANATimezone[]
605
606
 
606
- // oxlint-disable-next-line no-restricted-globals
607
- const TIMEZONES_FROM_JS = Intl.supportedValuesOf('timeZone') as IANATimezone[]
607
+ const TIMEZONES_FROM_JS = Intl2.getSupportedTimezones()
608
608
 
609
609
  /**
610
610
  * A complicated merge of timezones from the underlying Javascript engine
package/src/zip/zip2.ts CHANGED
@@ -16,11 +16,11 @@ const zstdCompressAsync = promisify(zlib.zstdCompress.bind(zlib))
16
16
  const zstdDecompressAsync = promisify(zlib.zstdDecompress.bind(zlib))
17
17
 
18
18
  class Zip2 {
19
- async decompressZstdOrInflateToString(buf: Buffer): Promise<string> {
19
+ async decompressZstdOrInflateToString(buf: Uint8Array): Promise<string> {
20
20
  return (await this.decompressZstdOrInflate(buf)).toString()
21
21
  }
22
22
 
23
- decompressZstdOrInflateToStringSync(buf: Buffer): string {
23
+ decompressZstdOrInflateToStringSync(buf: Uint8Array): string {
24
24
  return this.decompressZstdOrInflateSync(buf).toString()
25
25
  }
26
26
 
@@ -28,14 +28,14 @@ class Zip2 {
28
28
  * Detects if Buffer is zstd-compressed.
29
29
  * Otherwise attempts to Inflate.
30
30
  */
31
- async decompressZstdOrInflate(buf: Buffer): Promise<Buffer<ArrayBuffer>> {
31
+ async decompressZstdOrInflate(buf: Uint8Array): Promise<Buffer<ArrayBuffer>> {
32
32
  if (this.isZstdBuffer(buf)) {
33
33
  return await zstdDecompressAsync(buf)
34
34
  }
35
35
  return await inflateAsync(buf)
36
36
  }
37
37
 
38
- decompressZstdOrInflateSync(buf: Buffer): Buffer<ArrayBuffer> {
38
+ decompressZstdOrInflateSync(buf: Uint8Array): Buffer<ArrayBuffer> {
39
39
  if (this.isZstdBuffer(buf)) {
40
40
  return zlib.zstdDecompressSync(buf)
41
41
  }
@@ -46,7 +46,10 @@ class Zip2 {
46
46
  * deflateBuffer uses `deflate`.
47
47
  * It's 9 bytes shorter than `gzip`.
48
48
  */
49
- async deflate(input: string | Buffer, options: ZlibOptions = {}): Promise<Buffer<ArrayBuffer>> {
49
+ async deflate(
50
+ input: string | Uint8Array,
51
+ options: ZlibOptions = {},
52
+ ): Promise<Buffer<ArrayBuffer>> {
50
53
  return await deflateAsync(input, options)
51
54
  }
52
55
 
@@ -54,23 +57,23 @@ class Zip2 {
54
57
  * deflateSync uses `deflate`.
55
58
  * It's 9 bytes shorter than `gzip`.
56
59
  */
57
- deflateSync(input: string | Buffer, options?: ZlibOptions): Buffer<ArrayBuffer> {
60
+ deflateSync(input: string | Uint8Array, options?: ZlibOptions): Buffer<ArrayBuffer> {
58
61
  return zlib.deflateSync(input, options)
59
62
  }
60
63
 
61
- async inflate(buf: Buffer, options: ZlibOptions = {}): Promise<Buffer<ArrayBuffer>> {
64
+ async inflate(buf: Uint8Array, options: ZlibOptions = {}): Promise<Buffer<ArrayBuffer>> {
62
65
  return await inflateAsync(buf, options)
63
66
  }
64
67
 
65
- inflateSync(buf: Buffer, options: ZlibOptions = {}): Buffer<ArrayBuffer> {
68
+ inflateSync(buf: Uint8Array, options: ZlibOptions = {}): Buffer<ArrayBuffer> {
66
69
  return zlib.inflateSync(buf, options)
67
70
  }
68
71
 
69
- async inflateToString(buf: Buffer, options?: ZlibOptions): Promise<string> {
72
+ async inflateToString(buf: Uint8Array, options?: ZlibOptions): Promise<string> {
70
73
  return (await this.inflate(buf, options)).toString()
71
74
  }
72
75
 
73
- inflateToStringSync(buf: Buffer, options?: ZlibOptions): string {
76
+ inflateToStringSync(buf: Uint8Array, options?: ZlibOptions): string {
74
77
  return zlib.inflateSync(buf, options).toString()
75
78
  }
76
79
 
@@ -78,7 +81,7 @@ class Zip2 {
78
81
  * gzipBuffer uses `gzip`
79
82
  * It's 9 bytes longer than `deflate`.
80
83
  */
81
- async gzip(input: string | Buffer, options: ZlibOptions = {}): Promise<Buffer<ArrayBuffer>> {
84
+ async gzip(input: string | Uint8Array, options: ZlibOptions = {}): Promise<Buffer<ArrayBuffer>> {
82
85
  return await gzipAsync(input, options)
83
86
  }
84
87
 
@@ -86,28 +89,28 @@ class Zip2 {
86
89
  * gzipBuffer uses `gzip`
87
90
  * It's 9 bytes longer than `deflate`.
88
91
  */
89
- gzipSync(input: string | Buffer, options: ZlibOptions = {}): Buffer<ArrayBuffer> {
92
+ gzipSync(input: string | Uint8Array, options: ZlibOptions = {}): Buffer<ArrayBuffer> {
90
93
  return zlib.gzipSync(input, options)
91
94
  }
92
95
 
93
- async gunzip(buf: Buffer, options: ZlibOptions = {}): Promise<Buffer<ArrayBuffer>> {
96
+ async gunzip(buf: Uint8Array, options: ZlibOptions = {}): Promise<Buffer<ArrayBuffer>> {
94
97
  return await gunzipAsync(buf, options)
95
98
  }
96
99
 
97
- gunzipSync(buf: Buffer, options: ZlibOptions = {}): Buffer<ArrayBuffer> {
100
+ gunzipSync(buf: Uint8Array, options: ZlibOptions = {}): Buffer<ArrayBuffer> {
98
101
  return zlib.gunzipSync(buf, options)
99
102
  }
100
103
 
101
- async gunzipToString(buf: Buffer, options?: ZlibOptions): Promise<string> {
104
+ async gunzipToString(buf: Uint8Array, options?: ZlibOptions): Promise<string> {
102
105
  return (await this.gunzip(buf, options)).toString()
103
106
  }
104
107
 
105
- gunzipToStringSync(buf: Buffer, options?: ZlibOptions): string {
108
+ gunzipToStringSync(buf: Uint8Array, options?: ZlibOptions): string {
106
109
  return zlib.gunzipSync(buf, options).toString()
107
110
  }
108
111
 
109
112
  async zstdCompress(
110
- input: Buffer | string,
113
+ input: Uint8Array | string,
111
114
  level?: Integer, // defaults to 3
112
115
  options: ZstdOptions = {},
113
116
  ): Promise<Buffer<ArrayBuffer>> {
@@ -115,7 +118,7 @@ class Zip2 {
115
118
  }
116
119
 
117
120
  zstdCompressSync(
118
- input: Buffer | string,
121
+ input: Uint8Array | string,
119
122
  level?: Integer, // defaults to 3
120
123
  options: ZstdOptions = {},
121
124
  ): Buffer<ArrayBuffer> {
@@ -134,33 +137,34 @@ class Zip2 {
134
137
  }
135
138
  }
136
139
 
137
- async zstdDecompressToString(input: Buffer, options: ZstdOptions = {}): Promise<string> {
140
+ async zstdDecompressToString(input: Uint8Array, options: ZstdOptions = {}): Promise<string> {
138
141
  return (await zstdDecompressAsync(input, options)).toString()
139
142
  }
140
143
 
141
144
  /**
142
145
  * Warning! It leaks memory severely. Prefer sync.
143
146
  */
144
- async zstdDecompress(input: Buffer, options: ZstdOptions = {}): Promise<Buffer<ArrayBuffer>> {
147
+ async zstdDecompress(input: Uint8Array, options: ZstdOptions = {}): Promise<Buffer<ArrayBuffer>> {
145
148
  return await zstdDecompressAsync(input, options)
146
149
  }
147
150
 
148
151
  /**
149
152
  * Warning! It leaks memory severely. Prefer sync.
150
153
  */
151
- zstdDecompressToStringSync(input: Buffer, options: ZstdOptions = {}): string {
154
+ zstdDecompressToStringSync(input: Uint8Array, options: ZstdOptions = {}): string {
152
155
  return zlib.zstdDecompressSync(input, options).toString()
153
156
  }
154
157
 
155
- zstdDecompressSync(input: Buffer, options: ZstdOptions = {}): Buffer<ArrayBuffer> {
158
+ zstdDecompressSync(input: Uint8Array, options: ZstdOptions = {}): Buffer<ArrayBuffer> {
156
159
  return zlib.zstdDecompressSync(input, options)
157
160
  }
158
161
 
159
- isZstdBuffer(input: Buffer): boolean {
160
- return input.readUInt32LE(0) === ZSTD_MAGIC_NUMBER
162
+ isZstdBuffer(input: Uint8Array): boolean {
163
+ // zstd magic number 0xFD2FB528, stored little-endian
164
+ return input[0] === 0x28 && input[1] === 0xb5 && input[2] === 0x2f && input[3] === 0xfd
161
165
  }
162
166
 
163
- isGzipBuffer(input: Buffer): boolean {
167
+ isGzipBuffer(input: Uint8Array): boolean {
164
168
  return input[0] === 0x1f && input[1] === 0x8b
165
169
  }
166
170
 
@@ -211,11 +215,9 @@ class Zip2 {
211
215
  *
212
216
  * For large archives or streamed inputs prefer {@link createZip} / {@link ZipWriter}.
213
217
  */
214
- async createZipBuffer(entries: ZipFileEntry[]): Promise<Buffer> {
218
+ async createZipBuffer(entries: ZipFileEntry[]): Promise<Buffer<ArrayBuffer>> {
215
219
  return await createZipBuffer(entries)
216
220
  }
217
221
  }
218
222
 
219
223
  export const zip2 = new Zip2()
220
-
221
- const ZSTD_MAGIC_NUMBER = 0xfd2fb528
@@ -62,7 +62,7 @@ const deflateRawAsync = promisify(zlib.deflateRaw.bind(zlib))
62
62
  *
63
63
  * Each input path may be a file (added directly) or a directory (walked
64
64
  * recursively; all nested files are added). By default an entry's name is its
65
- * path relative to its input's parent directory, so `zipFiles(['./photos'], 'out.zip')`
65
+ * path relative to its input's parent directory, so `zipPaths(['./photos'], 'out.zip')`
66
66
  * stores entries under `photos/...`. Override the base via {@link ZipPathsOptions.baseDir}.
67
67
  *
68
68
  * Files discovered by walking a directory are added in deterministic (sorted)
@@ -105,7 +105,7 @@ export async function zipPaths(
105
105
  *
106
106
  * For large archives or streamed inputs prefer {@link createZip} / {@link ZipWriter}.
107
107
  */
108
- export async function createZipBuffer(entries: ZipFileEntry[]): Promise<Buffer> {
108
+ export async function createZipBuffer(entries: ZipFileEntry[]): Promise<Buffer<ArrayBuffer>> {
109
109
  const chunks: Buffer[] = []
110
110
  const sink = new Writable({
111
111
  write(chunk: Buffer, _enc, cb) {
@@ -176,7 +176,11 @@ export class ZipWriter implements AsyncDisposable {
176
176
  * Add an in-memory buffer as a file entry.
177
177
  * The CRC-32 and sizes are computed up front and written inline (no data descriptor).
178
178
  */
179
- async addBuffer(data: Buffer, fileName: string, opt: ZipWriterEntryOptions = {}): Promise<void> {
179
+ async addBuffer(
180
+ data: Uint8Array,
181
+ fileName: string,
182
+ opt: ZipWriterEntryOptions = {},
183
+ ): Promise<void> {
180
184
  this.assertWritable()
181
185
  const entry = this.createEntry(fileName, false, opt)
182
186
  entry.crc32 = zlib.crc32(data)
@@ -325,7 +329,7 @@ export class ZipWriter implements AsyncDisposable {
325
329
  }
326
330
 
327
331
  /** Write an entry whose CRC and sizes are already known: header, name, data, no descriptor. */
328
- private async writeKnownEntry(entry: WriteEntry, data: Buffer): Promise<void> {
332
+ private async writeKnownEntry(entry: WriteEntry, data: Uint8Array): Promise<void> {
329
333
  entry.relativeOffsetOfLocalHeader = this.offset
330
334
  await this.write(buildLocalFileHeader(entry))
331
335
  await this.write(entry.nameBuf)
@@ -389,7 +393,7 @@ export class ZipWriter implements AsyncDisposable {
389
393
  }
390
394
 
391
395
  /** Write a buffer to the output, tracking the byte offset and respecting backpressure. */
392
- private async write(buf: Buffer): Promise<void> {
396
+ private async write(buf: Uint8Array): Promise<void> {
393
397
  if (this.streamError) throw this.streamError
394
398
  if (buf.length === 0) return
395
399
  this.offset += buf.length
@@ -1,4 +0,0 @@
1
- /**
2
- * Same as _chunk, but for Buffer.
3
- */
4
- export declare function _chunkBuffer(buf: Buffer, size: number): Buffer[];
@@ -1,10 +0,0 @@
1
- /**
2
- * Same as _chunk, but for Buffer.
3
- */
4
- export function _chunkBuffer(buf, size) {
5
- const out = [];
6
- for (let i = 0; i < buf.length; i += size) {
7
- out.push(buf.subarray(i, i + size));
8
- }
9
- return out;
10
- }
@@ -1,24 +0,0 @@
1
- import type { NumberOfSeconds, PositiveInteger } from '@naturalcycles/js-lib/types';
2
- import type { TransformOptions, TransformTyped } from '../stream.model.js';
3
- export interface TransformWarmupOptions extends TransformOptions {
4
- /**
5
- * Target concurrency after warmup completes.
6
- */
7
- concurrency: PositiveInteger;
8
- /**
9
- * Time in seconds to gradually increase concurrency from 1 to `concurrency`.
10
- * Set to 0 to disable warmup (pass-through mode from the start).
11
- */
12
- warmupSeconds: NumberOfSeconds;
13
- }
14
- /**
15
- * Transform that gradually increases concurrency from 1 to the configured maximum
16
- * over a warmup period. Useful for scenarios where you want to avoid overwhelming
17
- * a system at startup (e.g., database connections, API rate limits).
18
- *
19
- * During warmup: limits concurrent items based on elapsed time.
20
- * After warmup: passes items through immediately with zero overhead.
21
- *
22
- * @experimental
23
- */
24
- export declare function transformWarmup<T>(opt: TransformWarmupOptions): TransformTyped<T, T>;
@@ -1,79 +0,0 @@
1
- import { Transform } from 'node:stream';
2
- import { createCommonLoggerAtLevel } from '@naturalcycles/js-lib/log';
3
- import { pDefer } from '@naturalcycles/js-lib/promise/pDefer.js';
4
- /**
5
- * Transform that gradually increases concurrency from 1 to the configured maximum
6
- * over a warmup period. Useful for scenarios where you want to avoid overwhelming
7
- * a system at startup (e.g., database connections, API rate limits).
8
- *
9
- * During warmup: limits concurrent items based on elapsed time.
10
- * After warmup: passes items through immediately with zero overhead.
11
- *
12
- * @experimental
13
- */
14
- export function transformWarmup(opt) {
15
- const { concurrency, warmupSeconds, objectMode = true, highWaterMark = 1 } = opt;
16
- const warmupMs = warmupSeconds * 1000;
17
- const logger = createCommonLoggerAtLevel(opt.logger, opt.logLevel);
18
- let startTime = 0;
19
- let warmupComplete = warmupSeconds <= 0 || concurrency <= 1;
20
- let inFlight = 0;
21
- const waiters = [];
22
- return new Transform({
23
- objectMode,
24
- highWaterMark,
25
- async transform(item, _, cb) {
26
- // Initialize start time on first item
27
- if (startTime === 0) {
28
- startTime = Date.now();
29
- }
30
- // Fast-path: after warmup, just pass through with zero overhead
31
- if (warmupComplete) {
32
- cb(null, item);
33
- return;
34
- }
35
- const currentConcurrency = getCurrentConcurrency();
36
- if (inFlight < currentConcurrency) {
37
- // Have room, proceed immediately
38
- inFlight++;
39
- logger.debug(`inFlight++ ${inFlight}/${currentConcurrency}, waiters ${waiters.length}`);
40
- }
41
- else {
42
- // Wait for a slot
43
- const waiter = pDefer();
44
- waiters.push(waiter);
45
- logger.debug(`inFlight ${inFlight}/${currentConcurrency}, waiters++ ${waiters.length}`);
46
- await waiter;
47
- logger.debug(`waiter resolved, inFlight ${inFlight}/${getCurrentConcurrency()}`);
48
- }
49
- // Push the item
50
- cb(null, item);
51
- // Release slot on next microtask - essential for concurrency control.
52
- // Without this, the slot would be freed immediately and items would
53
- // flow through without any limiting effect.
54
- queueMicrotask(release);
55
- },
56
- });
57
- function getCurrentConcurrency() {
58
- if (warmupComplete)
59
- return concurrency;
60
- const elapsed = Date.now() - startTime;
61
- if (elapsed >= warmupMs) {
62
- warmupComplete = true;
63
- logger.debug('warmup complete');
64
- return concurrency;
65
- }
66
- // Linear interpolation from 1 to concurrency
67
- const progress = elapsed / warmupMs;
68
- return Math.max(1, Math.floor(1 + (concurrency - 1) * progress));
69
- }
70
- function release() {
71
- inFlight--;
72
- // Wake up waiters based on current concurrency (may have increased)
73
- const currentConcurrency = getCurrentConcurrency();
74
- while (waiters.length && inFlight < currentConcurrency) {
75
- inFlight++;
76
- waiters.shift().resolve();
77
- }
78
- }
79
- }
@@ -1,12 +0,0 @@
1
- /**
2
- * Same as _chunk, but for Buffer.
3
- */
4
- export function _chunkBuffer(buf: Buffer, size: number): Buffer[] {
5
- const out: Buffer[] = []
6
-
7
- for (let i = 0; i < buf.length; i += size) {
8
- out.push(buf.subarray(i, i + size))
9
- }
10
-
11
- return out
12
- }