@naturalcycles/js-lib 15.75.0 → 15.76.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.
@@ -217,6 +217,20 @@ 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 a non-empty iterable (Set, Map.values(), generator, etc.).
222
+ * Throws 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;
229
+ /**
230
+ * Returns the first item of an iterable (or undefined if the iterable is empty).
231
+ * See `_firstFromIterable` for the iteration semantics.
232
+ */
233
+ export declare function _firstOrUndefinedFromIterable<T>(iter: Iterable<T>): T | undefined;
220
234
  export declare function _minOrUndefined<T>(array: readonly T[]): NonNullable<T> | undefined;
221
235
  /**
222
236
  * Filters out nullish values (undefined and null).
@@ -403,6 +403,28 @@ export function _first(array) {
403
403
  export function _firstOrUndefined(array) {
404
404
  return array[0];
405
405
  }
406
+ /**
407
+ * Returns the first item of a non-empty iterable (Set, Map.values(), generator, etc.).
408
+ * Throws 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
+ throw new Error('_firstFromIterable called on empty iterable');
418
+ }
419
+ /**
420
+ * Returns the first item of an iterable (or undefined if the iterable is empty).
421
+ * See `_firstFromIterable` for the iteration semantics.
422
+ */
423
+ export function _firstOrUndefinedFromIterable(iter) {
424
+ for (const item of iter)
425
+ return item;
426
+ return undefined;
427
+ }
406
428
  export function _minOrUndefined(array) {
407
429
  let min;
408
430
  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,46 @@ 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 a non-empty object.
92
+ * Throws if the object is empty.
93
+ *
94
+ * Performance-optimised: uses `for...in` with an early return to avoid
95
+ * allocating the full `Object.keys(obj)` array. The `Object.hasOwn` filter
96
+ * matches `Object.keys()` semantics (own enumerable string keys only) and
97
+ * satisfies the `guard-for-in` lint rule; cost is a single check before
98
+ * the early return. Iteration order matches `Object.keys()` for plain
99
+ * objects (integer-like keys ascending first, then string keys in
100
+ * insertion order).
101
+ */
102
+ export declare function _firstKey<T extends AnyObject>(obj: T): keyof T;
103
+ /**
104
+ * Returns the first key of the object (or undefined if the object is empty).
105
+ * See `_firstKey` for the iteration-order contract.
106
+ */
107
+ export declare function _firstKeyOrUndefined<T extends AnyObject>(obj: T): keyof T | undefined;
108
+ /**
109
+ * Returns the first value of a non-empty object.
110
+ * Throws if the object is empty.
111
+ * See `_firstKey` for the iteration-order contract.
112
+ */
113
+ export declare function _firstValue<T extends AnyObject>(obj: T): ValueOf<T>;
114
+ /**
115
+ * Returns the first value of the object (or undefined if the object is empty).
116
+ * See `_firstKey` for the iteration-order contract.
117
+ */
118
+ export declare function _firstValueOrUndefined<T extends AnyObject>(obj: T): ValueOf<T> | undefined;
119
+ /**
120
+ * Returns the first [key, value] tuple of a non-empty object.
121
+ * Throws if the object is empty.
122
+ * See `_firstKey` for the iteration-order contract.
123
+ */
124
+ export declare function _firstEntry<T extends AnyObject>(obj: T): [keyof T, ValueOf<T>];
125
+ /**
126
+ * Returns the first [key, value] tuple of the object (or undefined if the object is empty).
127
+ * See `_firstKey` for the iteration-order contract.
128
+ */
129
+ export declare function _firstEntryOrUndefined<T extends AnyObject>(obj: T): [keyof T, ValueOf<T>] | undefined;
90
130
  export declare function _objectNullValuesToUndefined<T extends AnyObject>(obj: T, opt?: MutateOptions): T;
91
131
  /**
92
132
  * Deep copy object (by json parse/stringify, since it has unbeatable performance+simplicity combo).
@@ -191,6 +191,82 @@ 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 a non-empty object.
196
+ * Throws if the object is empty.
197
+ *
198
+ * Performance-optimised: uses `for...in` with an early return to avoid
199
+ * allocating the full `Object.keys(obj)` array. The `Object.hasOwn` filter
200
+ * matches `Object.keys()` semantics (own enumerable string keys only) and
201
+ * satisfies the `guard-for-in` lint rule; cost is a single check before
202
+ * the early return. Iteration order matches `Object.keys()` for plain
203
+ * objects (integer-like keys ascending first, then string keys in
204
+ * insertion order).
205
+ */
206
+ export function _firstKey(obj) {
207
+ for (const k in obj) {
208
+ if (Object.hasOwn(obj, k))
209
+ return k;
210
+ }
211
+ throw new Error('_firstKey called on empty object');
212
+ }
213
+ /**
214
+ * Returns the first key of the object (or undefined if the object is empty).
215
+ * See `_firstKey` for the iteration-order contract.
216
+ */
217
+ export function _firstKeyOrUndefined(obj) {
218
+ for (const k in obj) {
219
+ if (Object.hasOwn(obj, k))
220
+ return k;
221
+ }
222
+ return undefined;
223
+ }
224
+ /**
225
+ * Returns the first value of a non-empty object.
226
+ * Throws if the object is empty.
227
+ * See `_firstKey` for the iteration-order contract.
228
+ */
229
+ export function _firstValue(obj) {
230
+ for (const k in obj) {
231
+ if (Object.hasOwn(obj, k))
232
+ return obj[k];
233
+ }
234
+ throw new Error('_firstValue called on empty object');
235
+ }
236
+ /**
237
+ * Returns the first value of the object (or undefined if the object is empty).
238
+ * See `_firstKey` for the iteration-order contract.
239
+ */
240
+ export function _firstValueOrUndefined(obj) {
241
+ for (const k in obj) {
242
+ if (Object.hasOwn(obj, k))
243
+ return obj[k];
244
+ }
245
+ return undefined;
246
+ }
247
+ /**
248
+ * Returns the first [key, value] tuple of a non-empty object.
249
+ * Throws if the object is empty.
250
+ * See `_firstKey` for the iteration-order contract.
251
+ */
252
+ export function _firstEntry(obj) {
253
+ for (const k in obj) {
254
+ if (Object.hasOwn(obj, k))
255
+ return [k, obj[k]];
256
+ }
257
+ throw new Error('_firstEntry called on empty object');
258
+ }
259
+ /**
260
+ * Returns the first [key, value] tuple of the object (or undefined if the object is empty).
261
+ * See `_firstKey` for the iteration-order contract.
262
+ */
263
+ export function _firstEntryOrUndefined(obj) {
264
+ for (const k in obj) {
265
+ if (Object.hasOwn(obj, k))
266
+ return [k, obj[k]];
267
+ }
268
+ return undefined;
269
+ }
194
270
  export function _objectNullValuesToUndefined(obj, opt = {}) {
195
271
  return _mapValues(obj, (_k, v) => (v === null ? undefined : v), opt);
196
272
  }
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.1",
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,28 @@ 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 a non-empty iterable (Set, Map.values(), generator, etc.).
473
+ * Throws 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 {
480
+ for (const item of iter) return item
481
+ throw new Error('_firstFromIterable called on empty iterable')
482
+ }
483
+
484
+ /**
485
+ * Returns the first item of an iterable (or undefined if the iterable is empty).
486
+ * See `_firstFromIterable` for the iteration semantics.
487
+ */
488
+ export function _firstOrUndefinedFromIterable<T>(iter: Iterable<T>): T | undefined {
489
+ for (const item of iter) return item
490
+ return undefined
491
+ }
492
+
471
493
  export function _minOrUndefined<T>(array: readonly T[]): NonNullable<T> | undefined {
472
494
  let min: NonNullable<T> | undefined
473
495
  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,84 @@ 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 a non-empty object.
247
+ * Throws if the object is empty.
248
+ *
249
+ * Performance-optimised: uses `for...in` with an early return to avoid
250
+ * allocating the full `Object.keys(obj)` array. The `Object.hasOwn` filter
251
+ * matches `Object.keys()` semantics (own enumerable string keys only) and
252
+ * satisfies the `guard-for-in` lint rule; cost is a single check before
253
+ * the early return. Iteration order matches `Object.keys()` for plain
254
+ * objects (integer-like keys ascending first, then string keys in
255
+ * insertion order).
256
+ */
257
+ export function _firstKey<T extends AnyObject>(obj: T): keyof T {
258
+ for (const k in obj) {
259
+ if (Object.hasOwn(obj, k)) return k as keyof T
260
+ }
261
+ throw new Error('_firstKey called on empty object')
262
+ }
263
+
264
+ /**
265
+ * Returns the first key of the object (or undefined if the object is empty).
266
+ * See `_firstKey` for the iteration-order contract.
267
+ */
268
+ export function _firstKeyOrUndefined<T extends AnyObject>(obj: T): keyof T | undefined {
269
+ for (const k in obj) {
270
+ if (Object.hasOwn(obj, k)) return k as keyof T
271
+ }
272
+ return undefined
273
+ }
274
+
275
+ /**
276
+ * Returns the first value of a non-empty object.
277
+ * Throws if the object is empty.
278
+ * See `_firstKey` for the iteration-order contract.
279
+ */
280
+ export function _firstValue<T extends AnyObject>(obj: T): ValueOf<T> {
281
+ for (const k in obj) {
282
+ if (Object.hasOwn(obj, k)) return obj[k] as ValueOf<T>
283
+ }
284
+ throw new Error('_firstValue called on empty object')
285
+ }
286
+
287
+ /**
288
+ * Returns the first value of the object (or undefined if the object is empty).
289
+ * See `_firstKey` for the iteration-order contract.
290
+ */
291
+ export function _firstValueOrUndefined<T extends AnyObject>(obj: T): ValueOf<T> | undefined {
292
+ for (const k in obj) {
293
+ if (Object.hasOwn(obj, k)) return obj[k] as ValueOf<T>
294
+ }
295
+ return undefined
296
+ }
297
+
298
+ /**
299
+ * Returns the first [key, value] tuple of a non-empty object.
300
+ * Throws if the object is empty.
301
+ * See `_firstKey` for the iteration-order contract.
302
+ */
303
+ export function _firstEntry<T extends AnyObject>(obj: T): [keyof T, ValueOf<T>] {
304
+ for (const k in obj) {
305
+ if (Object.hasOwn(obj, k)) return [k as keyof T, obj[k] as ValueOf<T>]
306
+ }
307
+ throw new Error('_firstEntry called on empty object')
308
+ }
309
+
310
+ /**
311
+ * Returns the first [key, value] tuple of the object (or undefined if the object is empty).
312
+ * See `_firstKey` for the iteration-order contract.
313
+ */
314
+ export function _firstEntryOrUndefined<T extends AnyObject>(
315
+ obj: T,
316
+ ): [keyof T, ValueOf<T>] | undefined {
317
+ for (const k in obj) {
318
+ if (Object.hasOwn(obj, k)) return [k as keyof T, obj[k] as ValueOf<T>]
319
+ }
320
+ return undefined
321
+ }
322
+
245
323
  export function _objectNullValuesToUndefined<T extends AnyObject>(
246
324
  obj: T,
247
325
  opt: MutateOptions = {},