@rsdoctor/utils 1.5.11 → 1.5.12

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,58 +1,98 @@
1
- interface FileSizeOptionsBase {
2
- base?: 10 | 2;
3
- bits?: boolean;
4
- exponent?: number;
5
- fullform?: boolean;
6
- fullforms?: string[];
7
- locale?: string | boolean;
8
- localeOptions?: Intl.DateTimeFormatOptions;
9
- pad?: boolean;
10
- precision?: number;
11
- round?: number;
12
- roundingMethod?: 'round' | 'floor' | 'ceil';
13
- separator?: string;
14
- spacer?: string;
15
- standard?: 'si' | 'iec' | 'jedec';
16
- symbols?: {};
17
- }
18
-
19
- interface FileSizeOptionsArray extends FileSizeOptionsBase {
20
- output: 'array'
21
- }
22
-
23
- interface FileSizeOptionsExponent extends FileSizeOptionsBase {
24
- output: 'exponent'
25
- }
26
-
27
- interface FileSizeOptionsObject extends FileSizeOptionsBase {
28
- output: 'object'
29
- }
30
-
31
- interface FileSizeOptionsString extends FileSizeOptionsBase {
32
- output: 'string'
33
- }
34
-
35
- interface FileSizeReturnObject {
36
- value: string,
37
- symbol: string,
38
- exponent: number,
39
- unit: string,
40
- }
41
-
42
- type FileSizeReturnArray = [ number, string ]
43
-
44
- type FileSizeOptionStringOrBase = FileSizeOptionsString | FileSizeOptionsBase;
45
- type FileSizeOptions = FileSizeOptionsArray | FileSizeOptionsExponent | FileSizeOptionsObject | FileSizeOptionStringOrBase | undefined
46
- type FileSizeReturnType<Options extends FileSizeOptions> =
47
- Options extends FileSizeOptionsArray
48
- ? FileSizeReturnArray
49
- : Options extends FileSizeOptionsExponent
50
- ? number
51
- : Options extends FileSizeOptionsObject
52
- ? FileSizeReturnObject
53
- : string;
54
-
55
- declare function filesize<Options extends FileSizeOptions = undefined>(byteCount: number | string | bigint, options?: Options): FileSizeReturnType<Options>
56
- declare function partial<Options extends FileSizeOptions = undefined>(options?: Options): (byteCount: number | string | bigint) => FileSizeReturnType<Options>
1
+ /**
2
+ * Options interface for configuring filesize behavior
3
+ */
4
+ interface FilesizeOptions {
5
+ /** If true, calculates bits instead of bytes */
6
+ bits?: boolean;
7
+ /** If true, pads decimal places to match round parameter */
8
+ pad?: boolean;
9
+ /** Number base (2 for binary, 10 for decimal, -1 for auto) */
10
+ base?: number;
11
+ /** Number of decimal places to round to */
12
+ round?: number;
13
+ /** Locale for number formatting, true for system locale */
14
+ locale?: string | boolean;
15
+ /** Additional options for locale formatting */
16
+ localeOptions?: Intl.NumberFormatOptions;
17
+ /** Custom decimal separator */
18
+ separator?: string;
19
+ /** String to separate value and unit */
20
+ spacer?: string;
21
+ /** Custom unit symbols */
22
+ symbols?: Record<string, string>;
23
+ /** Unit standard to use (SI, IEC, JEDEC) */
24
+ standard?: "si" | "iec" | "jedec" | "";
25
+ /** Output format: "string", "array", "object", or "exponent" */
26
+ output?: "string" | "array" | "object" | "exponent";
27
+ /** If true, uses full unit names instead of abbreviations */
28
+ fullform?: boolean;
29
+ /** Custom full unit names */
30
+ fullforms?: string[];
31
+ /** Force specific exponent (-1 for auto) */
32
+ exponent?: number;
33
+ /** Math rounding method to use */
34
+ roundingMethod?: "round" | "floor" | "ceil";
35
+ /** Number of significant digits (0 for auto) */
36
+ precision?: number;
37
+ }
38
+
39
+ /**
40
+ * Object format return type when output is "object"
41
+ */
42
+ interface FilesizeObject {
43
+ /** The numeric value */
44
+ value: number | string;
45
+ /** The unit symbol */
46
+ symbol: string;
47
+ /** The exponent used in calculation */
48
+ exponent: number;
49
+ /** The original unit before symbol customization */
50
+ unit: string;
51
+ }
52
+
53
+ /**
54
+ * Array format return type when output is "array"
55
+ */
56
+ type FilesizeArray = [number | string, string];
57
+
58
+ /**
59
+ * Return type based on output option
60
+ */
61
+ type FilesizeReturn<T extends FilesizeOptions = {}> =
62
+ T['output'] extends "object" ? FilesizeObject :
63
+ T['output'] extends "array" ? FilesizeArray :
64
+ T['output'] extends "exponent" ? number :
65
+ string;
66
+
67
+ /**
68
+ * Converts a file size in bytes to a human-readable string with appropriate units
69
+ * @param arg - The file size in bytes to convert
70
+ * @param options - Configuration options for formatting
71
+ * @returns Formatted file size based on output option
72
+ * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid
73
+ * @example
74
+ * filesize(1024) // "1.02 kB"
75
+ * filesize(1024, {bits: true}) // "8.19 kbit"
76
+ * filesize(1024, {output: "object"}) // {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"}
77
+ */
78
+ declare function filesize<T extends FilesizeOptions = {}>(
79
+ arg: number | string | bigint,
80
+ options?: T
81
+ ): FilesizeReturn<T>;
82
+
83
+ /**
84
+ * Creates a partially applied version of filesize with preset options
85
+ * @param options - Default options to apply to the returned function
86
+ * @returns A function that takes a file size and returns formatted output
87
+ * @example
88
+ * const formatBytes = partial({round: 1, standard: "iec"});
89
+ * formatBytes(1024) // "1 KiB"
90
+ * formatBytes(2048) // "2 KiB"
91
+ * formatBytes(1536) // "1.5 KiB"
92
+ */
93
+ declare function partial<T extends FilesizeOptions = {}>(
94
+ options?: T
95
+ ): (arg: number | string | bigint) => FilesizeReturn<T>;
57
96
 
