@naturalcycles/js-lib 15.96.0 → 15.97.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 (52) hide show
  1. package/dist/abort.d.ts +3 -3
  2. package/dist/abort.js +1 -1
  3. package/dist/analytics/analytics.model.d.ts +1 -1
  4. package/dist/array/array.util.d.ts +4 -4
  5. package/dist/browser/i18n/translation.service.d.ts +1 -1
  6. package/dist/datetime/localDate.d.ts +4 -4
  7. package/dist/datetime/localTime.d.ts +4 -4
  8. package/dist/decorators/decorator.util.d.ts +1 -1
  9. package/dist/decorators/timeout.decorator.js +5 -2
  10. package/dist/error/error.model.d.ts +1 -1
  11. package/dist/http/fetcher.js +5 -4
  12. package/dist/http/http.model.d.ts +1 -1
  13. package/dist/log/commonLogger.d.ts +1 -1
  14. package/dist/math/math.util.d.ts +8 -8
  15. package/dist/math/math.util.js +2 -2
  16. package/dist/math/stack.util.d.ts +8 -3
  17. package/dist/math/stack.util.js +13 -6
  18. package/dist/number/number.util.d.ts +1 -1
  19. package/dist/object/keySortedMap.d.ts +1 -1
  20. package/dist/object/lazyKeySortedMap.d.ts +1 -1
  21. package/dist/object/object.util.d.ts +1 -1
  22. package/dist/object/sortObject.d.ts +1 -1
  23. package/dist/promise/pTimeout.js +1 -1
  24. package/dist/semver.d.ts +4 -4
  25. package/dist/string/pupa.d.ts +1 -1
  26. package/package.json +2 -2
  27. package/src/abort.ts +4 -4
  28. package/src/analytics/analytics.model.ts +1 -1
  29. package/src/array/array.util.ts +4 -4
  30. package/src/browser/i18n/translation.service.ts +1 -1
  31. package/src/browser/imageFitter.ts +1 -1
  32. package/src/datetime/localDate.ts +4 -4
  33. package/src/datetime/localTime.ts +4 -4
  34. package/src/decorators/decorator.util.ts +1 -1
  35. package/src/decorators/timeout.decorator.ts +5 -2
  36. package/src/error/error.model.ts +1 -1
  37. package/src/http/fetcher.ts +6 -5
  38. package/src/http/http.model.ts +1 -1
  39. package/src/log/commonLogger.ts +1 -1
  40. package/src/math/math.util.ts +13 -10
  41. package/src/math/stack.util.ts +16 -8
  42. package/src/number/number.util.ts +1 -1
  43. package/src/object/keySortedMap.ts +1 -1
  44. package/src/object/lazyKeySortedMap.ts +1 -1
  45. package/src/object/object.util.ts +5 -1
  46. package/src/object/sortObject.ts +1 -1
  47. package/src/promise/pMap.ts +2 -2
  48. package/src/promise/pTimeout.ts +1 -1
  49. package/src/semver.ts +4 -4
  50. package/src/string/pupa.ts +5 -1
  51. package/src/string/readingTime.ts +1 -1
  52. package/src/ua/ua.ts +1 -1
package/dist/abort.d.ts CHANGED
@@ -40,7 +40,7 @@ export declare function polyfilledAbortSignalTimeout(ms: NumberOfMilliseconds):
40
40
  * Returns AbortSignal.any(signals) is the array (after filtering undefined inputs) is not empty,
41
41
  * otherwise undefined.
42
42
  */
43
- export declare function abortSignalAnyOrUndefined(signals: (AbortSignal | undefined)[]): AbortSignal | undefined;
43
+ export declare function abortSignalAnyOrUndefined(signals: readonly (AbortSignal | undefined)[]): AbortSignal | undefined;
44
44
  /**
45
45
  * Returns an AbortSignal that aborts when any of the given signals abort.
46
46
  * Uses native `AbortSignal.any()` when available, falls back to a polyfill.
@@ -50,5 +50,5 @@ export declare function abortSignalAnyOrUndefined(signals: (AbortSignal | undefi
50
50
  *
51
51
  * If only 1 signal is passed in the input array - that Signal is returned as-is.
52
52
  */
53
- export declare function abortSignalAny(signals: AbortSignal[]): AbortSignal;
54
- export declare function polyfilledAbortSignalAny(signals: AbortSignal[]): AbortSignal;
53
+ export declare function abortSignalAny(signals: readonly AbortSignal[]): AbortSignal;
54
+ export declare function polyfilledAbortSignalAny(signals: readonly AbortSignal[]): AbortSignal;
package/dist/abort.js CHANGED
@@ -58,7 +58,7 @@ export function abortSignalAny(signals) {
58
58
  return signals[0];
59
59
  }
60
60
  return typeof AbortSignal.any === 'function'
61
- ? AbortSignal.any(signals)
61
+ ? AbortSignal.any(signals) // lib.dom types require a mutable array, but it is only read
62
62
  : polyfilledAbortSignalAny(signals);
63
63
  }
