@rsdoctor/utils 1.5.10 → 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.
@@ -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;