58
97
  export { filesize, partial };
98
+ export type { FilesizeArray, FilesizeObject, FilesizeOptions, FilesizeReturn };
@@ -12,237 +12,747 @@ var exports = __webpack_exports__;
12
12
  /**
13
13
  * filesize
14
14
  *
15
- * @copyright 2024 Jason Mulligan <jason.mulligan@avoidwork.com>
15
+ * @copyright 2026 Jason Mulligan <jason.mulligan@avoidwork.com>
16
16
  * @license BSD-3-Clause
17
- * @version 10.1.6
17
+ * @version 11.0.17
18
18
  */
19
19
 
20
20
 
21
- const ARRAY = "array";
22
- const BIT = "bit";
23
- const BITS = "bits";
24
- const BYTE = "byte";
25
- const BYTES = "bytes";
26
- const EMPTY = "";
27
- const EXPONENT = "exponent";
28
- const FUNCTION = "function";
29
- const IEC = "iec";
30
- const INVALID_NUMBER = "Invalid number";
31
- const INVALID_ROUND = "Invalid rounding method";
32
- const JEDEC = "jedec";
33
- const OBJECT = "object";
34
- const PERIOD = ".";
35
- const ROUND = "round";
36
- const S = "s";
37
- const SI = "si";
38
- const SI_KBIT = "kbit";
39
- const SI_KBYTE = "kB";
40
- const SPACE = " ";
41
- const STRING = "string";
42
- const ZERO = "0";
43
- const STRINGS = {
44
- symbol: {
45
- iec: {
46
- bits: ["bit", "Kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"],
47
- bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]
48
- },
49
- jedec: {
50
- bits: ["bit", "Kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"],
51
- bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]
52
- }
53
- },
54
- fullform: {
55
- iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"],
56
- jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"]
57
- }
21
+ // Error Messages
22
+ const INVALID_NUMBER = "Invalid number";
23
+ const INVALID_ROUND = "Invalid rounding method";
24
+
25
+ // Standard Types
26
+ const IEC = "iec";
27
+ const JEDEC = "jedec";
28
+ const SI = "si";
29
+
30
+ // Unit Types
31
+ const BIT = "bit";
32
+ const BITS = "bits";
33
+ const BYTE = "byte";
34
+ const BYTES = "bytes";
35
+ const SI_KBIT = "kbit";
36
+ const SI_KBYTE = "kB";
37
+
38
+ // Output Format Types
39
+ const ARRAY = "array";
40
+ const FUNCTION = "function";
41
+ const OBJECT = "object";
42
+ const STRING = "string";
43
+
44
+ // Processing Constants
45
+ const EXPONENT = "exponent";
46
+ const ROUND = "round";
47
+
48
+ // Special Characters and Values
49
+ const E = "e";
50
+ const EMPTY = "";
51
+ const PERIOD = ".";
52
+ const S = "s";
53
+ const SPACE = " ";
54
+ const ZERO = "0";
55
+
56
+ // Data Structures
57
+ const STRINGS = {
58
+ symbol: {
59
+ iec: {
60
+ bits: ["bit", "Kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"],
61
+ bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"],
62
+ },
63
+ jedec: {
64
+ bits: ["bit", "Kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"],
65
+ bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
66
+ },
67
+ },
68
+ fullform: {
69
+ iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"],
70
+ jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"],
71
+ },
58
72
  };
59
73
 