64
64
  export function polyfilledAbortSignalAny(signals) {
@@ -60,7 +60,7 @@ export interface AnalyticsClientEvent {
60
60
  userId?: AnalyticsDistinctId;
61
61
  }
62
62
  /** Canonical utm parameters. */
63
- export declare const CANONICAL_UTM_PARAMS: string[];
63
+ export declare const CANONICAL_UTM_PARAMS: readonly string[];
64
64
  /** Keyed by `initial_<canonical utm param>`, to avoid permanently saving non-standard ones. */
65
65
  export type FirstTouchUtms = StringMap<string | null>;
66
66
  /**
@@ -138,7 +138,7 @@ export declare function _countBy<T>(items: Iterable<T>, mapper: Mapper<T, any>):
138
138
  * _intersection([2, 1], [2, 3])
139
139
  * // [2]
140
140
  */
141
- export declare function _intersection<T>(a1: T[], a2: T[] | Set<T>): T[];
141
+ export declare function _intersection<T>(a1: readonly T[], a2: readonly T[] | Set<T>): T[];
142
142
  /**
143
143
  * Returns true if there is at least 1 item common between 2 arrays.
144
144
  * Otherwise returns false.
@@ -147,7 +147,7 @@ export declare function _intersection<T>(a1: T[], a2: T[] | Set<T>): T[];
147
147
  *
148
148
  * Passing second array as Set is more performant (it'll skip turning the array into Set in-place).
149
149
  */
150
- export declare function _intersectsWith<T>(a1: T[], a2: T[] | Set<T>): boolean;
150
+ export declare function _intersectsWith<T>(a1: readonly T[], a2: readonly T[] | Set<T>): boolean;
151
151
  /**
152
152
  * Returns array1 minus array2.
153
153
  *
@@ -157,11 +157,11 @@ export declare function _intersectsWith<T>(a1: T[], a2: T[] | Set<T>): boolean;
157
157
  *
158
158
  * Passing second array as Set is more performant (it'll skip turning the array into Set in-place).
159
159
  */
160
- export declare function _difference<T>(a1: T[], a2: T[] | Set<T>): T[];
160
+ export declare function _difference<T>(a1: readonly T[], a2: readonly T[] | Set<T>): T[];
161
161
  /**
162
162
  * Does NOT mutate the array, returns a filtered array instead.
163
163
  */
164
- export declare function _arrayRemove<T>(a: T[], itemToRemove: T): T[];
164
+ export declare function _arrayRemove<T>(a: readonly T[], itemToRemove: T): T[];
165
165
  /**
166
166
  * "Toggles" an item to be present or absent in the array,
167
167
  * based on the predicate. Respects uniqueness.
@@ -37,7 +37,7 @@ export declare class TranslationService {
37
37
  * Loads locale(s) (if not already cached) via configured TranslationLoader.
38
38
  * Resolves promise when done (ready to be used).
39
39
  */
40
- loadLocale(locale: string | string[]): Promise<void>;
40
+ loadLocale(locale: string | readonly string[]): Promise<void>;
41
41
  /**
42
42
  * Will invoke `missingTranslationHandler` on missing tranlation.
43
43
  *
@@ -250,21 +250,21 @@ declare class LocalDateFactory {
250
250
  /**
251
251
  * Returns the earliest (min) LocalDate from the array, or undefined if the array is empty.
252
252
  */
253
- minOrUndefined(items: LocalDateInputNullable[]): LocalDate | undefined;
253
+ minOrUndefined(items: readonly LocalDateInputNullable[]): LocalDate | undefined;
254
254
  /**
255
255
  * Returns the earliest LocalDate from the array.
256
256
  * Throws if the array is empty.
257
257
  */
258
- min(items: LocalDateInputNullable[]): LocalDate;
258
+ min(items: readonly LocalDateInputNullable[]): LocalDate;
259
259
  /**
260
260
  * Returns the latest (max) LocalDate from the array, or undefined if the array is empty.
261
261
  */
262
- maxOrUndefined(items: LocalDateInputNullable[]): LocalDate | undefined;
262
+ maxOrUndefined(items: readonly LocalDateInputNullable[]): LocalDate | undefined;
263
263
  /**
264
264
  * Returns the latest LocalDate from the array.
265
265
  * Throws if the array is empty.
266
266
  */
267
- max(items: LocalDateInputNullable[]): LocalDate;
267
+ max(items: readonly LocalDateInputNullable[]): LocalDate;
268
268
  /**
269
269
  * Returns the range (array) of LocalDates between min and max.
270
270
  * By default, min is included, max is excluded.
@@ -322,10 +322,10 @@ declare class LocalTimeFactory {
322
322
  */
323
323
  isTimezoneValid(tz: string): boolean;
324
324
  sort(items: LocalTime[], dir?: SortDirection, opt?: MutateOptions): LocalTime[];
325
- minOrUndefined(items: LocalTimeInputNullable[]): LocalTime | undefined;
326
- min(items: LocalTimeInputNullable[]): LocalTime;
327
- maxOrUndefined(items: LocalTimeInputNullable[]): LocalTime | undefined;
328
- max(items: LocalTimeInputNullable[]): LocalTime;
325
+ minOrUndefined(items: readonly LocalTimeInputNullable[]): LocalTime | undefined;
326
+ min(items: readonly LocalTimeInputNullable[]): LocalTime;
327
+ maxOrUndefined(items: readonly LocalTimeInputNullable[]): LocalTime | undefined;
328
+ max(items: readonly LocalTimeInputNullable[]): LocalTime;
329
329
  }
