@naturalcycles/js-lib 15.75.0 → 15.76.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.
@@ -217,6 +217,15 @@ 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
+ * Returns the first item of an iterable (Set, Map.values(), generator, etc.),
222
+ * or `undefined` if the iterable is empty.
223
+ *
224
+ * Avoids the `Array.from(iter)[0]` pattern that materialises the entire iterable.
225
+ * `for...of` with an early return advances the iterator exactly once; if the
226
+ * iterator implements `return()` (generators, etc.), it is invoked for cleanup.
227
+ */
228
+ export declare function _firstFromIterable<T>(iter: Iterable<T>): T | undefined;
220
229
  export declare function _minOrUndefined<T>(array: readonly T[]): NonNullable<T> | undefined;
221
230
  /**
222
231
  * Filters out nullish values (undefined and null).
@@ -403,6 +403,19 @@ export function _first(array) {
403
403
  export function _firstOrUndefined(array) {
404
404
  return array[0];
405
405
  }
406
+ /**
407
+ * Returns the first item of an iterable (Set, Map.values(), generator, etc.),
408
+ * or `undefined` if the iterable is empty.
409
+ *
410
+ * Avoids the `Array.from(iter)[0]` pattern that materialises the entire iterable.
411
+ * `for...of` with an early return advances the iterator exactly once; if the
412
+ * iterator implements `return()` (generators, etc.), it is invoked for cleanup.
413
+ */
414
+ export function _firstFromIterable(iter) {
415
+ for (const item of iter)
416
+ return item;
417
+ return undefined;
418
+ }
406
419
  export function _minOrUndefined(array) {
407
420
  let min;
408
421
  for (const item of array) {
package/dist/is.util.js CHANGED
@@ -33,10 +33,20 @@ export function _isPrimitive(v) {
33
33
  typeof v === 'symbol');
34
34
  }
35
35
  export function _isEmptyObject(obj) {
36
- return Object.keys(obj).length === 0;
36
+ // for...in with early return avoids allocating the full Object.keys array.
37
+ // Object.hasOwn matches Object.keys() semantics (own enumerable keys only).
38
+ for (const k in obj) {
39
+ if (Object.hasOwn(obj, k))
40
+ return false;
41
+ }
42
+ return true;
37
43
  }
38
44
  export function _isNotEmptyObject(obj) {
39
- return Object.keys(obj).length > 0;
45
+ for (const k in obj) {
46
+ if (Object.hasOwn(obj, k))
47
+ return true;
48
+ }
49
+ return false;
40
50
  }
41
51
  /**
42
52
  * Object is considered empty if it's one of:
@@ -58,7 +68,11 @@ export function _isEmpty(obj) {
58
68
  return obj.size === 0;
59
69
  }
60
70
  if (typeof obj === 'object') {
61
- return Object.keys(obj).length === 0;
71
+ for (const k in obj) {
72
+ if (Object.hasOwn(obj, k))
73
+ return false;
74
+ }
75
+ return true;
62
76
  }
63
77
  return false;
64
78
  }
@@ -87,6 +87,29 @@ export declare function _mapKeys<T extends AnyObject>(obj: T, mapper: ObjectMapp
87
87
  */
88
88
  export declare function _mapObject<OUT = unknown, IN extends AnyObject = AnyObject>(obj: IN, mapper: ObjectMapper<IN, KeyValueTuple<string, any> | typeof SKIP>): OUT;
89
89
  export declare function _findKeyByValue<T extends AnyObject>(obj: T, v: ValueOf<T>): keyof T | undefined;
90
+ /**
91
+ * Returns the first key of the object, or `undefined` if the object is empty.
92
+ *
93
+ * Performance-optimised: uses `for...in` with an early return to avoid
94
+ * allocating the full `Object.keys(obj)` array. The `Object.hasOwn` filter
95
+ * matches `Object.keys()` semantics (own enumerable string keys only) and
96
+ * satisfies the `guard-for-in` lint rule; cost is a single check before
97
+ * the early return. Iteration order matches `Object.keys()` for plain
98
+ * objects (integer-like keys ascending first, then string keys in
99
+ * insertion order).
100
+ */
101
+ export declare function _firstKey<T extends AnyObject>(obj: T): keyof T | undefined;
102
+ /**
103
+ * Returns the first value of the object, or `undefined` if the object is empty.
104
+ * See `_firstKey` for the iteration-order contract.
105
+ */
106
+ export declare function _firstValue<T extends AnyObject>(obj: T): ValueOf<T> | undefined;
107
+ /**
108
+ * Returns the first [key, value] tuple of the object,
109
+ * or `undefined` if the object is empty.
110
+ * See `_firstKey` for the iteration-order contract.
111
+ */
112
+ export declare function _firstEntry<T extends AnyObject>(obj: T): [keyof T, ValueOf<T>] | undefined;
90
113
  export declare function _objectNullValuesToUndefined<T extends AnyObject>(obj: T, opt?: MutateOptions): T;
91
114
  /**
92
115
  * Deep copy object (by json parse/stringify, since it has unbeatable performance+simplicity combo).
@@ -191,6 +191,47 @@ export function _mapObject(obj, mapper) {
191
191
  export function _findKeyByValue(obj, v) {
192
192
  return Object.entries(obj).find(([_, value]) => value === v)?.[0];
193
193
  }
194
+ /**
195
+ * Returns the first key of the object, or `undefined` if the object is empty.
196
+ *
197
+ * Performance-optimised: uses `for...in` with an early return to avoid
198
+ * allocating the full `Object.keys(obj)` array. The `Object.hasOwn` filter
199
+ * matches `Object.keys()` semantics (own enumerable string keys only) and
200
+ * satisfies the `guard-for-in` lint rule; cost is a single check before
201
+ * the early return. Iteration order matches `Object.keys()` for plain
202
+ * objects (integer-like keys ascending first, then string keys in
203
+ * insertion order).
204
+ */
205
+ export function _firstKey(obj) {
206
+ for (const k in obj) {
207
+ if (Object.hasOwn(obj, k))
208
+ return k;
209
+ }
210
+ return undefined;
211
+ }
212
+ /**
213
+ * Returns the first value of the object, or `undefined` if the object is empty.
214
+ * See `_firstKey` for the iteration-order contract.
215
+ */
216
+ export function _firstValue(obj) {
217
+ for (const k in obj) {
218
+ if (Object.hasOwn(obj, k))
219
+ return obj[k];
220
+ }
221
+ return undefined;
222
+ }
223
+ /**
224
+ * Returns the first [key, value] tuple of the object,
225
+ * or `undefined` if the object is empty.
226
+ * See `_firstKey` for the iteration-order contract.
227
+ */
228
+ export function _firstEntry(obj) {
229
+ for (const k in obj) {
230
+ if (Object.hasOwn(obj, k))
231
+ return [k, obj[k]];
232
+ }
233
+ return undefined;
234
+ }
194
235
  export function _objectNullValuesToUndefined(obj, opt = {}) {
195
236
  return _mapValues(obj, (_k, v) => (v === null ? undefined : v), opt);
196
237
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
3
  "type": "module",
4
- "version": "15.75.0",
4
+ "version": "15.76.0",
5
5
  "dependencies": {
6
6
  "tslib": "^2"
7
7
  },
@@ -20,7 +20,7 @@
20
20
  "@typescript/native-preview": "beta",
21
21
  "crypto-js": "^4",
22
22
  "dayjs": "^1",
23
- "@naturalcycles/dev-lib": "20.48.0"
23
+ "@naturalcycles/dev-lib": "18.4.2"
24
24
  },
25
25
  "exports": {
26
26
  ".": "./dist/index.js",
@@ -468,6 +468,19 @@ export function _firstOrUndefined<T>(array: readonly T[]): T | undefined {
468
468
  return array[0]
469
469
  }
470
470
 
471
+ /**
472
+ * Returns the first item of an iterable (Set, Map.values(), generator, etc.),
473
+ * or `undefined` if the iterable is empty.
474
+ *
475
+ * Avoids the `Array.from(iter)[0]` pattern that materialises the entire iterable.
476
+ * `for...of` with an early return advances the iterator exactly once; if the
477
+ * iterator implements `return()` (generators, etc.), it is invoked for cleanup.
478
+ */
479
+ export function _firstFromIterable<T>(iter: Iterable<T>): T | undefined {
480
+ for (const item of iter) return item
481
+ return undefined
482
+ }
483
+
471
484
  export function _minOrUndefined<T>(array: readonly T[]): NonNullable<T> | undefined {
472
485
  let min: NonNullable<T> | undefined
473
486
  for (const item of array) {
package/src/is.util.ts CHANGED
@@ -45,11 +45,19 @@ export function _isPrimitive(v: any): v is Primitive {
45
45
  }
46
46
 
47
47
  export function _isEmptyObject(obj: AnyObject): boolean {
48
- return Object.keys(obj).length === 0
48
+ // for...in with early return avoids allocating the full Object.keys array.
49
+ // Object.hasOwn matches Object.keys() semantics (own enumerable keys only).
50
+ for (const k in obj) {
51
+ if (Object.hasOwn(obj, k)) return false
52
+ }
53
+ return true
49
54
  }
50
55
 
51
56
  export function _isNotEmptyObject(obj: AnyObject): boolean {
52
- return Object.keys(obj).length > 0
57
+ for (const k in obj) {
58
+ if (Object.hasOwn(obj, k)) return true
59
+ }
60
+ return false
53
61
  }
54
62
 
55
63
  /**
@@ -74,7 +82,10 @@ export function _isEmpty(obj: any): boolean {
74
82
  }
75
83
 
76
84
  if (typeof obj === 'object') {
77
- return Object.keys(obj).length === 0
85
+ for (const k in obj) {
86
+ if (Object.hasOwn(obj, k)) return false
87
+ }
88
+ return true
78
89
  }
79
90
 
80
91
  return false
@@ -242,6 +242,47 @@ export function _findKeyByValue<T extends AnyObject>(obj: T, v: ValueOf<T>): key
242
242
  return Object.entries(obj).find(([_, value]) => value === v)?.[0] as keyof T
243
243
  }
244
244
 
245
+ /**
246
+ * Returns the first key of the object, or `undefined` if the object is empty.
247
+ *
248
+ * Performance-optimised: uses `for...in` with an early return to avoid
249
+ * allocating the full `Object.keys(obj)` array. The `Object.hasOwn` filter
250
+ * matches `Object.keys()` semantics (own enumerable string keys only) and
251
+ * satisfies the `guard-for-in` lint rule; cost is a single check before
252
+ * the early return. Iteration order matches `Object.keys()` for plain
253
+ * objects (integer-like keys ascending first, then string keys in
254
+ * insertion order).
255
+ */
256
+ export function _firstKey<T extends AnyObject>(obj: T): keyof T | undefined {
257
+ for (const k in obj) {
258
+ if (Object.hasOwn(obj, k)) return k as keyof T
259
+ }
260
+ return undefined
261
+ }
262
+
263
+ /**
264
+ * Returns the first value of the object, or `undefined` if the object is empty.
265
+ * See `_firstKey` for the iteration-order contract.
266
+ */
267
+ export function _firstValue<T extends AnyObject>(obj: T): ValueOf<T> | undefined {
268
+ for (const k in obj) {
269
+ if (Object.hasOwn(obj, k)) return obj[k] as ValueOf<T>
270
+ }
271
+ return undefined
272
+ }
273
+
274
+ /**
275
+ * Returns the first [key, value] tuple of the object,
276
+ * or `undefined` if the object is empty.
277
+ * See `_firstKey` for the iteration-order contract.
278
+ */
279
+ export function _firstEntry<T extends AnyObject>(obj: T): [keyof T, ValueOf<T>] | undefined {
280
+ for (const k in obj) {
281
+ if (Object.hasOwn(obj, k)) return [k as keyof T, obj[k] as ValueOf<T>]
282
+ }
283
+ return undefined
284
+ }
285
+
245
286
  export function _objectNullValuesToUndefined<T extends AnyObject>(
246
287
  obj: T,
247
288
  opt: MutateOptions = {},