@naturalcycles/js-lib 15.94.1 → 15.96.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,4 +1,4 @@
1
- import type { AbortablePredicate, FalsyValue, Mapper, MutateOptions, Predicate, StringMap } from '../types.js';
1
+ import type { AbortablePredicate, FalsyValue, Mapper, MutateOptions, NonEmptyArray, Predicate, StringMap } from '../types.js';
2
2
  /**
3
3
  * Creates an array of elements split into groups the length of size. If collection can’t be split evenly, the
4
4
  * final chunk will be the remaining elements.
@@ -11,7 +11,7 @@ import type { AbortablePredicate, FalsyValue, Mapper, MutateOptions, Predicate,
11
11
  *
12
12
  * Based on: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_chunk
13
13
  */
14
- export declare function _chunk<T>(array: readonly T[], size?: number): T[][];
14
+ export declare function _chunk<T>(array: readonly T[], size?: number): NonEmptyArray<T>[];
15
15
  /**
16
16
  * Removes duplicates from given array.
17
17
  */
@@ -89,7 +89,7 @@ export declare function _mapBy<ITEM, KEY>(items: readonly ITEM[], mapper: Mapper
89
89
  *
90
90
  * Returning `undefined` from the Mapper will EXCLUDE the item.
91
91
  */
92
- export declare function _groupBy<T>(items: readonly T[], mapper: Mapper<T, any>): StringMap<T[]>;
92
+ export declare function _groupBy<T>(items: readonly T[], mapper: Mapper<T, any>): StringMap<NonEmptyArray<T>>;
93
93
  /**
94
94
  * Similar to `Array.find`, but the `predicate` may return `END` to stop the iteration early.
95
95
  *
@@ -217,6 +217,12 @@ export declare function _first<T>(array: readonly T[]): T;
217
217
  * Returns first item of the array (or undefined if array is empty).
218
218
  */
219
219
  export declare function _firstOrUndefined<T>(array: readonly T[]): T | undefined;
220
+ /**
221
+ * Type guard that narrows an array to NonEmptyArray (array with at least one element).
222
+ * Readonly arrays are narrowed to `Readonly<NonEmptyArray<T>>`.
223
+ */
224
+ export declare function _isNonEmptyArray<T>(array: T[]): array is NonEmptyArray<T>;
225
+ export declare function _isNonEmptyArray<T>(array: readonly T[]): array is Readonly<NonEmptyArray<T>>;
220
226
  /**
221
227
  * Returns the first item of a non-empty iterable (Set, Map.values(), generator, etc.).
222
228
  * Throws if the iterable is empty.
@@ -144,9 +144,14 @@ export function _groupBy(items, mapper) {
144
144
  const map = {};
145
145
  for (const item of items) {
146
146
  const key = mapper(item);
147
- if (key !== undefined) {
148
- ;
149
- (map[key] ||= []).push(item);
147
+ if (key === undefined)
148
+ continue;
149
+ const group = map[key];
150
+ if (group) {
151
+ group.push(item);
152
+ }
153
+ else {
154
+ map[key] = [item];
150
155
  }
151
156
  }
152
157
  return map;
@@ -403,6 +408,9 @@ export function _first(array) {
403
408
  export function _firstOrUndefined(array) {
404
409
  return array[0];
405
410
  }
411
+ export function _isNonEmptyArray(array) {
412
+ return array.length > 0;
413
+ }
406
414
  /**
407
415
  * Returns the first item of a non-empty iterable (Set, Map.values(), generator, etc.).
408
416
  * Throws if the iterable is empty.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Returns an ArrayBuffer that contains exactly the bytes of the given view (e.g Uint8Array or Buffer).
3
+ *
4
+ * Safe alternative to `view.buffer`, which returns the whole underlying ArrayBuffer.
5
+ * It can be larger than the view, and the view may start at a non-zero offset:
6
+ * e.g Node.js allocates small Buffers from a shared pool (`Buffer.from(string)`,
7
+ * `fs.readFileSync` of small files), or the view was created with `subarray()`.
8
+ *
9
+ * Zero-copy when the view covers the whole ArrayBuffer, otherwise copies exactly its bytes.
10
+ */
11
+ export declare function _toArrayBuffer<T extends ArrayBufferLike>(view: ArrayBufferView<T>): T;
12
+ /**
13
+ * Same as _chunk, but for Uint8Array (or Buffer).
14
+ * Chunks are zero-copy views (subarray) into the input, and preserve its type
15
+ * (e.g Buffer in - Buffer[] out).
16
+ */
17
+ export declare function _chunkBuffer<T extends Uint8Array>(buf: T, size: number): T[];
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Returns an ArrayBuffer that contains exactly the bytes of the given view (e.g Uint8Array or Buffer).
3
+ *
4
+ * Safe alternative to `view.buffer`, which returns the whole underlying ArrayBuffer.
5
+ * It can be larger than the view, and the view may start at a non-zero offset:
6
+ * e.g Node.js allocates small Buffers from a shared pool (`Buffer.from(string)`,
7
+ * `fs.readFileSync` of small files), or the view was created with `subarray()`.
8
+ *
9
+ * Zero-copy when the view covers the whole ArrayBuffer, otherwise copies exactly its bytes.
10
+ */
11
+ export function _toArrayBuffer(view) {
12
+ const { buffer, byteOffset, byteLength } = view;
13
+ if (byteOffset === 0 && byteLength === buffer.byteLength)
14
+ return buffer;
15
+ return buffer.slice(byteOffset, byteOffset + byteLength);
16
+ }
17
+ /**
18
+ * Same as _chunk, but for Uint8Array (or Buffer).
19
+ * Chunks are zero-copy views (subarray) into the input, and preserve its type
20
+ * (e.g Buffer in - Buffer[] out).
21
+ */
22
+ export function _chunkBuffer(buf, size) {
23
+ const out = [];
24
+ for (let i = 0; i < buf.length; i += size) {
25
+ // subarray preserves the subclass at runtime (Buffer#subarray returns a Buffer)
26
+ out.push(buf.subarray(i, i + size));
27
+ }
28
+ return out;
29
+ }
@@ -0,0 +1 @@
1
+ export * from './buffer.util.js';
@@ -0,0 +1 @@
1
+ export * from './buffer.util.js';
package/dist/types.d.ts CHANGED
@@ -181,6 +181,17 @@ export interface BatchResult<RES = any, ERR = Error> {
181
181
  * type Foo = ValuesOf<typeof arr> // 'a' | 'b'
182
182
  */
183
183
  export type ValuesOf<T extends readonly any[]> = T[number];
184
+ /**
185
+ * Array with at least one element.
186
+ * Index 0 is typed as `T` (not `T | undefined`) under `noUncheckedIndexedAccess`.
187
+ *
188
+ * Use `_isNonEmptyArray` to narrow a regular array to it.
189
+ *
190
+ * Caveats:
191
+ * - Mutating methods (pop, shift, splice, `length = 0`) can empty it without the type noticing.
192
+ * - Non-emptiness is lost after map, filter, slice, concat (they return `T[]`). Spread preserves it.
193
+ */
194
+ export type NonEmptyArray<T> = [T, ...T[]];
184
195
  /**
185
196
  * Based on: https://stackoverflow.com/a/49286056/4919972
186
197
  *
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
3
  "type": "module",
4
- "version": "15.94.1",
4
+ "version": "15.96.0",
5
5
  "dependencies": {
6
6
  "tslib": "^2"
7
7
  },
@@ -28,6 +28,8 @@
28
28
  "./array/*.js": "./dist/array/*.js",
29
29
  "./browser": "./dist/browser/index.js",
30
30
  "./browser/*.js": "./dist/browser/*.js",
31
+ "./buffer": "./dist/buffer/index.js",
32
+ "./buffer/*.js": "./dist/buffer/*.js",
31
33
  "./datetime": "./dist/datetime/index.js",
32
34
  "./datetime/*.js": "./dist/datetime/*.js",
33
35
  "./decorators": "./dist/decorators/index.js",
@@ -4,6 +4,7 @@ import type {
4
4
  FalsyValue,
5
5
  Mapper,
6
6
  MutateOptions,
7
+ NonEmptyArray,
7
8
  Predicate,
8
9
  StringMap,
9
10
  } from '../types.js'
@@ -21,12 +22,12 @@ import { END } from '../types.js'
21
22
  *
22
23
  * Based on: https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_chunk
23
24
  */
24
- export function _chunk<T>(array: readonly T[], size = 1): T[][] {
25
+ export function _chunk<T>(array: readonly T[], size = 1): NonEmptyArray<T>[] {
25
26
  const a: T[][] = []
26
27
  for (let i = 0; i < array.length; i += size) {
27
28
  a.push(array.slice(i, i + size))
28
29
  }
29
- return a
30
+ return a as NonEmptyArray<T>[]
30
31
  }
31
32
 
32
33
  /**
@@ -158,13 +159,20 @@ export function _mapBy<ITEM, KEY>(
158
159
  *
159
160
  * Returning `undefined` from the Mapper will EXCLUDE the item.
160
161
  */
161
- export function _groupBy<T>(items: readonly T[], mapper: Mapper<T, any>): StringMap<T[]> {
162
- const map: StringMap<T[]> = {}
162
+ export function _groupBy<T>(
163
+ items: readonly T[],
164
+ mapper: Mapper<T, any>,
165
+ ): StringMap<NonEmptyArray<T>> {
166
+ const map: StringMap<NonEmptyArray<T>> = {}
163
167
 
164
168
  for (const item of items) {
165
169
  const key = mapper(item)
166
- if (key !== undefined) {
167
- ;(map[key] ||= []).push(item)
170
+ if (key === undefined) continue
171
+ const group = map[key]
172
+ if (group) {
173
+ group.push(item)
174
+ } else {
175
+ map[key] = [item]
168
176
  }
169
177
  }
170
178
  return map
@@ -468,6 +476,16 @@ export function _firstOrUndefined<T>(array: readonly T[]): T | undefined {
468
476
  return array[0]
469
477
  }
470
478
 
479
+ /**
480
+ * Type guard that narrows an array to NonEmptyArray (array with at least one element).
481
+ * Readonly arrays are narrowed to `Readonly<NonEmptyArray<T>>`.
482
+ */
483
+ export function _isNonEmptyArray<T>(array: T[]): array is NonEmptyArray<T>
484
+ export function _isNonEmptyArray<T>(array: readonly T[]): array is Readonly<NonEmptyArray<T>>
485
+ export function _isNonEmptyArray(array: readonly unknown[]): boolean {
486
+ return array.length > 0
487
+ }
488
+
471
489
  /**
472
490
  * Returns the first item of a non-empty iterable (Set, Map.values(), generator, etc.).
473
491
  * Throws if the iterable is empty.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Returns an ArrayBuffer that contains exactly the bytes of the given view (e.g Uint8Array or Buffer).
3
+ *
4
+ * Safe alternative to `view.buffer`, which returns the whole underlying ArrayBuffer.
5
+ * It can be larger than the view, and the view may start at a non-zero offset:
6
+ * e.g Node.js allocates small Buffers from a shared pool (`Buffer.from(string)`,
7
+ * `fs.readFileSync` of small files), or the view was created with `subarray()`.
8
+ *
9
+ * Zero-copy when the view covers the whole ArrayBuffer, otherwise copies exactly its bytes.
10
+ */
11
+ export function _toArrayBuffer<T extends ArrayBufferLike>(view: ArrayBufferView<T>): T {
12
+ const { buffer, byteOffset, byteLength } = view
13
+ if (byteOffset === 0 && byteLength === buffer.byteLength) return buffer
14
+ return buffer.slice(byteOffset, byteOffset + byteLength) as T
15
+ }
16
+
17
+ /**
18
+ * Same as _chunk, but for Uint8Array (or Buffer).
19
+ * Chunks are zero-copy views (subarray) into the input, and preserve its type
20
+ * (e.g Buffer in - Buffer[] out).
21
+ */
22
+ export function _chunkBuffer<T extends Uint8Array>(buf: T, size: number): T[] {
23
+ const out: T[] = []
24
+
25
+ for (let i = 0; i < buf.length; i += size) {
26
+ // subarray preserves the subclass at runtime (Buffer#subarray returns a Buffer)
27
+ out.push(buf.subarray(i, i + size) as T)
28
+ }
29
+
30
+ return out
31
+ }
@@ -0,0 +1 @@
1
+ export * from './buffer.util.js'
package/src/types.ts CHANGED
@@ -225,6 +225,18 @@ export interface BatchResult<RES = any, ERR = Error> {
225
225
  */
226
226
  export type ValuesOf<T extends readonly any[]> = T[number]
227
227
 
228
+ /**
229
+ * Array with at least one element.
230
+ * Index 0 is typed as `T` (not `T | undefined`) under `noUncheckedIndexedAccess`.
231
+ *
232
+ * Use `_isNonEmptyArray` to narrow a regular array to it.
233
+ *
234
+ * Caveats:
235
+ * - Mutating methods (pop, shift, splice, `length = 0`) can empty it without the type noticing.
236
+ * - Non-emptiness is lost after map, filter, slice, concat (they return `T[]`). Spread preserves it.
237
+ */
238
+ export type NonEmptyArray<T> = [T, ...T[]]
239
+
228
240
  /**
229
241
  * Based on: https://stackoverflow.com/a/49286056/4919972
230
242
  *