330
330
  interface LocalTimeFn extends LocalTimeFactory {
331
331
  (d: LocalTimeInput): LocalTime;
@@ -21,4 +21,4 @@ export declare function _getTargetMethodSignature(target: AnyObject, keyStr: str
21
21
  * returns:
22
22
  * a, b, c
23
23
  */
24
- export declare function _getArgsSignature(args?: any[], logArgs?: boolean): string;
24
+ export declare function _getArgsSignature(args?: readonly any[], logArgs?: boolean): string;
@@ -10,8 +10,11 @@ export function _Timeout(opt) {
10
10
  const keyStr = String(key);
11
11
  descriptor.value = async function (...args) {
12
12
  const ctx = this;
13
- opt.name ||= _getMethodSignature(ctx, keyStr);
14
- return await pTimeout(() => originalFn.apply(this, args), opt);
13
+ // Not mutating the shared decorator options, as the name depends on the instance (ctx)
14
+ return await pTimeout(() => originalFn.apply(this, args), {
15
+ ...opt,
16
+ name: opt.name || _getMethodSignature(ctx, keyStr),
17
+ });
15
18
  };
16
19
  return descriptor;
17
20
  };
@@ -81,7 +81,7 @@ export interface ErrorData {
81
81
  /**
82
82
  * Used in e.g http 403 error.
83
83
  */
84
- adminPermissionsRequired?: string[];
84
+ adminPermissionsRequired?: readonly string[];
85
85
  /**
86
86
  * Open-ended.
87
87
  */
@@ -706,10 +706,6 @@ export class Fetcher {
706
706
  }
707
707
  normalizeCfg(cfg) {
708
708
  const { debug = false, logger = consoleLogger } = cfg;
709
- if (cfg.baseUrl?.endsWith('/')) {
710
- logger.warn(`Fetcher: baseUrl should not end with slash: ${cfg.baseUrl}`);
711
- cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1);
712
- }
713
709
  const norm = _merge({
714
710
  baseUrl: '',
715
711
  name: this.getFetcherName(cfg),
@@ -746,6 +742,11 @@ export class Fetcher {
746
742
  errorData: {},
747
743
  }, _omit(cfg, ['method', 'credentials', 'headers', 'redirect', 'logger', 'name', 'keepalive']));
748
744
  norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase());
745
+ // Normalizing `norm`, not the input `cfg`, to not mutate the input
746
+ if (norm.baseUrl.endsWith('/')) {
747
+ logger.warn(`Fetcher: baseUrl should not end with slash: ${norm.baseUrl}`);
748
+ norm.baseUrl = norm.baseUrl.slice(0, -1);
749
+ }
749
750
  return norm;
750
751
  }
751
752
  getFetcherName(cfg) {
@@ -4,4 +4,4 @@ export type HttpStatusFamily = 5 | 4 | 3 | 2 | 1;
4
4
  * Number, representing a valid Http status code (e.g 401, 500, etc)
5
5
  */
6
6
  export type HttpStatusCode = number;
7
- export declare const HTTP_METHODS: HttpMethod[];
7
+ export declare const HTTP_METHODS: readonly HttpMethod[];
@@ -74,4 +74,4 @@ export declare function createCommonLoggerAtLevel(logger?: CommonLogger, minLeve
74
74
  /**
75
75
  * Creates a "proxy" CommonLogger that pipes log entries to all provided sub-loggers.
76
76
  */
77
- export declare function commonLoggerPipe(loggers: CommonLogger[]): CommonLogger;
77
+ export declare function commonLoggerPipe(loggers: readonly CommonLogger[]): CommonLogger;
@@ -6,11 +6,11 @@
6
6
  * _average([1, 2, 3, 4])
7
7
  * // 2.5
8
8
  */
9
- export declare function _average(values: number[]): number;
9
+ export declare function _average(values: readonly number[]): number;
10
10
  /**
11
11
  * Same as _average, but safely returns null if input array is empty or nullish.
12
12
  */
13
- export declare function _averageOrNull(values: number[] | undefined | null): number | null;
13
+ export declare function _averageOrNull(values: readonly number[] | undefined | null): number | null;
14
14
  /**
15
15
  * @returns Population standard deviation of the array of numbers
16
16
  *
@@ -19,15 +19,15 @@ export declare function _averageOrNull(values: number[] | undefined | null): num
19
19
  * _stdDev([2, 4, 4, 4, 5, 5, 7, 9])
20
20
  * // 2
21
21
  */
22
- export declare function _stdDev(values: number[]): number;
22
+ export declare function _stdDev(values: readonly number[]): number;
23
23
  /**
24
24
  * Same as _stdDev, but safely returns null if input array is empty or nullish.
25
25
  */
26
- export declare function _stdDevOrNull(values: number[] | undefined | null): number | null;
26
+ export declare function _stdDevOrNull(values: readonly number[] | undefined | null): number | null;
27
27
  /**
28
28
  * valuesArray and weightsArray length is expected to be the same.
29
29
  */
30
- export declare function _averageWeighted(values: number[], weights: number[]): number;
30
+ export declare function _averageWeighted(values: readonly number[], weights: readonly number[]): number;
31
31
  /**
32
32
  * @example
33
33
  *
@@ -40,11 +40,11 @@ export declare function _averageWeighted(values: number[], weights: number[]): n
40
40
  * _percentile([1, 2, 3], 100)
41
41
  * // 3
42
42
  */
43
- export declare function _percentile(values: number[], pc: number): number;
43
+ export declare function _percentile(values: readonly number[], pc: number): number;
44
44
  /**
45
45
  * A tiny bit more efficient function than calling _percentile individually.
46
46
  */
47
- export declare function _percentiles(values: number[], pcs: number[]): Record<number, number>;
47
+ export declare function _percentiles(values: readonly number[], pcs: readonly number[]): Record<number, number>;
48
48
  /**
49
49
  * @example
50
50
  *
@@ -54,4 +54,4 @@ export declare function _percentiles(values: number[], pcs: number[]): Record<nu
54
54
  * _median([1, 2, 3, 4])
55
55
  * // 2.5
56
56
  */
57
- export declare function _median(values: number[]): number;
57
+ export declare function _median(values: readonly number[]): number;
@@ -70,7 +70,7 @@ export function _averageWeighted(values, weights) {
70
70
  * // 3
71
71
  */
72
72
  export function _percentile(values, pc) {
73
- const sorted = values.sort(comparators.numericAsc);
73
+ const sorted = values.toSorted(comparators.numericAsc);
74
74
  // Floating pos in the range of [0; length - 1]
75
75
  const pos = ((values.length - 1) * pc) / 100;
76
76
  const dec = pos % 1;
@@ -83,7 +83,7 @@ export function _percentile(values, pc) {
83
83
  */
84
84
  export function _percentiles(values, pcs) {
85
85
  const r = {};
86
- const sorted = values.sort(comparators.numericAsc);
86
+ const sorted = values.toSorted(comparators.numericAsc);
87
87
  for (const pc of pcs) {
88
88
  // Floating pos in the range of [0; length - 1]
89
89
  const pos = ((values.length - 1) * pc) / 100;
@@ -13,7 +13,12 @@ export declare class Stack<T> {
13
13
  * Currently this slot contains OLDEST item (if any).
14
14
  */
15
15
  private nextIndex;
16
- readonly items: T[];
16
+ private readonly buffer;
17
+ /**
18
+ * Raw "items buffer" as-is (not ordered properly).
19
+ * Readonly, because mutating it (e.g sorting in-place) would corrupt the Stack.
20
+ */
21
+ get items(): readonly T[];
17
22
  push(item: T): this;
18
23
  /**
19
24
  * Fill (overwrite) the whole Stack (all its items) with the passed `item`.
@@ -23,7 +28,7 @@ export declare class Stack<T> {
23
28
  * Returns last items in the right order.
24
29
  * Unlike raw `items` property that returns "items buffer" as-is (not ordered properly).
25
30
  */
26
- get itemsOrdered(): T[];
31
+ get itemsOrdered(): readonly T[];
27
32
  }
28
33
  /**
29
34
  * Fixed-size FILO stack of Numbers.
@@ -46,5 +51,5 @@ export declare class NumberStack extends Stack<number> {
46
51
  * Returns null if Stack is empty.
47
52
  */
48
53
  percentileOrNull(pc: number): number | null;
49
- percentiles(pcs: number[]): Record<number, number>;
54
+ percentiles(pcs: readonly number[]): Record<number, number>;
50
55
  }
@@ -16,9 +16,16 @@ export class Stack {
16
16
  * Currently this slot contains OLDEST item (if any).
17
17
  */
18
18
  nextIndex = 0;
19
- items = [];
19
+ buffer = [];
20
+ /**
21
+ * Raw "items buffer" as-is (not ordered properly).
22
+ * Readonly, because mutating it (e.g sorting in-place) would corrupt the Stack.
23
+ */
24
+ get items() {
25
+ return this.buffer;
26
+ }
20
27
  push(item) {
21
- this.items[this.nextIndex] = item;
28
+ this.buffer[this.nextIndex] = item;
22
29
  this.nextIndex = this.nextIndex === this.size - 1 ? 0 : this.nextIndex + 1;
23
30
  return this;
24
31
  }
@@ -27,7 +34,7 @@ export class Stack {
27
34
  */
28
35
  fill(item) {
29
36
  for (let i = 0; i < this.size; i++) {
30
- this.items[i] = item;
37
+ this.buffer[i] = item;
31
38
  }
32
39
  return this;
33
40
  }
@@ -36,12 +43,12 @@ export class Stack {
36
43
  * Unlike raw `items` property that returns "items buffer" as-is (not ordered properly).
37
44
  */
38
45
  get itemsOrdered() {
39
- if (this.items.length < this.size) {
46
+ if (this.buffer.length < this.size) {
40
47
  // Buffer is not filled yet, just return it as-is
41
- return this.items;
48
+ return this.buffer;
42
49
  }
43
50
  // Buffer was filled and started to "overwrite itself", will need to return 2 slices
44
- return [...this.items.slice(this.nextIndex), ...this.items.slice(0, this.nextIndex)];
51
+ return [...this.buffer.slice(this.nextIndex), ...this.buffer.slice(0, this.nextIndex)];
45
52
  }
46
53
  }
47
54
  /**
@@ -5,7 +5,7 @@ export declare function _randomInt(minIncl: number, maxIncl: number): number;
5
5
  * Should be used on non-empty arrays! (otherwise will return undefined,
6
6
  * which is not reflected in the output type)
7
7
  */
8
- export declare function _randomArrayItem<T>(array: T[]): T;
8
+ export declare function _randomArrayItem<T>(array: readonly T[]): T;
9
9
  /**
10
10
  * Convenience function to "throttle" some code - run it less often.
11
11
  *
@@ -24,7 +24,7 @@ export interface KeySortedMap<K, V> extends Map<K, V> {
24
24
  export declare class KeySortedMap<K, V> {
25
25
  #private;
26
26
  private readonly map;
27
- constructor(entries?: [K, V][], opt?: KeySortedMapOptions<K>);
27
+ constructor(entries?: readonly [K, V][], opt?: KeySortedMapOptions<K>);
28
28
  /**
29
29
  * Convenience way to create KeySortedMap from object.
30
30
  */
@@ -21,7 +21,7 @@ export declare class LazyKeySortedMap<K, V> {
21
21
  private readonly map;
22
22
  private readonly maybeSortedKeys;
23
23
  private keysAreSorted;
24
- constructor(entries?: [K, V][], opt?: LazyKeySortedMapOptions<K>);
24
+ constructor(entries?: readonly [K, V][], opt?: LazyKeySortedMapOptions<K>);
25
25
  /**
26
26
  * Convenience way to create KeySortedMap from object.
27
27
  */
@@ -28,7 +28,7 @@ export declare function _omitWithUndefined<T extends AnyObject, K extends keyof
28
28
  * 'account.updated',
29
29
  * ])
30
30
  */
31
- export declare function _mask<T extends AnyObject>(obj: T, props: string[], opt?: MutateOptions): T;
31
+ export declare function _mask<T extends AnyObject>(obj: T, props: readonly string[], opt?: MutateOptions): T;
32
32
  /**
33
33
  * Removes "falsy" values from the object.
34
34
  */
@@ -4,4 +4,4 @@ import type { AnyObject } from '../types.js';
4
4
  * All keys that are not listed in `keyOrder` go last.
5
5
  * Does not mutate original object.
6
6
  */
7
- export declare function _sortObject<T extends AnyObject>(obj: T, keyOrder: (keyof T)[]): T;
7
+ export declare function _sortObject<T extends AnyObject>(obj: T, keyOrder: readonly (keyof T)[]): T;
@@ -8,7 +8,7 @@ import { _typeCast } from '../types.js';
8
8
  * If the Function rejects - passes this rejection further.
9
9
  */
10
10
  export function pTimeoutFn(fn, opt) {
11
- opt.name ||= fn.name;
11
+ opt = { ...opt, name: opt.name || fn.name }; // copy, to not mutate the input options
12
12
  if (!opt.timeout) {
13
13
  return fn;
14
14
  }
package/dist/semver.d.ts CHANGED
@@ -46,21 +46,21 @@ declare class SemverFactory {
46
46
  /**
47
47
  * Returns the highest (max) Semver from the array, or undefined if the array is empty.
48
48
  */
49
- maxOrUndefined(items: SemverInputNullable[]): Semver | undefined;
49
+ maxOrUndefined(items: readonly SemverInputNullable[]): Semver | undefined;
50
50
  /**
51
51
  * Returns the highest Semver from the array.
52
52
  * Throws if the array is empty.
53
53
  */
54
- max(items: SemverInputNullable[]): Semver;
54
+ max(items: readonly SemverInputNullable[]): Semver;
55
55
  /**
56
56
  * Returns the lowest (min) Semver from the array, or undefined if the array is empty.
57
57
  */
58
- minOrUndefined(items: SemverInputNullable[]): Semver | undefined;
58
+ minOrUndefined(items: readonly SemverInputNullable[]): Semver | undefined;
59
59
  /**
60
60
  * Returns the lowest Semver from the array.
61
61
  * Throws if the array is empty.
62
62
  */
63
- min(items: SemverInputNullable[]): Semver;
63
+ min(items: readonly SemverInputNullable[]): Semver;
64
64
  /**
65
65
  * Sorts an array of Semvers in `dir` order (ascending by default).
66
66
  */
@@ -19,4 +19,4 @@ export interface PupaOptions {
19
19
  /**
20
20
  * API: https://github.com/sindresorhus/pupa
21
21
  */
22
- export declare function pupa(template: string, data: any[] | AnyObject, opt?: PupaOptions): string;
22
+ export declare function pupa(template: string, data: readonly any[] | AnyObject, opt?: PupaOptions): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
3
  "type": "module",
4
- "version": "15.96.0",
4
+ "version": "15.97.0",
5
5
  "dependencies": {
6
6
  "tslib": "^2"
7
7
  },
@@ -14,7 +14,7 @@
14
14
  }
15
15
  },
16
16
  "devDependencies": {
17
- "@naturalcycles/dev-lib": "0.0.0",
17
+ "@naturalcycles/dev-lib": "20.68.0",
18
18
  "@types/node": "^26",
19
19
  "@types/semver": "^7",
20
20
  "typescript": "^7",
package/src/abort.ts CHANGED
@@ -67,7 +67,7 @@ export function polyfilledAbortSignalTimeout(ms: NumberOfMilliseconds): AbortSig
67
67
  * otherwise undefined.
68
68
  */
69
69
  export function abortSignalAnyOrUndefined(
70
- signals: (AbortSignal | undefined)[],
70
+ signals: readonly (AbortSignal | undefined)[],
71
71
  ): AbortSignal | undefined {
72
72
  const filtered = signals.filter(_isTruthy)
73
73
  return filtered.length ? abortSignalAny(filtered) : undefined
@@ -82,17 +82,17 @@ export function abortSignalAnyOrUndefined(
82
82
  *
83
83
  * If only 1 signal is passed in the input array - that Signal is returned as-is.
84
84
  */
85
- export function abortSignalAny(signals: AbortSignal[]): AbortSignal {
85
+ export function abortSignalAny(signals: readonly AbortSignal[]): AbortSignal {
86
86
  if (signals.length === 1) {
87
87
  return signals[0]!
88
88
  }
89
89
 
90
90
  return typeof AbortSignal.any === 'function'
91
- ? AbortSignal.any(signals)
91
+ ? AbortSignal.any(signals as AbortSignal[]) // lib.dom types require a mutable array, but it is only read
92
92
  : polyfilledAbortSignalAny(signals)
93
93
  }
94
94
 
95
- export function polyfilledAbortSignalAny(signals: AbortSignal[]): AbortSignal {
95
+ export function polyfilledAbortSignalAny(signals: readonly AbortSignal[]): AbortSignal {
96
96
  const ac = new AbortController()
97
97
 
98
98
  for (const signal of signals) {
@@ -70,7 +70,7 @@ export interface AnalyticsClientEvent {
70
70
  }
71
71
 
72
72
  /** Canonical utm parameters. */
73
- export const CANONICAL_UTM_PARAMS = [
73
+ export const CANONICAL_UTM_PARAMS: readonly string[] = [
74
74
  'utm_source',
75
75
  'utm_medium',
76
76
  'utm_campaign',
@@ -302,7 +302,7 @@ export function _countBy<T>(items: Iterable<T>, mapper: Mapper<T, any>): StringM
302
302
  * _intersection([2, 1], [2, 3])
303
303
  * // [2]
304
304
  */
305
- export function _intersection<T>(a1: T[], a2: T[] | Set<T>): T[] {
305
+ export function _intersection<T>(a1: readonly T[], a2: readonly T[] | Set<T>): T[] {
306
306
  const a2set = a2 instanceof Set ? a2 : new Set(a2)
307
307
  return a1.filter(v => a2set.has(v))
308
308
  }
@@ -315,7 +315,7 @@ export function _intersection<T>(a1: T[], a2: T[] | Set<T>): T[] {
315
315
  *
316
316
  * Passing second array as Set is more performant (it'll skip turning the array into Set in-place).
317
317
  */
318
- export function _intersectsWith<T>(a1: T[], a2: T[] | Set<T>): boolean {
318
+ export function _intersectsWith<T>(a1: readonly T[], a2: readonly T[] | Set<T>): boolean {
319
319
  const a2set = a2 instanceof Set ? a2 : new Set(a2)
320
320
  return a1.some(v => a2set.has(v))
321
321
  }
@@ -329,7 +329,7 @@ export function _intersectsWith<T>(a1: T[], a2: T[] | Set<T>): boolean {
329
329
  *
330
330
  * Passing second array as Set is more performant (it'll skip turning the array into Set in-place).
331
331
  */
332
- export function _difference<T>(a1: T[], a2: T[] | Set<T>): T[] {
332
+ export function _difference<T>(a1: readonly T[], a2: readonly T[] | Set<T>): T[] {
333
333
  const a2set = a2 instanceof Set ? a2 : new Set(a2)
334
334
  return a1.filter(v => !a2set.has(v))
335
335
  }
@@ -337,7 +337,7 @@ export function _difference<T>(a1: T[], a2: T[] | Set<T>): T[] {
337
337
  /**
338
338
  * Does NOT mutate the array, returns a filtered array instead.
339
339
  */
340
- export function _arrayRemove<T>(a: T[], itemToRemove: T): T[] {
340
+ export function _arrayRemove<T>(a: readonly T[], itemToRemove: T): T[] {
341
341
  return a.filter(r => r !== itemToRemove)
342
342
  }
343
343
 
@@ -71,7 +71,7 @@ export class TranslationService {
71
71
  * Loads locale(s) (if not already cached) via configured TranslationLoader.
72
72
  * Resolves promise when done (ready to be used).
73
73
  */
74
- async loadLocale(locale: string | string[]): Promise<void> {
74
+ async loadLocale(locale: string | readonly string[]): Promise<void> {
75
75
  const locales = Array.isArray(locale) ? locale : [locale]
76
76
 
77
77
  await pMap(locales, async locale => {
@@ -74,7 +74,7 @@ export class ImageFitter {
74
74
  this.resizeObserver.disconnect()
75
75
  }
76
76
 
77
- private update(entries: ResizeObserverEntry[]): void {
77
+ private update(entries: readonly ResizeObserverEntry[]): void {
78
78
  const width = Math.floor(entries[0]!.contentRect.width)
79
79
  if (width === this.containerWidth) return // we're only interested in width changes
80
80
  this.containerWidth = width
@@ -730,7 +730,7 @@ class LocalDateFactory {
730
730
  /**
731
731
  * Returns the earliest (min) LocalDate from the array, or undefined if the array is empty.
732
732
  */
733
- minOrUndefined(items: LocalDateInputNullable[]): LocalDate | undefined {
733
+ minOrUndefined(items: readonly LocalDateInputNullable[]): LocalDate | undefined {
734
734
  let min: LocalDate | undefined
735
735
  for (const item of items) {
736
736
  if (!item) continue
@@ -746,7 +746,7 @@ class LocalDateFactory {
746
746
  * Returns the earliest LocalDate from the array.
747
747
  * Throws if the array is empty.
748
748
  */
749
- min(items: LocalDateInputNullable[]): LocalDate {
749
+ min(items: readonly LocalDateInputNullable[]): LocalDate {
750
750
  const min = this.minOrUndefined(items)
751
751
  _assert(min, 'localDate.min called on empty array')
752
752
  return min
@@ -755,7 +755,7 @@ class LocalDateFactory {
755
755
  /**
756
756
  * Returns the latest (max) LocalDate from the array, or undefined if the array is empty.
757
757
  */
758
- maxOrUndefined(items: LocalDateInputNullable[]): LocalDate | undefined {
758
+ maxOrUndefined(items: readonly LocalDateInputNullable[]): LocalDate | undefined {
759
759
  let max: LocalDate | undefined
760
760
  for (const item of items) {
761
761
  if (!item) continue
@@ -771,7 +771,7 @@ class LocalDateFactory {
771
771
  * Returns the latest LocalDate from the array.
772
772
  * Throws if the array is empty.
773
773
  */
774
- max(items: LocalDateInputNullable[]): LocalDate {
774
+ max(items: readonly LocalDateInputNullable[]): LocalDate {
775
775
  const max = this.maxOrUndefined(items)
776
776
  _assert(max, 'localDate.max called on empty array')
777
777
  return max
@@ -1024,7 +1024,7 @@ class LocalTimeFactory {
1024
1024
  return opt.mutate ? items.sort(cmp) : items.toSorted(cmp)
1025
1025
  }
1026
1026
 
1027
- minOrUndefined(items: LocalTimeInputNullable[]): LocalTime | undefined {
1027
+ minOrUndefined(items: readonly LocalTimeInputNullable[]): LocalTime | undefined {
1028
1028
  let min: LocalTime | undefined
1029
1029
  for (const item of items) {
1030
1030
  if (!item) continue
@@ -1036,13 +1036,13 @@ class LocalTimeFactory {
1036
1036
  return min
1037
1037
  }
1038
1038
 
1039
- min(items: LocalTimeInputNullable[]): LocalTime {
1039
+ min(items: readonly LocalTimeInputNullable[]): LocalTime {
1040
1040
  const min = this.minOrUndefined(items)
1041
1041
  _assert(min, 'localTime.min called on empty array')
1042
1042
  return min
1043
1043
  }
1044
1044
 
1045
- maxOrUndefined(items: LocalTimeInputNullable[]): LocalTime | undefined {
1045
+ maxOrUndefined(items: readonly LocalTimeInputNullable[]): LocalTime | undefined {
1046
1046
  let max: LocalTime | undefined
1047
1047
  for (const item of items) {
1048
1048
  if (!item) continue
@@ -1054,7 +1054,7 @@ class LocalTimeFactory {
1054
1054
  return max
1055
1055
  }
1056
1056
 
1057
- max(items: LocalTimeInputNullable[]): LocalTime {
1057
+ max(items: readonly LocalTimeInputNullable[]): LocalTime {
1058
1058
  const max = this.maxOrUndefined(items)
1059
1059
  _assert(max, 'localTime.max called on empty array')
1060
1060
  return max
@@ -34,7 +34,7 @@ export function _getTargetMethodSignature(target: AnyObject, keyStr: string): st
34
34
  * returns:
35
35
  * a, b, c
36
36
  */
37
- export function _getArgsSignature(args: any[] = [], logArgs = true): string {
37
+ export function _getArgsSignature(args: readonly any[] = [], logArgs = true): string {
38
38
  if (!logArgs) return ''
39
39
 
40
40
  return args
@@ -14,8 +14,11 @@ export function _Timeout(opt: PTimeoutOptions): MethodDecorator {
14
14
 
15
15
  descriptor.value = async function (this: typeof _target, ...args: any[]) {
16
16
  const ctx = this
17
- opt.name ||= _getMethodSignature(ctx, keyStr)
18
- return await pTimeout(() => originalFn.apply(this, args), opt)
17
+ // Not mutating the shared decorator options, as the name depends on the instance (ctx)
18
+ return await pTimeout(() => originalFn.apply(this, args), {
19
+ ...opt,
20
+ name: opt.name || _getMethodSignature(ctx, keyStr),
21
+ })
19
22
  } as any
20
23
 
21
24
  return descriptor
@@ -94,7 +94,7 @@ export interface ErrorData {
94
94
  /**
95
95
  * Used in e.g http 403 error.
96
96
  */
97
- adminPermissionsRequired?: string[]
97
+ adminPermissionsRequired?: readonly string[]
98
98
 
99
99
  /**
100
100
  * Open-ended.
@@ -848,11 +848,6 @@ export class Fetcher {
848
848
  private normalizeCfg(cfg: FetcherCfg & FetcherOptions): FetcherNormalizedCfg {
849
849
  const { debug = false, logger = consoleLogger } = cfg
850
850
 
851
- if (cfg.baseUrl?.endsWith('/')) {
852
- logger.warn(`Fetcher: baseUrl should not end with slash: ${cfg.baseUrl}`)
853
- cfg.baseUrl = cfg.baseUrl.slice(0, cfg.baseUrl.length - 1)
854
- }
855
-
856
851
  const norm: FetcherNormalizedCfg = _merge(
857
852
  {
858
853
  baseUrl: '',
@@ -894,6 +889,12 @@ export class Fetcher {
894
889
 
895
890
  norm.init.headers = _mapKeys(norm.init.headers, k => k.toLowerCase())
896
891
 
892
+ // Normalizing `norm`, not the input `cfg`, to not mutate the input
893
+ if (norm.baseUrl.endsWith('/')) {
894
+ logger.warn(`Fetcher: baseUrl should not end with slash: ${norm.baseUrl}`)
895
+ norm.baseUrl = norm.baseUrl.slice(0, -1)
896
+ }
897
+
897
898
  return norm
898
899
  }
899
900
 
@@ -8,4 +8,4 @@ export type HttpStatusFamily = 5 | 4 | 3 | 2 | 1
8
8
  */
9
9
  export type HttpStatusCode = number
10
10
 
11
- export const HTTP_METHODS: HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD']
11
+ export const HTTP_METHODS: readonly HttpMethod[] = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD']
@@ -142,7 +142,7 @@ export function createCommonLoggerAtLevel(
142
142
  /**
143
143
  * Creates a "proxy" CommonLogger that pipes log entries to all provided sub-loggers.
144
144
  */
145
- export function commonLoggerPipe(loggers: CommonLogger[]): CommonLogger {
145
+ export function commonLoggerPipe(loggers: readonly CommonLogger[]): CommonLogger {
146
146
  return {
147
147
  debug: (...args) => loggers.forEach(logger => logger.debug(...args)),
148
148
  log: (...args) => loggers.forEach(logger => logger.log(...args)),
@@ -9,7 +9,7 @@ import { _assert } from '../error/assert.js'
9
9
  * _average([1, 2, 3, 4])
10
10
  * // 2.5
11
11
  */
12
- export function _average(values: number[]): number {
12
+ export function _average(values: readonly number[]): number {
13
13
  _assert(values.length, '_average is called on empty array')
14
14
  let total = 0
15
15
  for (const n of values) total += n
@@ -19,7 +19,7 @@ export function _average(values: number[]): number {
19
19
  /**
20
20
  * Same as _average, but safely returns null if input array is empty or nullish.
21
21
  */
22
- export function _averageOrNull(values: number[] | undefined | null): number | null {
22
+ export function _averageOrNull(values: readonly number[] | undefined | null): number | null {
23
23
  return values?.length ? _average(values) : null
24
24
  }
25
25
 
@@ -31,7 +31,7 @@ export function _averageOrNull(values: number[] | undefined | null): number | nu
31
31
  * _stdDev([2, 4, 4, 4, 5, 5, 7, 9])
32
32
  * // 2
33
33
  */
34
- export function _stdDev(values: number[]): number {
34
+ export function _stdDev(values: readonly number[]): number {
35
35
  _assert(values.length, '_stdDev is called on empty array')
36
36
  const avg = _average(values)
37
37
  let sumSquaredDiff = 0
@@ -45,14 +45,14 @@ export function _stdDev(values: number[]): number {
45
45
  /**
46
46
  * Same as _stdDev, but safely returns null if input array is empty or nullish.
47
47
  */
48
- export function _stdDevOrNull(values: number[] | undefined | null): number | null {
48
+ export function _stdDevOrNull(values: readonly number[] | undefined | null): number | null {
49
49
  return values?.length ? _stdDev(values) : null
50
50
  }
51
51
 
52
52
  /**
53
53
  * valuesArray and weightsArray length is expected to be the same.
54
54
  */
55
- export function _averageWeighted(values: number[], weights: number[]): number {
55
+ export function _averageWeighted(values: readonly number[], weights: readonly number[]): number {
56
56
  let numerator = 0
57
57
  let denominator = 0
58
58
  for (let i = 0; i < values.length; i++) {
@@ -74,8 +74,8 @@ export function _averageWeighted(values: number[], weights: number[]): number {
74
74
  * _percentile([1, 2, 3], 100)
75
75
  * // 3
76
76
  */
77
- export function _percentile(values: number[], pc: number): number {
78
- const sorted = values.sort(comparators.numericAsc)
77
+ export function _percentile(values: readonly number[], pc: number): number {
78
+ const sorted = values.toSorted(comparators.numericAsc)
79
79
 
80
80
  // Floating pos in the range of [0; length - 1]
81
81
  const pos = ((values.length - 1) * pc) / 100
@@ -89,10 +89,13 @@ export function _percentile(values: number[], pc: number): number {
89
89
  /**
90
90
  * A tiny bit more efficient function than calling _percentile individually.
91
91
  */
92
- export function _percentiles(values: number[], pcs: number[]): Record<number, number> {
92
+ export function _percentiles(
93
+ values: readonly number[],
94
+ pcs: readonly number[],
95
+ ): Record<number, number> {
93
96
  const r = {} as Record<number, number>
94
97
 
95
- const sorted = values.sort(comparators.numericAsc)
98
+ const sorted = values.toSorted(comparators.numericAsc)
96
99
 
97
100
  for (const pc of pcs) {
98
101
  // Floating pos in the range of [0; length - 1]
@@ -116,7 +119,7 @@ export function _percentiles(values: number[], pcs: number[]): Record<number, nu
116
119
  * _median([1, 2, 3, 4])
117
120
  * // 2.5
118
121
  */
119
- export function _median(values: number[]): number {
122
+ export function _median(values: readonly number[]): number {
120
123
  const sorted = values.toSorted(comparators.numericAsc)
121
124
  const len = sorted.length
122
125
  const mid = Math.floor(len / 2)
@@ -16,10 +16,18 @@ export class Stack<T> {
16
16
  */
17
17
  private nextIndex = 0
18
18
 
19
- readonly items: T[] = []
19
+ private readonly buffer: T[] = []
20
+
21
+ /**
22
+ * Raw "items buffer" as-is (not ordered properly).
23
+ * Readonly, because mutating it (e.g sorting in-place) would corrupt the Stack.
24
+ */
25
+ get items(): readonly T[] {
26
+ return this.buffer
27
+ }
20
28
 
21
29
  push(item: T): this {
22
- this.items[this.nextIndex] = item
30
+ this.buffer[this.nextIndex] = item
23
31
  this.nextIndex = this.nextIndex === this.size - 1 ? 0 : this.nextIndex + 1
24
32
  return this
25
33
  }
@@ -29,7 +37,7 @@ export class Stack<T> {
29
37
  */
30
38
  fill(item: T): this {
31
39
  for (let i = 0; i < this.size; i++) {
32
- this.items[i] = item
40
+ this.buffer[i] = item
33
41
  }
34
42
  return this
35
43
  }
@@ -38,14 +46,14 @@ export class Stack<T> {
38
46
  * Returns last items in the right order.
39
47
  * Unlike raw `items` property that returns "items buffer" as-is (not ordered properly).
40
48
  */
41
- get itemsOrdered(): T[] {
42
- if (this.items.length < this.size) {
49
+ get itemsOrdered(): readonly T[] {
50
+ if (this.buffer.length < this.size) {
43
51
  // Buffer is not filled yet, just return it as-is
44
- return this.items
52
+ return this.buffer
45
53
  }
46
54
 
47
55
  // Buffer was filled and started to "overwrite itself", will need to return 2 slices
48
- return [...this.items.slice(this.nextIndex), ...this.items.slice(0, this.nextIndex)]
56
+ return [...this.buffer.slice(this.nextIndex), ...this.buffer.slice(0, this.nextIndex)]
49
57
  }
50
58
  }
51
59
 
@@ -90,7 +98,7 @@ export class NumberStack extends Stack<number> {
90
98
  return this.items.length === 0 ? null : _percentile(this.items, pc)
91
99
  }
92
100
 
93
- percentiles(pcs: number[]): Record<number, number> {
101
+ percentiles(pcs: readonly number[]): Record<number, number> {
94
102
  return _percentiles(this.items, pcs)
95
103
  }
96
104
  }
@@ -9,7 +9,7 @@ export function _randomInt(minIncl: number, maxIncl: number): number {
9
9
  * Should be used on non-empty arrays! (otherwise will return undefined,
10
10
  * which is not reflected in the output type)
11
11
  */
12
- export function _randomArrayItem<T>(array: T[]): T {
12
+ export function _randomArrayItem<T>(array: readonly T[]): T {
13
13
  return array[_randomInt(0, array.length - 1)]!
14
14
  }
15
15
 
@@ -29,7 +29,7 @@ export class KeySortedMap<K, V> {
29
29
  private readonly map: Map<K, V>
30
30
  readonly #sortedKeys: K[]
31
31
 
32
- constructor(entries: [K, V][] = [], opt: KeySortedMapOptions<K> = {}) {
32
+ constructor(entries: readonly [K, V][] = [], opt: KeySortedMapOptions<K> = {}) {
33
33
  this.#comparator = opt.comparator
34
34
  this.map = new Map(entries)
35
35
  this.#sortedKeys = Array.from(this.map.keys())
@@ -25,7 +25,7 @@ export class LazyKeySortedMap<K, V> {
25
25
  private readonly maybeSortedKeys: K[]
26
26
  private keysAreSorted = false
27
27
 
28
- constructor(entries: [K, V][] = [], opt: LazyKeySortedMapOptions<K> = {}) {
28
+ constructor(entries: readonly [K, V][] = [], opt: LazyKeySortedMapOptions<K> = {}) {
29
29
  this.#comparator = opt.comparator
30
30
  this.map = new Map(entries)
31
31
  this.maybeSortedKeys = Array.from(this.map.keys())
@@ -101,7 +101,11 @@ export function _omitWithUndefined<T extends AnyObject, K extends keyof T>(
101
101
  * 'account.updated',
102
102
  * ])
103
103
  */
104
- export function _mask<T extends AnyObject>(obj: T, props: string[], opt: MutateOptions = {}): T {
104
+ export function _mask<T extends AnyObject>(
105
+ obj: T,
106
+ props: readonly string[],
107
+ opt: MutateOptions = {},
108
+ ): T {
105
109
  const r = opt.mutate ? obj : _deepCopy(obj)
106
110
  for (const k of props) {
107
111
  _unset(r, k)
@@ -5,7 +5,7 @@ import type { AnyObject } from '../types.js'
5
5
  * All keys that are not listed in `keyOrder` go last.
6
6
  * Does not mutate original object.
7
7
  */
8
- export function _sortObject<T extends AnyObject>(obj: T, keyOrder: (keyof T)[]): T {
8
+ export function _sortObject<T extends AnyObject>(obj: T, keyOrder: readonly (keyof T)[]): T {
9
9
  const r = {} as T
10
10
 
11
11
  // First, go over ordered keys
@@ -166,7 +166,7 @@ export async function pMap<IN, OUT>(
166
166
  pMap with serial (non-concurrent) execution.
167
167
  */
168
168
  async function pMap1<IN, OUT>(
169
- items: IN[],
169
+ items: readonly IN[],
170
170
  mapper: AbortableAsyncMapper<IN, OUT>,
171
171
  errorMode: ErrorMode,
172
172
  logger: CommonLogger | null,
@@ -202,7 +202,7 @@ async function pMap1<IN, OUT>(
202
202
  pMap with fully concurrent execution, like Promise.all
203
203
  */
204
204
  async function pMapAll<IN, OUT>(
205
- items: IN[],
205
+ items: readonly IN[],
206
206
  mapper: AbortableAsyncMapper<IN, OUT>,
207
207
  errorMode: ErrorMode,
208
208
  logger: CommonLogger | null,
@@ -47,7 +47,7 @@ export interface PTimeoutOptions {
47
47
  * If the Function rejects - passes this rejection further.
48
48
  */
49
49
  export function pTimeoutFn<T extends AnyAsyncFunction>(fn: T, opt: PTimeoutOptions): T {
50
- opt.name ||= fn.name
50
+ opt = { ...opt, name: opt.name || fn.name } // copy, to not mutate the input options
51
51
 
52
52
  if (!opt.timeout) {
53
53
  return fn
package/src/semver.ts CHANGED
@@ -88,7 +88,7 @@ class SemverFactory {
88
88
  /**
89
89
  * Returns the highest (max) Semver from the array, or undefined if the array is empty.
90
90
  */
91
- maxOrUndefined(items: SemverInputNullable[]): Semver | undefined {
91
+ maxOrUndefined(items: readonly SemverInputNullable[]): Semver | undefined {
92
92
  let max: Semver | undefined
93
93
  for (const item of items) {
94
94
  const input = this.fromInputOrUndefined(item)
@@ -103,7 +103,7 @@ class SemverFactory {
103
103
  * Returns the highest Semver from the array.
104
104
  * Throws if the array is empty.
105
105
  */
106
- max(items: SemverInputNullable[]): Semver {
106
+ max(items: readonly SemverInputNullable[]): Semver {
107
107
  const max = this.maxOrUndefined(items)
108
108
  _assert(max, 'semver.max called on empty array')
109
109
  return max
@@ -112,7 +112,7 @@ class SemverFactory {
112
112
  /**
113
113
  * Returns the lowest (min) Semver from the array, or undefined if the array is empty.
114
114
  */
115
- minOrUndefined(items: SemverInputNullable[]): Semver | undefined {
115
+ minOrUndefined(items: readonly SemverInputNullable[]): Semver | undefined {
116
116
  let min: Semver | undefined
117
117
  for (const item of items) {
118
118
  const input = this.fromInputOrUndefined(item)
@@ -127,7 +127,7 @@ class SemverFactory {
127
127
  * Returns the lowest Semver from the array.
128
128
  * Throws if the array is empty.
129
129
  */
130
- min(items: SemverInputNullable[]): Semver {
130
+ min(items: readonly SemverInputNullable[]): Semver {
131
131
  const min = this.minOrUndefined(items)
132
132
  _assert(min, 'semver.min called on empty array')
133
133
  return min
@@ -36,7 +36,11 @@ export interface PupaOptions {
36
36
  /**
37
37
  * API: https://github.com/sindresorhus/pupa
38
38
  */
39
- export function pupa(template: string, data: any[] | AnyObject, opt: PupaOptions = {}): string {
39
+ export function pupa(
40
+ template: string,
41
+ data: readonly any[] | AnyObject,
42
+ opt: PupaOptions = {},
43
+ ): string {
40
44
  if (typeof template !== 'string') {
41
45
  throw new TypeError(`Expected a \`string\` in the first argument, got \`${typeof template}\``)
42
46
  }
@@ -41,7 +41,7 @@ export interface ReadingTimeResult extends ReadingTimeStats {
41
41
 
42
42
  type WordBoundFunction = (char: string) => boolean
43
43
 
44
- function codeIsInRanges(num: number, arrayOfRanges: number[][]): boolean {
44
+ function codeIsInRanges(num: number, arrayOfRanges: readonly number[][]): boolean {
45
45
  return arrayOfRanges.some(([lowerBound, upperBound]) => lowerBound! <= num && num <= upperBound!)
46
46
  }
47
47
 
package/src/ua/ua.ts CHANGED
@@ -379,7 +379,7 @@ function mergeMapEntries(
379
379
  function rgxMapper(
380
380
  target: UABrowser | UACpu | UADevice | UAEngine | UAOS,
381
381
  ua: string,
382
- arrays: UARegexMapEntry[],
382
+ arrays: readonly UARegexMapEntry[],
383
383
  ): void {
384
384
  const t = target as unknown as Record<string, string | undefined>
385
385
  let matches: RegExpExecArray | null = null