60
- function filesize (arg, {
61
- bits = false,
62
- pad = false,
63
- base = -1,
64
- round = 2,
65
- locale = EMPTY,
66
- localeOptions = {},
67
- separator = EMPTY,
68
- spacer = SPACE,
69
- symbols = {},
70
- standard = EMPTY,
71
- output = STRING,
72
- fullform = false,
73
- fullforms = [],
74
- exponent = -1,
75
- roundingMethod = ROUND,
76
- precision = 0
77
- } = {}) {
78
- let e = exponent,
79
- num = Number(arg),
80
- result = [],
81
- val = 0,
82
- u = EMPTY;
83
-
84
- // Sync base & standard
85
- if (standard === SI) {
86
- base = 10;
87
- standard = JEDEC;
88
- } else if (standard === IEC || standard === JEDEC) {
89
- base = 2;
90
- } else if (base === 2) {
91
- standard = IEC;
92
- } else {
93
- base = 10;
94
- standard = JEDEC;
95
- }
96
-
97
- const ceil = base === 10 ? 1000 : 1024,
98
- full = fullform === true,
99
- neg = num < 0,
100
- roundingFunc = Math[roundingMethod];
101
-
102
- if (typeof arg !== "bigint" && isNaN(arg)) {
103
- throw new TypeError(INVALID_NUMBER);
104
- }
105
-
106
- if (typeof roundingFunc !== FUNCTION) {
107
- throw new TypeError(INVALID_ROUND);
108
- }
109
-
110
- // Flipping a negative number to determine the size
111
- if (neg) {
112
- num = -num;
113
- }
114
-
115
- // Determining the exponent
116
- if (e === -1 || isNaN(e)) {
117
- e = Math.floor(Math.log(num) / Math.log(ceil));
118
-
119
- if (e < 0) {
120
- e = 0;
121
- }
122
- }
123
-
124
- // Exceeding supported length, time to reduce & multiply
125
- if (e > 8) {
126
- if (precision > 0) {
127
- precision += 8 - e;
128
- }
129
-
130
- e = 8;
131
- }
132
-
133
- if (output === EXPONENT) {
134
- return e;
135
- }
136
-
137
- // Zero is now a special case because bytes divide by 1
138
- if (num === 0) {
139
- result[0] = 0;
140
- u = result[1] = STRINGS.symbol[standard][bits ? BITS : BYTES][e];
141
- } else {
142
- val = num / (base === 2 ? Math.pow(2, e * 10) : Math.pow(1000, e));
143
-
144
- if (bits) {
145
- val = val * 8;
146
-
147
- if (val >= ceil && e < 8) {
148
- val = val / ceil;
149
- e++;
150
- }
151
- }
152
-
153
- const p = Math.pow(10, e > 0 ? round : 0);
154
- result[0] = roundingFunc(val * p) / p;
155
-
156
- if (result[0] === ceil && e < 8 && exponent === -1) {
157
- result[0] = 1;
158
- e++;
159
- }
160
-
161
- u = result[1] = base === 10 && e === 1 ? bits ? SI_KBIT : SI_KBYTE : STRINGS.symbol[standard][bits ? BITS : BYTES][e];
162
- }
163
-
164
- // Decorating a 'diff'
165
- if (neg) {
166
- result[0] = -result[0];
167
- }
168
-
169
- // Setting optional precision
170
- if (precision > 0) {
171
- result[0] = result[0].toPrecision(precision);
172
- }
173
-
174
- // Applying custom symbol
175
- result[1] = symbols[result[1]] || result[1];
176
-
177
- if (locale === true) {
178
- result[0] = result[0].toLocaleString();
179
- } else if (locale.length > 0) {
180
- result[0] = result[0].toLocaleString(locale, localeOptions);
181
- } else if (separator.length > 0) {
182
- result[0] = result[0].toString().replace(PERIOD, separator);
183
- }
184
-
185
- if (pad && round > 0) {
186
- const i = result[0].toString(),
187
- x = separator || ((i.match(/(\D)/g) || []).pop() || PERIOD),
188
- tmp = i.toString().split(x),
189
- s = tmp[1] || EMPTY,
190
- l = s.length,
191
- n = round - l;
192
-
193
- result[0] = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;
194
- }
195
-
196
- if (full) {
197
- result[1] = fullforms[e] ? fullforms[e] : STRINGS.fullform[standard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S);
198
- }
199
-
200
- // Returning Array, Object, or String (default)
201
- return output === ARRAY ? result : output === OBJECT ? {
202
- value: result[0],
203
- symbol: result[1],
204
- exponent: e,
205
- unit: u
206
- } : result.join(spacer);
207
- }
208
-
209
- // Partial application for functional programming
210
- function partial ({
211
- bits = false,
212
- pad = false,
213
- base = -1,
214
- round = 2,
215
- locale = EMPTY,
216
- localeOptions = {},
217
- separator = EMPTY,
218
- spacer = SPACE,
219
- symbols = {},
220
- standard = EMPTY,
221
- output = STRING,
222
- fullform = false,
223
- fullforms = [],
224
- exponent = -1,
225
- roundingMethod = ROUND,
226
- precision = 0
227
- } = {}) {
228
- return arg => filesize(arg, {
229
- bits,
230
- pad,
231
- base,
232
- round,
233
- locale,
234
- localeOptions,
235
- separator,
236
- spacer,
237
- symbols,
238
- standard,
239
- output,
240
- fullform,
241
- fullforms,
242
- exponent,
243
- roundingMethod,
244
- precision
245
- });
74
+ // Pre-computed lookup tables for performance optimization
75
+ const BINARY_POWERS = [
76
+ 1, // 2^0
77
+ 1024, // 2^10
78
+ 1048576, // 2^20
79
+ 1073741824, // 2^30
80
+ 1099511627776, // 2^40
81
+ 1125899906842624, // 2^50
82
+ 1152921504606846976, // 2^60
83
+ 1180591620717411303424, // 2^70
84
+ 1208925819614629174706176, // 2^80
85
+ ];
86
+
87
+ const DECIMAL_POWERS = [
88
+ 1, // 10^0
89
+ 1000, // 10^3
90
+ 1000000, // 10^6
91
+ 1000000000, // 10^9
92
+ 1000000000000, // 10^12
93
+ 1000000000000000, // 10^15
94
+ 1000000000000000000, // 10^18
95
+ 1000000000000000000000, // 10^21
96
+ 1000000000000000000000000, // 10^24
97
+ ];
98
+
99
+ // Pre-computed log values for faster exponent calculation
100
+ const LOG_2_1024 = Math.log(1024);
101
+ const LOG_10_1000 = Math.log(1000);
102
+
103
+ // Cached configuration lookup for better performance
104
+ const STANDARD_CONFIGS = {
105
+ [SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC },
106
+ [IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC },
107
+ [JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC },
108
+ };
109
+
110
+ /**
111
+ * Optimized base configuration lookup
112
+ * @param {string} standard - Standard type
113
+ * @param {number} base - Base number
114
+ * @returns {Object} Configuration object
115
+ */
116
+ function getBaseConfiguration(standard, base) {
117
+ // Use cached lookup table for better performance
118
+ if (STANDARD_CONFIGS[standard]) {
119
+ return STANDARD_CONFIGS[standard];
120
+ }
121
+
122
+ // Base override
123
+ if (base === 2) {
124
+ return { isDecimal: false, ceil: 1024, actualStandard: IEC };
125
+ }
126
+
127
+ // Default
128
+ return { isDecimal: true, ceil: 1000, actualStandard: JEDEC };
129
+ }
130
+
131
+ /**
132
+ * Optimized zero value handling
133
+ * @param {number} precision - Precision value
134
+ * @param {string} actualStandard - Standard to use
135
+ * @param {boolean} bits - Whether to use bits
136
+ * @param {Object} symbols - Custom symbols
137
+ * @param {boolean} full - Whether to use full form
138
+ * @param {Array} fullforms - Custom full forms
139
+ * @param {string} output - Output format
140
+ * @param {string} spacer - Spacer character
141
+ * @param {string} [symbol] - Symbol to use (defaults based on bits/standard)
142
+ * @returns {string|Array|Object|number} Formatted result
143
+ */
144
+ function handleZeroValue(
145
+ precision,
146
+ actualStandard,
147
+ bits,
148
+ symbols,
149
+ full,
150
+ fullforms,
151
+ output,
152
+ spacer,
153
+ symbol,
154
+ ) {
155
+ let value;
156
+ if (precision > 0) {
157
+ value = (0).toPrecision(precision);
158
+ } else {
159
+ value = 0;
160
+ }
161
+
162
+ if (output === EXPONENT) {
163
+ return 0;
164
+ }
165
+
166
+ // Set default symbol if not provided
167
+ if (!symbol) {
168
+ symbol = bits
169
+ ? STRINGS.symbol[actualStandard].bits[0]
170
+ : STRINGS.symbol[actualStandard].bytes[0];
171
+ }
172
+
173
+ // Apply symbol customization
174
+ if (symbols[symbol]) {
175
+ symbol = symbols[symbol];
176
+ }
177
+
178
+ // Apply full form
179
+ if (full) {
180
+ if (fullforms[0]) {
181
+ symbol = fullforms[0];
182
+ } else {
183
+ symbol = STRINGS.fullform[actualStandard][0];
184
+ if (bits) {
185
+ symbol += BIT;
186
+ } else {
187
+ symbol += BYTE;
188
+ }
189
+ }
190
+ }
191
+
192
+ // Return in requested format
193
+ if (output === ARRAY) {
194
+ return [value, symbol];
195
+ }
196
+
197
+ if (output === OBJECT) {
198
+ return { value, symbol, exponent: 0, unit: symbol };
199
+ }
200
+
201
+ return value + spacer + symbol;
202
+ }
203
+
204
+ /**
205
+ * Optimized value calculation with bits handling
206
+ * @param {number} num - Input number
207
+ * @param {number} e - Exponent
208
+ * @param {boolean} isDecimal - Whether to use decimal powers
209
+ * @param {boolean} bits - Whether to calculate bits
210
+ * @param {number} ceil - Ceiling value for auto-increment
211
+ * @param {boolean} autoExponent - Whether exponent is auto (-1 or NaN)
212
+ * @returns {Object} Object with result and e properties
213
+ */
214
+ function calculateOptimizedValue(num, e, isDecimal, bits, ceil, autoExponent = true) {
215
+ let d;
216
+ if (isDecimal) {
217
+ d = DECIMAL_POWERS[e];
218
+ } else {
219
+ d = BINARY_POWERS[e];
220
+ }
221
+ let result = num / d;
222
+
223
+ if (bits) {
224
+ result *= 8;
225
+ // Handle auto-increment for bits (only when exponent is auto)
226
+ if (autoExponent && result >= ceil && e < 8) {
227
+ result /= ceil;
228
+ e++;
229
+ }
230
+ }
231
+
232
+ return { result, e };
233
+ }
234
+
235
+ /**
236
+ * Optimized precision handling with scientific notation correction
237
+ * @param {number} value - Current value
238
+ * @param {number} precision - Precision to apply
239
+ * @param {number} e - Current exponent
240
+ * @param {number} num - Original number
241
+ * @param {boolean} isDecimal - Whether using decimal base
242
+ * @param {boolean} bits - Whether calculating bits
243
+ * @param {number} ceil - Ceiling value
244
+ * @param {Function} roundingFunc - Rounding function
245
+ * @param {number} round - Round value
246
+ * @param {number} exponent - Forced exponent (-1 for auto)
247
+ * @returns {Object} Object with value and e properties
248
+ */
249
+ function applyPrecisionHandling(
250
+ value,
251
+ precision,
252
+ e,
253
+ num,
254
+ isDecimal,
255
+ bits,
256
+ ceil,
257
+ roundingFunc,
258
+ round,
259
+ exponent,
260
+ ) {
261
+ if (typeof value === "string") {
262
+ value = parseFloat(value);
263
+ }
264
+
265
+ let result = value.toPrecision(precision);
266
+
267
+ const autoExponent = exponent === -1 || isNaN(exponent);
268
+
269
+ // Handle scientific notation by recalculating with incremented exponent
270
+ if (result.includes(E) && e < 8 && autoExponent) {
271
+ e++;
272
+ const { result: valueResult } = calculateOptimizedValue(num, e, isDecimal, bits, ceil);
273
+ let p;
274
+ if (round > 0) {
275
+ p = Math.pow(10, round);
276
+ } else {
277
+ p = 1;
278
+ }
279
+ let computed;
280
+ if (p === 1) {
281
+ computed = roundingFunc(valueResult);
282
+ } else {
283
+ computed = roundingFunc(valueResult * p) / p;
284
+ }
285
+ result = computed.toPrecision(precision);
286
+ }
287
+
288
+ return { value: result, e };
289
+ }
290
+
291
+ /**
292
+ * Optimized number formatting with locale, separator, and padding
293
+ * @param {number|string} value - Value to format
294
+ * @param {string|boolean} locale - Locale setting
295
+ * @param {Object} localeOptions - Locale options
296
+ * @param {string} separator - Custom separator
297
+ * @param {boolean} pad - Whether to pad
298
+ * @param {number} round - Round value
299
+ * @returns {string|number} Formatted value
300
+ */
301
+ function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) {
302
+ let result = value;
303
+
304
+ // Apply locale formatting
305
+ if (locale === true) {
306
+ result = result.toLocaleString();
307
+ } else if (locale.length > 0) {
308
+ result = result.toLocaleString(locale, localeOptions);
309
+ } else if (separator.length > 0) {
310
+ result = result.toString().replace(PERIOD, separator);
311
+ }
312
+
313
+ // Apply padding
314
+ if (pad && round > 0) {
315
+ const resultStr = result.toString();
316
+ const x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD;
317
+ const tmp = resultStr.split(x);
318
+ const s = tmp[1] || EMPTY;
319
+
320
+ const l = s.length;
321
+ const n = round - l;
322
+
323
+ result = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;
324
+ }
325
+
326
+ return result;
327
+ }
328
+
329
+ /**
330
+ * Calculates exponent from the input value using pre-computed log values and clamps to supported range
331
+ * Also adjusts precision when exponent exceeds the lookup table bounds
332
+ * @param {number} num - Input file size in bytes
333
+ * @param {number} e - Current exponent value
334
+ * @param {number} exponent - Original user-provided exponent option (-1 for auto)
335
+ * @param {boolean} isDecimal - Whether to use decimal (SI) base
336
+ * @param {number} precision - Current precision value (modified when e > 8)
337
+ * @returns {Object} Object with computed e value and possibly adjusted precision
338
+ */
339
+ function calculateExponent(num, e, exponent, isDecimal, precision) {
340
+ if (e === -1 || isNaN(e)) {
341
+ if (isDecimal) {
342
+ e = Math.floor(Math.log(num) / LOG_10_1000);
343
+ } else {
344
+ e = Math.floor(Math.log(num) / LOG_2_1024);
345
+ }
346
+ if (e < 0) {
347
+ e = 0;
348
+ }
349
+ }
350
+
351
+ if (e > 8) {
352
+ if (precision > 0) {
353
+ precision += 8 - e;
354
+ }
355
+ return { e: 8, precision };
356
+ }
357
+
358
+ return { e, precision };
359
+ }
360
+
361
+ /**
362
+ * Applies rounding to the raw calculated value and handles auto-increment ceiling
363
+ * @param {number} val - Raw value before rounding
364
+ * @param {number} ceil - Ceiling threshold (1000 for SI, 1024 for IEC)
365
+ * @param {number} e - Current exponent value
366
+ * @param {number} round - Number of decimal places
367
+ * @param {Function} roundingFunc - Rounding method (Math.round, Math.floor, Math.ceil)
368
+ * @param {boolean} autoExponent - Whether exponent is auto-calculated (-1 or NaN)
369
+ * @returns {Object} Object with rounded value and possibly incremented exponent
370
+ */
371
+ function applyRounding(val, ceil, e, round, roundingFunc, autoExponent) {
372
+ let p;
373
+ if (e > 0 && round > 0) {
374
+ p = Math.pow(10, round);
375
+ } else {
376
+ p = 1;
377
+ }
378
+ let r;
379
+ if (p === 1) {
380
+ r = roundingFunc(val);
381
+ } else {
382
+ r = roundingFunc(val * p) / p;
383
+ }
384
+
385
+ if (r === ceil && e < 8 && autoExponent) {
386
+ r = 1;
387
+ e++;
388
+ }
389
+
390
+ return { value: r, e };
391
+ }
392
+
393
+ /**
394
+ * Resolves the unit symbol for the given standard, bits mode, and exponent
395
+ * Handles SI standard special case where exponent 1 always uses "kB" or "kbit"
396
+ * @param {string} actualStandard - The resolved standard (iec, jedec)
397
+ * @param {boolean} bits - Whether formatting bit values
398
+ * @param {number} e - Current exponent index
399
+ * @param {boolean} isDecimal - Whether using decimal (SI) base
400
+ * @returns {string} The resolved unit symbol string
401
+ */
402
+ function resolveSymbol(actualStandard, bits, e, isDecimal) {
403
+ const symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES];
404
+ let result;
405
+ if (isDecimal && e === 1) {
406
+ if (bits) {
407
+ result = SI_KBIT;
408
+ } else {
409
+ result = SI_KBYTE;
410
+ }
411
+ } else {
412
+ result = symbolTable[e];
413
+ }
414
+ return result;
415
+ }
416
+
417
+ /**
418
+ * Decorates the result: applies negation, custom symbols, number formatting, and full form names
419
+ * Mutates the result array in-place for both value (index 0) and symbol (index 1)
420
+ * @param {Array} result - Result array with numeric value at [0] and string symbol at [1]
421
+ * @param {boolean} neg - Whether the original input was negative
422
+ * @param {Object} symbols - Custom symbol override map
423
+ * @param {string|boolean} locale - Locale string for formatting
424
+ * @param {Object} localeOptions - Additional locale formatting options
425
+ * @param {string} separator - Custom decimal separator
426
+ * @param {boolean} pad - Whether zero-pad decimals
427
+ * @param {number} round - Target decimal count for padding
428
+ * @param {boolean} full - Whether to use full unit names
429
+ * @param {Array} fullforms - Custom full unit name overrides
430
+ * @param {string} actualStandard - Unit standard for full form lookup
431
+ * @param {number} e - Current exponent index
432
+ * @param {boolean} bits - Whether formatting bit values
433
+ * @returns {void} Mutates result array in place
434
+ */
435
+ function decorateResult(
436
+ result,
437
+ neg,
438
+ symbols,
439
+ locale,
440
+ localeOptions,
441
+ separator,
442
+ pad,
443
+ round,
444
+ full,
445
+ fullforms,
446
+ actualStandard,
447
+ e,
448
+ bits,
449
+ ) {
450
+ if (neg) {
451
+ result[0] = -result[0];
452
+ }
453
+
454
+ if (symbols[result[1]]) {
455
+ result[1] = symbols[result[1]];
456
+ }
457
+
458
+ result[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round);
459
+
460
+ if (full) {
461
+ let unit;
462
+ if (bits) {
463
+ unit = BIT;
464
+ } else {
465
+ unit = BYTE;
466
+ }
467
+ let val;
468
+ if (typeof result[0] === "string") {
469
+ val = parseFloat(result[0]);
470
+ } else {
471
+ val = result[0];
472
+ }
473
+ // Determine singular/plural suffix
474
+ let suffix;
475
+ if (val === 1) {
476
+ suffix = EMPTY;
477
+ } else {
478
+ suffix = S;
479
+ }
480
+ // Determine symbol — custom fullforms are the complete name, defaults get unit+suffix
481
+ if (fullforms[e]) {
482
+ result[1] = fullforms[e];
483
+ } else {
484
+ result[1] = STRINGS.fullform[actualStandard][e] + unit + suffix;
485
+ }
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Formats the computed result array into the requested output type
491
+ * @param {Array} result - Result array with formatted value at [0] and symbol at [1]
492
+ * @param {number} e - Current exponent
493
+ * @param {string} u - Original resolved symbol (before custom override)
494
+ * @param {string} output - Output type (ARRAY, OBJECT, STRING)
495
+ * @param {string} spacer - String separator between value and unit
496
+ * @returns {string|Array|Object|number} Formatted result in requested type
497
+ */
498
+ function formatOutput(result, e, u, output, spacer) {
499
+ if (output === ARRAY) {
500
+ return result;
501
+ }
502
+
503
+ if (output === OBJECT) {
504
+ return {
505
+ value: result[0],
506
+ symbol: result[1],
507
+ exponent: e,
508
+ unit: u,
509
+ };
510
+ }
511
+
512
+ let formatted;
513
+ if (spacer === SPACE) {
514
+ formatted = `${result[0]} ${result[1]}`;
515
+ } else {
516
+ formatted = result.join(spacer);
517
+ }
518
+ return formatted;
519
+ }
520
+
521
+ /**
522
+ * Converts a file size in bytes to a human-readable string with appropriate units
523
+ * @param {number|string|bigint} arg - The file size in bytes to convert
524
+ * @param {Object} [options={}] - Configuration options for formatting
525
+ * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes
526
+ * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter
527
+ * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)
528
+ * @param {number} [options.round=2] - Number of decimal places to round to
529
+ * @param {string|boolean} [options.locale=""] - Locale for number formatting, true for system locale
530
+ * @param {Object} [options.localeOptions={}] - Additional options for locale formatting
531
+ * @param {string} [options.separator=""] - Custom decimal separator
532
+ * @param {string} [options.spacer=" "] - String to separate value and unit
533
+ * @param {Object} [options.symbols={}] - Custom unit symbols
534
+ * @param {string} [options.standard=""] - Unit standard to use (SI, IEC, JEDEC)
535
+ * @param {string} [options.output="string"] - Output format: "string", "array", "object", or "exponent"
536
+ * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations
537
+ * @param {Array} [options.fullforms=[]] - Custom full unit names
538
+ * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)
539
+ * @param {string} [options.roundingMethod="round"] - Math rounding method to use
540
+ * @param {number} [options.precision=0] - Number of significant digits (0 for auto)
541
+ * @returns {string|Array|Object|number} Formatted file size based on output option
542
+ * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid
543
+ * @example
544
+ * filesize(1024) // "1.02 kB"
545
+ * filesize(1024, {bits: true}) // "8.19 kbit"
546
+ * filesize(1024, {output: "object"}) // {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"}
547
+ */
548
+ function filesize(
549
+ arg,
550
+ {
551
+ bits = false,
552
+ pad = false,
553
+ base = -1,
554
+ round = 2,
555
+ locale = EMPTY,
556
+ localeOptions = {},
557
+ separator = EMPTY,
558
+ spacer = SPACE,
559
+ symbols = {},
560
+ standard = EMPTY,
561
+ output = STRING,
562
+ fullform = false,
563
+ fullforms = [],
564
+ exponent = -1,
565
+ roundingMethod = ROUND,
566
+ precision = 0,
567
+ } = {},
568
+ ) {
569
+ let e = exponent,
570
+ num,
571
+ result = [],
572
+ val = 0,
573
+ u = EMPTY;
574
+
575
+ if (typeof arg === "bigint") {
576
+ num = Number(arg);
577
+ } else {
578
+ num = Number(arg);
579
+
580
+ if (isNaN(arg)) {
581
+ throw new TypeError(INVALID_NUMBER);
582
+ }
583
+
584
+ if (!isFinite(num)) {
585
+ throw new TypeError(INVALID_NUMBER);
586
+ }
587
+ }
588
+
589
+ const { isDecimal, ceil, actualStandard } = getBaseConfiguration(standard, base);
590
+
591
+ const full = fullform === true,
592
+ neg = num < 0,
593
+ roundingFunc = Math[roundingMethod];
594
+
595
+ if (typeof roundingFunc !== FUNCTION) {
596
+ throw new TypeError(INVALID_ROUND);
597
+ }
598
+
599
+ if (neg) {
600
+ num = -num;
601
+ }
602
+
603
+ if (num === 0) {
604
+ return handleZeroValue(
605
+ precision,
606
+ actualStandard,
607
+ bits,
608
+ symbols,
609
+ full,
610
+ fullforms,
611
+ output,
612
+ spacer,
613
+ );
614
+ }
615
+
616
+ // Exponent calculation + clamp + precision adjustment
617
+ const { e: calculatedE, precision: precisionAdjusted } = calculateExponent(
618
+ num,
619
+ e,
620
+ exponent,
621
+ isDecimal,
622
+ precision,
623
+ );
624
+ e = calculatedE;
625
+ const autoExponent = exponent === -1 || isNaN(exponent);
626
+
627
+ if (output === EXPONENT) {
628
+ return e;
629
+ }
630
+
631
+ const { result: valueResult, e: valueExponent } = calculateOptimizedValue(
632
+ num,
633
+ e,
634
+ isDecimal,
635
+ bits,
636
+ ceil,
637
+ autoExponent,
638
+ );
639
+ val = valueResult;
640
+ e = valueExponent;
641
+
642
+ // Rounding + auto-increment ceiling
643
+ const rounded = applyRounding(val, ceil, e, round, roundingFunc, autoExponent);
644
+ result[0] = rounded.value;
645
+ e = rounded.e;
646
+
647
+ // Precision handling
648
+ if (precisionAdjusted > 0) {
649
+ const precisionResult = applyPrecisionHandling(
650
+ result[0],
651
+ precisionAdjusted,
652
+ e,
653
+ num,
654
+ isDecimal,
655
+ bits,
656
+ ceil,
657
+ roundingFunc,
658
+ round,
659
+ exponent,
660
+ );
661
+ result[0] = precisionResult.value;
662
+ e = precisionResult.e;
663
+ }
664
+
665
+ u = resolveSymbol(actualStandard, bits, e, isDecimal);
666
+ result[1] = u;
667
+
668
+ decorateResult(
669
+ result,
670
+ neg,
671
+ symbols,
672
+ locale,
673
+ localeOptions,
674
+ separator,
675
+ pad,
676
+ round,
677
+ full,
678
+ fullforms,
679
+ actualStandard,
680
+ e,
681
+ bits,
682
+ );
683
+
684
+ return formatOutput(result, e, u, output, spacer);
685
+ }
686
+
687
+ /**
688
+ * Creates a partially applied version of filesize with preset options
689
+ * @param {Object} [options={}] - Configuration options (same as filesize)
690
+ * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes
691
+ * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter
692
+ * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)
693
+ * @param {number} [options.round=2] - Number of decimal places to round to
694
+ * @param {string|boolean} [options.locale=""] - Locale for number formatting, true for system locale
695
+ * @param {Object} [options.localeOptions={}] - Additional options for locale formatting
696
+ * @param {string} [options.separator=""] - Custom decimal separator
697
+ * @param {string} [options.spacer=" "] - String to separate value and unit
698
+ * @param {Object} [options.symbols={}] - Custom unit symbols
699
+ * @param {string} [options.standard=""] - Unit standard to use (SI, IEC, JEDEC)
700
+ * @param {string} [options.output="string"] - Output format: "string", "array", "object", or "exponent"
701
+ * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations
702
+ * @param {Array} [options.fullforms=[]] - Custom full unit names
703
+ * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)
704
+ * @param {string} [options.roundingMethod="round"] - Math rounding method to use
705
+ * @param {number} [options.precision=0] - Number of significant digits (0 for auto)
706
+ * @returns {Function} A function that takes a file size and returns formatted output
707
+ * @example
708
+ * const formatBytes = partial({round: 1, standard: "iec"});
709
+ * formatBytes(1024) // "1 KiB"
710
+ * formatBytes(2048) // "2 KiB"
711
+ * formatBytes(1536) // "1.5 KiB"
712
+ */
713
+ function partial({
714
+ bits = false,
715
+ pad = false,
716
+ base = -1,
717
+ round = 2,
718
+ locale = EMPTY,
719
+ separator = EMPTY,
720
+ spacer = SPACE,
721
+ standard = EMPTY,
722
+ output = STRING,
723
+ fullform = false,
724
+ exponent = -1,
725
+ roundingMethod = ROUND,
726
+ precision = 0,
727
+ localeOptions = {},
728
+ symbols = {},
729
+ fullforms = [],
730
+ } = {}) {
731
+ const cloned = {
732
+ localeOptions: JSON.parse(JSON.stringify(localeOptions)),
733
+ symbols: JSON.parse(JSON.stringify(symbols)),
734
+ fullforms: JSON.parse(JSON.stringify(fullforms)),
735
+ };
736
+
737
+ return (arg) =>
738
+ filesize(arg, {
739
+ bits,
740
+ pad,
741
+ base,
742
+ round,
743
+ locale,
744
+ localeOptions: cloned.localeOptions,
745
+ separator,
746
+ spacer,
747
+ symbols: cloned.symbols,
748
+ standard,
749
+ output,
750
+ fullform,
751
+ fullforms: cloned.fullforms,
752
+ exponent,
753
+ roundingMethod,
754
+ precision,
755
+ });
246
756
  }
247
757
 
248
758
  exports.filesize = filesize;
@@ -1,28 +1,28 @@
1
- Copyright (c) 2024, Jason Mulligan
2
- All rights reserved.
3
-
4
- Redistribution and use in source and binary forms, with or without
5
- modification, are permitted provided that the following conditions are met:
6
-
7
- * Redistributions of source code must retain the above copyright notice, this
8
- list of conditions and the following disclaimer.
9
-
10
- * Redistributions in binary form must reproduce the above copyright notice,
11
- this list of conditions and the following disclaimer in the documentation
12
- and/or other materials provided with the distribution.
13
-
14
- * Neither the name of filesize nor the names of its
15
- contributors may be used to endorse or promote products derived from
16
- this software without specific prior written permission.
17
-
18
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
- OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
-
1
+ Copyright (c) 2026, Jason Mulligan
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ * Neither the name of filesize nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
+
@@ -1 +1 @@
1
- {"name":"filesize","author":"Jason Mulligan <jason.mulligan@avoidwork.com>","version":"10.1.6","license":"BSD-3-Clause","types":"index.d.ts","type":"commonjs"}
1
+ {"name":"filesize","author":"Jason Mulligan <jason.mulligan@avoidwork.com>","version":"11.0.17","license":"BSD-3-Clause","types":"index.d.ts","type":"commonjs"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsdoctor/utils",
3
- "version": "1.5.11",
3
+ "version": "1.5.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/web-infra-dev/rsdoctor",
@@ -99,9 +99,9 @@
99
99
  "json-stream-stringify": "3.0.1",
100
100
  "lines-and-columns": "2.0.4",
101
101
  "picocolors": "^1.1.1",
102
- "rslog": "^1.3.2",
102
+ "rslog": "^2.1.1",
103
103
  "strip-ansi": "^6.0.1",
104
- "@rsdoctor/types": "1.5.11"
104
+ "@rsdoctor/types": "1.5.12"
105
105
  },
106
106
  "devDependencies": {
107
107
  "@types/babel__code-frame": "7.27.0",
@@ -109,12 +109,12 @@
109
109
  "@types/deep-eql": "4.0.2",
110
110
  "@types/envinfo": "7.8.4",
111
111
  "@types/fs-extra": "^11.0.4",
112
- "@types/node": "^22.8.1",
112
+ "@types/node": "^24.12.3",
113
113
  "prebundle": "1.6.4",
114
- "filesize": "^10.1.6",
114
+ "filesize": "^11.0.17",
115
115
  "connect": "3.7.0",
116
116
  "tslib": "2.8.1",
117
- "typescript": "^5.9.2"
117
+ "typescript": "^6.0.3"
118
118
  },
119
119
  "publishConfig": {
120
120
  "access": "public",