diff --git a/.cursor/rules/javascript.mdc b/.cursor/rules/javascript.mdc deleted file mode 100644 index 78c6287..0000000 --- a/.cursor/rules/javascript.mdc +++ /dev/null @@ -1,14 +0,0 @@ ---- -description: JavaScript module -globs: -alwaysApply: true ---- - -- Use JSDoc standard for creating docblocks of functions and classes. -- Always use camelCase for function names. -- Always use upper-case snake_case for constants. -- Create integration tests in 'tests/integration' that use node-assert, which run with mocha. -- Create unit tests in 'tests/unit' that use node-assert, which run with mocha. -- Use node.js community "Best Practices". -- Adhere to DRY, KISS, YAGNI, & SOLID principles -- Adhere to OWASP security guidance diff --git a/.husky/pre-commit b/.husky/pre-commit index 610c2a5..d9e5d42 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,4 +1,2 @@ #!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npm test diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..46696f8 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,6 @@ +{ + "indentWidth": 4, + "ignorePatterns": [], + "lineWidth": 120, + "useTabs": true +} diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..453362a --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,14 @@ +{ + "rules": { + "no-unused-vars": "error", + "no-console": "off" + }, + "overrides": [ + { + "files": ["src/**/*.js", "tests/**/*.js"], + "rules": { + "no-console": "error" + } + } + ] +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..01e6b38 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,407 @@ +# Agent Guidelines for filesize.js + +## Project Overview + +filesize.js is a lightweight, high-performance file size utility that converts bytes to human-readable strings. It supports multiple unit standards (SI, IEC, JEDEC), localization, and various output formats. + +**Key Facts:** +- **Single source file**: `src/filesize.js` (285 lines) +- **Helper functions**: `src/helpers.js` (215 lines) +- **Constants**: `src/constants.js` (81 lines) +- **Zero dependencies**: Uses only native JavaScript APIs +- **100% test coverage**: 149 tests passing + +## Coding Conventions + +- **JSDoc**: Use JSDoc standard for all functions and classes +- **Naming**: + - Functions: camelCase (`handleZeroValue`, `applyPrecisionHandling`) + - Constants: UPPER_SNAKE_CASE (`IEC`, `JEDEC`, `BINARY_POWERS`) +- **Testing**: + - Unit tests in `tests/unit/` using `node:test` + node:assert + - Use `describe()` and `it()` from `node:test` +- **Linting**: oxlint (fast Rust-based linter) +- **Formatting**: oxfmt (fast Rust-based formatter) +- **Principles**: DRY, KISS, YAGNI, SOLID +- **Security**: OWASP best practices + +### Code Style + +```javascript +// Function with JSDoc +/** + * Description + * @param {type} param - Description + * @returns {type} Description + */ +export function functionName (param) { + // Implementation +} + +// Constants +export const CONSTANT_NAME = "value"; + +// Imports: group by source, alphabetize +import { + ARRAY, + BIT, + BYTE +} from "./constants.js"; +import { + helperFunction +} from "./helpers.js"; +``` + +## Project Structure + +``` +filesize.js/ +├── src/ +│ ├── filesize.js # Main implementation (filesize + partial) +│ ├── helpers.js # Helper functions (5 exported functions) +│ └── constants.js # Constants, symbols, lookup tables +├── tests/ +│ └── unit/ +│ └── filesize-helpers.test.js # Helper function tests +│ └── filesize.test.js # Main function tests +├── dist/ # Built distributions (generated) +├── types/ # TypeScript definitions +└── docs/ # Documentation +``` + +## Core Architecture + +### Data Flow + +``` +Input → Validation → Standard Normalization → Exponent Calculation +→ Value Conversion → Formatting → Output Generation +``` + +### Key Components + +1. **`filesize(arg, options)`**: Main conversion function +2. **`partial(options)`**: Creates pre-configured function +3. **`handleZeroValue()`**: Special handling for zero input +4. **`calculateOptimizedValue()`**: Core conversion logic +5. **`applyPrecisionHandling()`**: Precision + scientific notation fix +6. **`applyNumberFormatting()`**: Locale, separator, padding + +### Standard Selection + +- **SI (default)**: Base 10, uses JEDEC symbols (kB, MB, GB) +- **IEC**: Base 1024, binary prefixes (KiB, MiB, GiB) +- **JEDEC**: Base 1024, uses traditional symbols (KB, MB, GB) + +## Common Tasks + +### Adding a New Feature + +1. Add constants to `src/constants.js` if needed +2. Implement logic in `src/filesize.js` or `src/helpers.js` +3. Add JSDoc documentation +4. Write unit tests in `tests/unit/` +5. Write integration tests in `tests/integration/` +6. Run `npm test` to verify 100% coverage maintained +7. Run `npm run build` to update distributions + +### Fixing a Bug + +1. Write a failing test first +2. Implement the fix +3. Verify test passes +4. Run full test suite +5. Check for regressions + +### Modifying `partial()` + +**Important**: `partial()` uses destructuring to freeze option values for immutability: +```javascript +export function partial({ + bits = false, + pad = false, + base = -1, + round = 2, + // ... other options +} = {}) { + return (arg) => + filesize(arg, { + bits, + pad, + base, + round, + // ... same options + }); +} +``` + +- Destructuring extracts and freezes primitive values at creation time +- Prevents mutations to original options object from affecting created formatters +- Simpler than deep cloning while maintaining immutability for all option types + +## API Reference + +### Options Object + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `bits` | boolean | `false` | Calculate bits instead of bytes | +| `pad` | boolean | `false` | Pad decimal places | +| `base` | number | `-1` | Number base (2, 10, or -1 for auto) | +| `round` | number | `2` | Decimal places to round | +| `locale` | string\|boolean | `""` | Locale for formatting | +| `separator` | string | `""` | Custom decimal separator | +| `spacer` | string | `" "` | Value-unit separator | +| `symbols` | Object | `{}` | Custom unit symbols | +| `standard` | string | `""` | Unit standard (si, iec, jedec) | +| `output` | string | `"string"` | Output format (string, array, object, exponent) | +| `fullform` | boolean | `false` | Use full unit names | +| `fullforms` | Array | `[]` | Custom full unit names | +| `exponent` | number | `-1` | Force specific exponent | +| `roundingMethod` | string | `"round"` | Math.round, floor, or ceil | +| `precision` | number | `0` | Significant digits | + +### Output Formats + +- **string**: `"1.5 KB"` (default) +- **array**: `[1.5, "KB"]` +- **object**: `{value: 1.5, symbol: "KB", exponent: 1, unit: "KB"}` +- **exponent**: `1` + +## Testing Guidelines + +### Running Tests + +```bash +npm test # Full test suite (lint + node:test) +npm run test:watch # Live test watching +``` + +### Test Structure + +```javascript +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { filesize } from '../../src/filesize.js'; + +describe('Feature', () => { + it('should do something', () => { + const result = filesize(1024); + assert.strictEqual(result, "1.02 kB"); + }); +}); +``` + +### Coverage Requirements + +- **100%** statement, branch, function, and line coverage required +- No uncovered lines allowed +- Coverage reported by Node's built-in test runner with `--experimental-test-coverage` + +## Build Process + +```bash +npm run build # Build all distributions +npm run dev # Development mode with live reload +npm run build:watch # Watch mode +npm run build:analyze # Bundle size analysis +``` + +### Distribution Files + +- `dist/filesize.cjs` - CommonJS +- `dist/filesize.js` - ES Module +- `dist/filesize.min.js` - Minified ES Module +- `dist/filesize.umd.js` - UMD (browser) +- `dist/filesize.umd.min.js` - Minified UMD + +## Performance Considerations + +### Fast Operations (>10M ops/sec) +- Basic conversions: `filesize(1024)` +- Large numbers: `filesize(1073741824)` +- Standard output formats + +### Slower Operations (<100K ops/sec) +- Locale formatting: `filesize(1024, {locale: "en-US"})` + +### Optimization Tips +1. Cache `partial()` formatters for reuse +2. Avoid locale formatting in performance-critical code +3. Use `object` output for fastest structured data access + +## Common Patterns + +### Creating Formatters + +```javascript +import { partial } from 'filesize'; + +const formatBinary = partial({base: 2, standard: "iec"}); +const formatBits = partial({bits: true}); +const formatPrecise = partial({round: 3, pad: true}); +``` + +### Error Handling + +```javascript +try { + filesize("invalid"); +} catch (error) { + // TypeError: "Invalid number" +} + +try { + partial({ exponent: NaN }); // Works - NaN is preserved via destructuring +} catch (error) { + // No error - destructuring handles all primitive values +} +``` + +### Padding with Negative Numbers + +When using `pad: true` with negative numbers, the decimal separator detection must skip the minus sign: +```javascript +// Correct: slice(1) to skip minus sign +const x = separator || (resultStr.slice(1).match(/(\D)/g) || []).pop() || PERIOD; +``` + +Without this, `-1.00` would split on `-` instead of `.`, producing incorrect output like `-10 kB` instead of `-1.00 kB`. + +### BigInt Support + +```javascript +filesize(BigInt(1024)); // "1.02 kB" +filesize(BigInt("10000000000000000000")); // Works with huge numbers +``` + +### Auto-Exponent Pattern + +When checking if the exponent should auto-calculate, use a named variable for clarity and coverage: +```javascript +const autoExponent = exponent === -1 || isNaN(exponent); + +if (result[0] === ceil && e < 8 && autoExponent) { + // Auto-increment logic +} +``` + +This pattern is used in: +- `filesize.js`: rounding-based auto-increment +- `filesize.js`: precision handling +- `helpers.js`: `calculateOptimizedValue()` bits auto-increment +- `helpers.js`: `applyPrecisionHandling()` scientific notation fix + +### Forced Exponent Behavior + +When `exponent` is explicitly set (not `-1` or `NaN`): +- Bits auto-increment is **disabled** in `calculateOptimizedValue()` +- Scientific notation normalization is **disabled** in `applyPrecisionHandling()` +- Rounding-based auto-increment is **disabled** in `filesize()` + +Example: +```javascript +filesize(1024, { exponent: 0, bits: true }); // "8192 bit" (not auto-incremented to kbit) +filesize(1024, { exponent: 0 }); // "1024 B" (not auto-incremented to kB) +``` + +### Security + +The library follows OWASP best practices and is secure for production use: + +**Secure Patterns:** +- No `eval`, `Function` constructor, or command injection +- No prototype pollution (read-only symbol access, deep cloning in `partial()`) +- No XSS (returns plain strings only, no HTML/JS generation) +- No SSRF (no network requests) +- No ReDoS (simple regex, no catastrophic backtracking) +- Input validation on all user-provided values + +**Security Considerations:** +- The `symbols` option allows user-controlled objects but only reads from them (safe) +- On Node.js <17, `partial()` uses JSON cloning which cannot serialize `NaN`/`Infinity` - throws clear error instead of silent data loss +- The `symbols` option allows user-controlled objects but only reads from them (safe) + +**See `docs/TECHNICAL_DOCUMENTATION.md` for full security details.** + +## Build System + +The `ensureNewline()` plugin in `rollup.config.js` uses `generateBundle()` (not `renderChunk()`) to add trailing newlines. This preserves sourcemaps in minified builds by modifying the bundle after sourcemap generation. + +## Documentation Standards + +### JSDoc Template + +```javascript +/** + * Function description + * @param {type} paramName - Parameter description + * @param {type} [paramName=default] - Optional parameter + * @returns {type} Return description + * @throws {ErrorType} When condition occurs + * @example + * // Example usage + * functionName(arg); // "result" + */ +``` + +### When to Update Docs + +- Add/modify function parameters +- Change default values +- Add new features +- Update examples + +## Git Workflow + +1. Create feature branch: `git checkout -b feature/name` +2. Make changes +3. Run tests: `npm test` +4. Build: `npm run build` +5. Commit: `git commit -m "type: description"` +6. Push: `git push origin feature/name` + +### Commit Message Types + +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation +- `refactor`: Code restructuring +- `build`: Build system changes +- `test`: Test additions/fixes + +## Troubleshooting + +### Tests Fail After Build +Run `npm test` before committing. The build process doesn't run tests automatically. + +### Coverage Drops Below 100% +Check the c8 report for uncovered lines. Add tests for missing branches. + +### Linting Errors +Run `npm run lint:fix` to auto-fix common issues. + +### Formatting +Run `npm run format:fix` to format code with oxfmt. + +### Build Output Files +Ensure output files end with newlines (configured in `rollup.config.js` with `ensureNewline()` plugin). + +## Quick Reference + +**Files to modify:** +- `src/filesize.js` - Main logic (95% of changes) +- `src/helpers.js` - Helper functions +- `src/constants.js` - Constants/lookup tables + +**Commands:** +- `npm test` - Run everything +- `npm run build` - Build distributions +- `npm run lint` - Check code style +- `npm run format:fix` - Format code + +**Key constraints:** +- No external dependencies +- 100% test coverage required +- JSDoc on all exported functions +- ES Modules only (no CommonJS in src/) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..a18b71b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,254 @@ +# Contributing to filesize.js + +Thank you for your interest in contributing to filesize.js! This document outlines the process for contributing to the project. + +## Table of Contents + +- [Getting Started](#getting-started) +- [Reporting Issues](#reporting-issues) +- [Development Workflow](#development-workflow) +- [Testing](#testing) +- [Code Style](#code-style) +- [Commit Messages](#commit-messages) +- [Pull Request Process](#pull-request-process) +- [License](#license) + +## Getting Started + +1. Fork the repository +2. Clone your fork: + ```bash + git clone https://github.com/your-username/filesize.js.git + cd filesize.js + ``` +3. Install dependencies: + ```bash + npm install + ``` +4. Start development mode: + ```bash + npm run dev + ``` + +## Reporting Issues + +Before reporting an issue, please search existing issues to avoid duplicates. + +When creating a new issue, include: +- A clear, descriptive title +- Steps to reproduce the problem +- Expected vs. actual behavior +- Environment details (Node.js version, OS, browser) +- Code samples if applicable + +## Development Workflow + +### Project Structure + +``` +filesize.js/ +├── src/ +│ ├── filesize.js # Main implementation +│ ├── helpers.js # Helper functions +│ └── constants.js # Constants and lookup tables +├── tests/ +│ └── unit/ # Unit tests +├── dist/ # Built distributions (generated) +└── types/ # TypeScript definitions +``` + +### Building + +```bash +npm run build # Build all distributions +npm run build:watch # Watch mode for development +npm run build:analyze # Analyze bundle sizes +``` + +### Distribution Files + +- `dist/filesize.cjs` - CommonJS +- `dist/filesize.js` - ES Module +- `dist/filesize.min.js` - Minified ES Module +- `dist/filesize.umd.js` - UMD (browser) +- `dist/filesize.umd.min.js` - Minified UMD + +## Testing + +### Running Tests + +```bash +npm test # Run all tests (lint + node:test) +npm run test:watch # Live test watching +``` + +### Coverage Requirements + +- **100% test coverage** is required for all changes +- Coverage includes: statements, branches, functions, and lines +- Run with coverage: `npm test` + +### Writing Tests + +Tests use Node.js built-in test runner (`node:test`): + +```javascript +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { filesize } from '../../src/filesize.js'; + +describe('Feature', () => { + it('should do something', () => { + const result = filesize(1024); + assert.strictEqual(result, '1.02 kB'); + }); +}); +``` + +### Test Coverage + +``` +------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +------------|---------|----------|---------|---------|------------------- +All files | 100 | 100 | 100 | 100 | +------------|---------|----------|---------|---------|------------------- +``` + +## Code Style + +### Linting + +We use oxlint (Rust-based, fast): + +```bash +npm run lint # Check code style +npm run lint:fix # Auto-fix issues +``` + +### Formatting + +We use oxfmt (Rust-based, fast): + +```bash +npm run format:fix # Format code +``` + +### Conventions + +- **JSDoc**: Use JSDoc standard for all functions and classes +- **Naming**: + - Functions: camelCase (`handleZeroValue`, `applyPrecisionHandling`) + - Constants: UPPER_SNAKE_CASE (`IEC`, `JEDEC`, `BINARY_POWERS`) +- **Imports**: Group by source, alphabetize +- **Principles**: DRY, KISS, YAGNI, SOLID +- **Security**: OWASP best practices + +### Code Style Example + +```javascript +/** + * Description + * @param {type} param - Description + * @returns {type} Description + */ +export function functionName(param) { + // Implementation +} + +// Constants +export const CONSTANT_NAME = 'value'; + +// Imports: group by source, alphabetize +import { + ARRAY, + BIT, + BYTE +} from './constants.js'; +import { + helperFunction +} from './helpers.js'; +``` + +## Commit Messages + +We follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): + +[optional body] + +[optional footer] +``` + +### Types + +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation +- `refactor`: Code restructuring +- `build`: Build system changes +- `test`: Test additions/fixes +- `chore`: Maintenance tasks + +### Examples + +``` +feat: add precision option for significant digits +fix: correct bits auto-increment with forced exponent +docs: update README with TypeScript examples +refactor: simplify exponent auto-detection logic +build: update rollup configuration +test: add coverage for NaN exponent edge case +``` + +## Pull Request Process + +1. **Create a branch**: + ```bash + git checkout -b feature/your-feature-name + ``` + +2. **Make your changes** and ensure: + - All tests pass (`npm test`) + - 100% test coverage is maintained + - Code is formatted (`npm run format:fix`) + - No linting errors (`npm run lint`) + +3. **Build the project**: + ```bash + npm run build + ``` + +4. **Commit your changes**: + ```bash + git commit -m "type: description" + ``` + +5. **Push to your fork**: + ```bash + git push origin feature/your-feature-name + ``` + +6. **Open a Pull Request**: + - Provide a clear title and description + - Reference any related issues + - Include test coverage for changes + +### PR Checklist + +- [ ] Tests pass (`npm test`) +- [ ] 100% test coverage maintained +- [ ] Code is formatted +- [ ] No linting errors +- [ ] Documentation updated (if applicable) +- [ ] Build successful (`npm run build`) + +## License + +By contributing to filesize.js, you agree that your contributions will be licensed under the BSD-3 license. + +--- + +Thank you for contributing to filesize.js! diff --git a/LICENSE b/LICENSE index a5edba3..e9f67f0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2025, Jason Mulligan +Copyright (c) 2026, Jason Mulligan All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index 16a1bcf..ae134d5 100644 --- a/README.md +++ b/README.md @@ -1,518 +1,236 @@ # filesize.js -[![downloads](https://img.shields.io/npm/dt/filesize.svg)](https://www.npmjs.com/package/filesize) -[![npm version](https://badge.fury.io/js/filesize.svg)](https://badge.fury.io/js/filesize) +[![npm version](https://badge.fury.io/js/filesize.svg)](https://www.npmjs.com/package/filesize) [![Node.js Version](https://img.shields.io/node/v/filesize.svg)](https://nodejs.org/) [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![Build Status](https://github.com/avoidwork/woodland/actions/workflows/ci.yml/badge.svg)](https://github.com/avoidwork/filesize.js/actions) +[![Build Status](https://github.com/avoidwork/filesize.js/actions/workflows/ci.yml/badge.svg)](https://github.com/avoidwork/filesize.js/actions) -A lightweight, high-performance file size utility for JavaScript that converts bytes to human-readable strings. Works in both Node.js and browser environments with comprehensive format support. +A lightweight, high-performance file size utility that converts bytes to human-readable strings. Zero dependencies. 100% test coverage. -## Installation - -```bash -npm install filesize -``` - -## Usage - -### ES Modules - -```javascript -import {filesize} from "filesize"; -filesize(265318, {standard: "jedec"}); // "259.1 KB" -``` - -### CommonJS - -```javascript -const {filesize} = require("filesize"); -filesize(1024); // "1.02 kB" -``` - -### Partial Application - -```javascript -import {partial} from "filesize"; -const size = partial({standard: "jedec"}); -size(265318); // "259.1 KB" -``` - -## Parameters - -* **input** `{Number|String|BigInt}` - The value to convert (required) -* **options** `{Object}` - Configuration object (optional) - -### Options Object - -* **base** `{Number}` - Number base, default is `10` -* **bits** `{Boolean}` - Enables `bit` sizes, default is `false` -* **exponent** `{Number}` - Specifies the symbol via exponent, e.g. `2` is `MB` for base 2, default is `-1` -* **fullform** `{Boolean}` - Enables full form of unit of measure, default is `false` -* **fullforms** `{Array}` - Array of full form overrides, default is `[]` -* **locale** `{String|Boolean}` - BCP 47 language tag to specify a locale, or `true` to use default locale, default is `""` -* **localeOptions** `{Object}` - Dictionary of options defined by ECMA-402 ([Number.prototype.toLocaleString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString)) -* **output** `{String}` - Output of function (`array`, `exponent`, `object`, or `string`), default is `string` -* **pad** `{Boolean}` - Decimal place end padding, default is `false` -* **precision** `{Number}` - Sets precision of numerical output, default is `0` -* **round** `{Number}` - Decimal place, default is `2` -* **roundingMethod** `{String}` - Rounding method, can be `round`, `floor`, or `ceil`, default is `round` -* **separator** `{String}` - Decimal separator character, default is an empty string -* **spacer** `{String}` - Character between the `result` and `symbol`, default is `" "` -* **standard** `{String}` - Standard unit of measure, can be `iec`, `jedec`, or `si`. Default is `si` (base 10) -* **symbols** `{Object}` - Dictionary of IEC/JEDEC symbols to replace for localization - -### Input Validation - -The function validates input and throws `TypeError` for invalid values: - -```javascript -// Invalid input will throw TypeError -try { - filesize("invalid"); -} catch (error) { - console.error(error.message); // "Invalid input" -} - -try { - filesize(NaN); -} catch (error) { - console.error(error.message); // "Invalid input" -} -``` - -## Testing - -filesize.js maintains **100% test coverage** across all metrics with a comprehensive test suite of 47 test cases: - -```console --------------|---------|----------|---------|---------|------------------- -File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s --------------|---------|----------|---------|---------|------------------- -All files | 100 | 100 | 100 | 100 | - filesize.js | 100 | 100 | 100 | 100 | --------------|---------|----------|---------|---------|------------------- -``` - -### Running Tests +## Why filesize.js? -```bash -# Run all tests (linting + unit tests) -npm test - -# Run only unit tests -npm run mocha -``` +- **Zero dependencies** - Pure JavaScript, no external packages +- **100% test coverage** - Reliable, well-tested codebase +- **TypeScript ready** - Full type definitions included +- **Multiple standards** - SI, IEC, and JEDEC support +- **Localization** - Intl API for international formatting +- **BigInt support** - Handle extremely large file sizes +- **Functional API** - Partial application for reusable formatters +- **Browser & Node.js** - Works everywhere -### Test Coverage - -The test suite comprehensively covers: - -* **Basic functionality**: Core conversion logic and edge cases -* **Output formats**: All output types (string, array, object, exponent) -* **Standards support**: IEC, JEDEC, and SI standards with different bases -* **Bit conversion**: Bits vs bytes with auto-increment logic -* **Precision handling**: Rounding methods and decimal precision -* **Localization**: Locale formatting and custom symbols -* **Error handling**: Invalid inputs and boundary conditions -* **Partial functions**: All option combinations with curried functions - -## Performance Benchmarks - -filesize.js is optimized for high performance with comprehensive benchmarks covering various usage patterns: - -### 🚀 Performance Overview - -| Scenario | Operations/sec | Notes | -|----------|----------------|-------| -| **Basic conversion** | ~16-27M ops/sec | Fastest operations (large numbers) | -| **Small numbers** | ~18-20M ops/sec | Consistent performance | -| **With options** | ~5-13M ops/sec | Depends on option complexity | -| **Locale formatting** | ~91K ops/sec | Most expensive operation | -| **Stress testing** | ~2-10M ops/sec | Handles edge cases gracefully | - -### 📊 Detailed Benchmark Results - -#### Basic Performance (5-run average) -- **filesize(0)**: 18.6M ops/sec -- **filesize(512)**: 20.3M ops/sec -- **filesize(1024)**: 18.7M ops/sec -- **filesize(1048576)**: 23.5M ops/sec -- **filesize(1073741824)**: 23.6M ops/sec -- **filesize(1099511627776)**: 26.9M ops/sec -- **With bits=true**: 16.8M ops/sec -- **With standard="iec"**: 16.6M ops/sec -- **With round=4**: 13.4M ops/sec - -#### Options Performance Impact -- **bits=true**: 12.5M ops/sec -- **pad=true**: 5.6M ops/sec -- **locale="en-US"**: 91K ops/sec (significant overhead) -- **standard="iec"**: 8.8M ops/sec -- **standard="jedec"**: 9.0M ops/sec -- **output="array"**: 10.2M ops/sec -- **output="object"**: 9.2M ops/sec -- **fullform=true**: 7.8M ops/sec -- **precision=3**: 6.3M ops/sec -- **separator=","**: 7.2M ops/sec - -#### Stress Test Results -- **Edge cases**: 2.3M ops/sec (90% success rate) -- **Very large numbers**: 4.6M ops/sec (100% success) -- **Very small numbers**: 10.4M ops/sec (100% success) -- **Negative numbers**: 5.4M ops/sec (100% success) -- **Random options**: 2.3M ops/sec (100% success) -- **BigInt values**: 3.7M ops/sec (100% success) -- **Memory pressure**: 49K ops/sec (100% success) -- **Error conditions**: 715K ops/sec (~40% success rate) - -#### Partial Function Performance -Partial functions maintain excellent performance with minimal overhead: -- **Acceptable overhead**: 1.1-1.4x slower for most configurations -- **Locale partials**: Significant overhead (~180x slower) due to locale formatting -- **Creation cost**: Amortized across multiple uses - -### 💡 Performance Insights - -**Excellent Performance (>10M ops/sec)** -- Basic conversions with minimal options -- Large number processing (1TB+ values) -- Standard output formats (string, array, object) -- IEC and JEDEC standards - -**Good Performance (1-10M ops/sec)** -- Complex option combinations -- Precision and rounding operations -- Fullform output -- Stress test scenarios - -**Use Sparingly (<100K ops/sec)** -- Locale formatting (significant overhead ~91K ops/sec) -- Memory pressure conditions - -### 🎯 Optimization Tips - -1. **Cache partial functions** for repeated operations with same options -2. **Avoid locale formatting** in performance-critical code -3. **Use object output** for fastest structured data -4. **Batch similar operations** together -5. **Profile your specific usage patterns** - -### Running Benchmarks +## Installation ```bash -# Run all benchmarks -cd benchmarks && node index.js - -# Run specific benchmark -node benchmarks/basic-performance.js - -# With garbage collection (more accurate) -node --expose-gc benchmarks/index.js +npm install filesize ``` -### 🔥 Recent Performance Optimizations (v11.0.8) - -The latest version includes significant performance improvements: - -- **Pre-computed lookup tables** for Math operations (eliminates expensive `Math.pow()` calls) -- **Optimized base/standard logic** with reduced branching -- **Fast path for zero values** with minimal computation -- **Cached object property access** to reduce repeated lookups -- **Improved mathematical operations** with conditional calculations +## TypeScript -**Overall performance improvement: 30-70% faster** across common use cases while maintaining full backward compatibility. +Fully typed with TypeScript definitions included: -*Benchmarks run on macOS ARM64, Node.js v24.8.0, 12 CPU cores, 24GB RAM (5-run averages)* +```typescript +import { filesize, partial } from 'filesize'; -## API Reference +const result: string = filesize(1024); +const formatted: { value: number; symbol: string; exponent: number; unit: string } = filesize(1024, { output: 'object' }); -### Functions - -#### filesize(input, options) - -Converts a numeric value to a human-readable file size string. - -**Parameters:** - -* `input` `{Number|String|BigInt}` - The value to convert -* `options` `{Object}` - Configuration options (optional) - -**Returns:** `{String|Array|Object|Number}` - Formatted size based on output option - -```javascript -filesize(500); // "500 B" -filesize(1024, {base: 2}); // "1 KiB" -filesize(265318, {output: "array"}); // [265.32, "kB"] +const formatter: (arg: number | bigint) => string = partial({ standard: 'iec' }); ``` -**See also:** partial() - -#### partial(options) - -Creates a pre-configured filesize function with options applied. - -**Parameters:** - -* `options` `{Object}` - Configuration options to apply - -**Returns:** `{Function}` - New function with options pre-applied +## Usage ```javascript -const formatBinary = partial({base: 2, standard: "iec"}); -formatBinary(1048576); // "1 MiB" - -const formatBits = partial({bits: true}); -formatBits(1024); // "8.19 kbit" -``` - -**See also:** filesize() - -### Output Formats +import {filesize, partial} from "filesize"; -#### String Output (default) - -```javascript +filesize(1024); // "1.02 kB" filesize(265318); // "265.32 kB" -filesize(265318, {separator: ","}); // "265,32 kB" +filesize(1024, {standard: "iec"}); // "1 KiB" +filesize(1024, {bits: true}); // "8.19 kbit" ``` -#### Array Output +### Partial Application ```javascript -filesize(265318, {output: "array"}); // [265.32, "kB"] -filesize(1024, {output: "array", base: 2}); // [1, "KiB"] -``` - -#### Object Output +import {partial} from "filesize"; -```javascript -filesize(265318, {output: "object"}); -// {value: 265.32, symbol: "kB", exponent: 1, unit: "kB"} +const formatBinary = partial({standard: "iec"}); +formatBinary(1024); // "1 KiB" +formatBinary(1048576); // "1 MiB" ``` -#### Exponent Output - -```javascript -filesize(1024, {output: "exponent"}); // 1 -filesize(1048576, {output: "exponent", base: 2}); // 2 -``` +## Options -### Standards Support +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `bits` | boolean | `false` | Calculate bits instead of bytes | +| `base` | number | `-1` | Number base (2 for binary, 10 for decimal, -1 for auto) | +| `round` | number | `2` | Decimal places to round | +| `locale` | string\|boolean | `""` | Locale for formatting, `true` for system locale | +| `localeOptions` | Object | `{}` | Additional locale options | +| `separator` | string | `""` | Custom decimal separator | +| `spacer` | string | `" "` | Value-unit separator | +| `symbols` | Object | `{}` | Custom unit symbols | +| `standard` | string | `""` | Unit standard (`si`, `iec`, `jedec`) | +| `output` | string | `"string"` | Output format (`string`, `array`, `object`, `exponent`) | +| `fullform` | boolean | `false` | Use full unit names | +| `fullforms` | Array | `[]` | Custom full unit names | +| `exponent` | number | `-1` | Force specific exponent (-1 for auto) | +| `roundingMethod` | string | `"round"` | Math method (`round`, `floor`, `ceil`) | +| `precision` | number | `0` | Significant digits (0 for auto) | +| `pad` | boolean | `false` | Pad decimal places | -#### SI (International System of Units) - Default +## Output Formats ```javascript -filesize(1000); // "1 kB" (base 10) -filesize(1000000); // "1 MB" -``` - -#### IEC (International Electrotechnical Commission) +// String (default) +filesize(1536); // "1.54 kB" -```javascript -filesize(1024, {standard: "iec", base: 2}); // "1 KiB" -filesize(1048576, {standard: "iec", base: 2}); // "1 MiB" -``` +// Array +filesize(1536, {output: "array"}); // [1.54, "kB"] -#### JEDEC (Joint Electron Device Engineering Council) +// Object +filesize(1536, {output: "object"}); +// {value: 1.54, symbol: "kB", exponent: 1, unit: "kB"} -```javascript -filesize(1024, {standard: "jedec"}); // "1 KB" -filesize(1048576, {standard: "jedec"}); // "1 MB" +// Exponent +filesize(1536, {output: "exponent"}); // 1 ``` -## Examples - -### Basic Usage +## Standards ```javascript -import {filesize} from "filesize"; - -filesize(500); // "500 B" -filesize(1024); // "1.02 kB" -filesize(265318); // "265.32 kB" -filesize(265318, {round: 0}); // "265 kB" -``` - -### Binary Formats +// SI (default, base 10) +filesize(1000); // "1 kB" -```javascript -// IEC binary prefixes (KiB, MiB, GiB) +// IEC (binary, requires base: 2) filesize(1024, {base: 2, standard: "iec"}); // "1 KiB" -filesize(1048576, {base: 2, standard: "iec"}); // "1 MiB" -// JEDEC binary format (KB, MB, GB with binary calculation) +// JEDEC (binary calculation, traditional symbols) filesize(1024, {standard: "jedec"}); // "1 KB" -filesize(265318, {standard: "jedec"}); // "259.1 KB" ``` -### Bits vs Bytes +## Examples ```javascript -filesize(500, {bits: true}); // "4 kbit" +// Bits filesize(1024, {bits: true}); // "8.19 kbit" filesize(1024, {bits: true, base: 2}); // "8 Kibit" -``` -### Custom Formatting - -```javascript -// Full form units +// Full form filesize(1024, {fullform: true}); // "1.02 kilobytes" filesize(1024, {base: 2, fullform: true}); // "1 kibibyte" -// Custom separators and spacing +// Custom separator filesize(265318, {separator: ","}); // "265,32 kB" -filesize(265318, {spacer: ""}); // "265.32kB" -// Precision and padding +// Padding filesize(1536, {round: 3, pad: true}); // "1.536 kB" -filesize(1536, {precision: 3}); // "1.54 kB" -``` -### Localization +// Precision +filesize(1536, {precision: 3}); // "1.54 kB" -```javascript -// German locale +// Locale filesize(265318, {locale: "de"}); // "265,32 kB" // Custom symbols filesize(1, {symbols: {B: "Б"}}); // "1 Б" -// Custom full forms -filesize(12, {fullform: true, fullforms: ["байтов"]}); // "12 байтов" +// BigInt support +filesize(BigInt(1024)); // "1.02 kB" + +// Negative numbers +filesize(-1024); // "-1.02 kB" ``` -### Advanced Usage +## Error Handling ```javascript -// Specific exponent -filesize(1024, {exponent: 0}); // "1024 B" -filesize(1024, {exponent: 1}); // "1.02 kB" - -// BigInt support -filesize(BigInt(1024), {standard: "jedec"}); // "1 KB" +try { + filesize("invalid"); +} catch (error) { + // TypeError: "Invalid number" +} -// Extreme precision for very large numbers -filesize(Math.pow(1024, 8), {precision: 3}); // "1.21 YB" +try { + filesize(1024, {roundingMethod: "invalid"}); +} catch (error) { + // TypeError: "Invalid rounding method" +} ``` -### Partial Application Patterns - -```javascript -import {partial} from "filesize"; +## Testing -// Create specialized formatters -const formatBinary = partial({base: 2, standard: "iec"}); -const formatBits = partial({bits: true}); -const formatPrecise = partial({round: 3, pad: true}); -const formatGerman = partial({locale: "de"}); +```bash +npm test # Run all tests (lint + node:test) +npm run test:watch # Live test watching +``` -// Use throughout application -formatBinary(1048576); // "1 MiB" -formatBits(1024); // "8.19 kbit" -formatPrecise(1536); // "1.536 kB" -formatGerman(265318); // "265,32 kB" +**100% test coverage** with 149 tests: -// Method chaining equivalent -const sizes = [1024, 2048, 4096]; -sizes.map(formatBinary); // ["1 KiB", "2 KiB", "4 KiB"] +``` +--------------|---------|----------|---------|---------|------------------- +File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s +--------------|---------|----------|---------|---------|------------------- +All files | 100 | 100 | 100 | 100 | + constants.js | 100 | 100 | 100 | 100 | + filesize.js | 100 | 100 | 100 | 100 | + helpers.js | 100 | 100 | 100 | 100 | +--------------|---------|----------|---------|---------|------------------- ``` ## Development -This project follows Node.js best practices and uses: - -* **ES Modules** for modern JavaScript -* **Mocha** for testing with comprehensive coverage -* **ESLint** for code quality and consistency -* **Rollup** for building distributions -* **TypeScript definitions** for type safety +```bash +npm install # Install dependencies +npm run dev # Development mode with live reload +npm run build # Build distributions +npm run lint # Check code style +npm run lint:fix # Auto-fix linting issues +``` ### Project Structure ``` filesize.js/ ├── src/ -│ ├── filesize.js # Main implementation -│ └── constants.js # Unit definitions and constants +│ ├── filesize.js # Main implementation (285 lines) +│ ├── helpers.js # Helper functions (215 lines) +│ └── constants.js # Constants (81 lines) ├── tests/ │ └── unit/ -│ └── filesize.test.js # Comprehensive test suite -├── types/ -│ ├── filesize.d.ts # TypeScript definitions -│ └── constants.d.ts # Constants type definitions -└── package.json # Dependencies and scripts +├── dist/ # Built distributions +└── types/ # TypeScript definitions ``` -### Contributing - -1. Fork the repository -2. Create your feature branch (`git checkout -b feature/amazing-feature`) -3. Write tests for your changes -4. Ensure all tests pass (`npm test`) -5. Commit your changes (`git commit -m 'Add amazing feature'`) -6. Push to the branch (`git push origin feature/amazing-feature`) -7. Open a Pull Request +## Performance -### Development Workflow +- **Basic conversions**: ~16-27M ops/sec +- **With options**: ~5-13M ops/sec +- **Locale formatting**: ~91K ops/sec (use sparingly) -filesize.js includes an optimized development workflow with modern build tools: +**Optimization tips:** +1. Cache `partial()` formatters for reuse +2. Avoid locale formatting in performance-critical code +3. Use `object` output for fastest structured data access -* **🔄 Live Reload**: Use `npm run dev` for automatic rebuilds during development -* **📊 Bundle Analysis**: Monitor build sizes with `npm run build:analyze` -* **⚡ Fast Testing**: Live test running with `npm run test:watch` -* **🔧 Auto-fixing**: Automatic linting fixes with `npm run lint:fix` -* **📈 Performance**: Optimized Rollup configuration with enhanced tree shaking - -**Build Output Analysis:** -- Minified ES Module: ~1.8KB (gzipped) -- UMD Bundle: ~1.9KB (gzipped) -- Comprehensive source maps included - -### Development Commands - -```bash -# Install dependencies -npm install +## Browser Usage -# Development mode with live rebuild -npm run dev - -# Build distribution -npm run build - -# Build with bundle size analysis -npm run build:analyze - -# Live rebuild during development -npm run build:watch - -# Run linting -npm run lint - -# Auto-fix linting issues -npm run lint:fix - -# Run tests -npm test +```html + + +``` -# Live testing during development -npm run test:watch +## Contributing -# Run only unit tests -npm run mocha +We welcome contributions! Please see our [Contributing Guidelines](https://github.com/avoidwork/filesize.js/blob/master/CONTRIBUTING.md) for details. -# Bundle size analysis -npm run analyze:size +## Changelog -# Benchmarking -npm run benchmark -npm run benchmark:basic -npm run benchmark:stress -``` +See [CHANGELOG.md](https://github.com/avoidwork/filesize.js/blob/master/CHANGELOG.md) for a history of changes. ## License -Copyright (c) 2025 Jason Mulligan +Copyright (c) 2026 Jason Mulligan Licensed under the BSD-3 license. diff --git a/benchmarks/README.md b/benchmarks/README.md index b8d0dad..190b1d6 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -5,21 +5,27 @@ This directory contains comprehensive performance benchmarks for the filesize.js ## 📁 Benchmark Files ### 🏃 `basic-performance.js` + Tests fundamental performance characteristics of the filesize function: + - Basic conversion performance with various input sizes - Different option combinations - Memory usage analysis - Baseline performance metrics ### ⚙️ `options-benchmark.js` + Analyzes the performance impact of different configuration options: + - Individual option performance costs - Complex option combinations - Relative performance comparisons - Optimization insights ### 🔥 `stress-test.js` + Evaluates performance under challenging conditions: + - Edge cases and extreme values - Error handling performance - Memory pressure scenarios @@ -27,14 +33,18 @@ Evaluates performance under challenging conditions: - BigInt support testing ### 🔧 `partial-benchmark.js` + Focuses on the partial function and functional programming patterns: + - Partial function vs direct calls - Function creation overhead - Functional programming patterns - Currying performance analysis ### 🎯 `index.js` + Main benchmark runner that executes all test suites: + - Orchestrates all benchmark execution - Provides comprehensive summary - System information reporting @@ -43,12 +53,14 @@ Main benchmark runner that executes all test suites: ## 🚀 Running Benchmarks ### Run All Benchmarks + ```bash cd benchmarks node index.js ``` ### Run Individual Benchmarks + ```bash # Basic performance tests node basic-performance.js @@ -64,7 +76,9 @@ node partial-benchmark.js ``` ### Enhanced Performance Mode + For more accurate memory-related benchmarks, run with garbage collection exposed: + ```bash node --expose-gc index.js ``` @@ -72,6 +86,7 @@ node --expose-gc index.js ## 📊 Understanding Results ### Performance Metrics + - **Ops/sec**: Operations per second (higher is better) - **Avg (ms)**: Average execution time per operation (lower is better) - **Total (ms)**: Total execution time for all iterations @@ -80,22 +95,26 @@ node --expose-gc index.js ### Benchmark Categories #### 🎯 **Basic Performance** + - Measures core function performance - Tests with various input sizes (0 bytes to MAX_SAFE_INTEGER) - Establishes baseline performance characteristics #### ⚙️ **Options Impact** + - Quantifies performance cost of each option - Identifies expensive operations (locale formatting, complex outputs) - Helps optimize option usage #### 🔥 **Stress Testing** + - Validates performance under extreme conditions - Tests error handling efficiency - Measures performance consistency - Evaluates memory usage patterns #### 🔧 **Functional Programming** + - Compares partial functions vs direct calls - Analyzes currying overhead - Tests functional composition patterns @@ -103,6 +122,7 @@ node --expose-gc index.js ## 📈 Performance Insights ### General Findings + - **Baseline Performance**: ~500K-1M+ ops/sec for basic conversions - **Locale Formatting**: Significant overhead (~2-5x slower) - **Object Output**: Minimal overhead (~10-20% slower) @@ -110,6 +130,7 @@ node --expose-gc index.js - **Partial Functions**: ~10-30% overhead, amortized over multiple uses ### Optimization Tips + 1. **Cache Partial Functions**: Reuse partial functions for repeated operations 2. **Avoid Locale When Possible**: Use locale formatting sparingly 3. **Prefer String Output**: Fastest output format for most use cases @@ -119,15 +140,18 @@ node --expose-gc index.js ## 🔧 Benchmark Configuration ### Iteration Counts + - **Basic Performance**: 100,000 iterations - **Options Testing**: 50,000 iterations - **Stress Testing**: 10,000 iterations - **Partial Functions**: 100,000 iterations ### Warmup Periods + All benchmarks include warmup periods to ensure JIT optimization and stable measurements. ### Memory Management + - Garbage collection calls between tests (when available) - Memory pressure testing - Memory usage monitoring @@ -135,43 +159,48 @@ All benchmarks include warmup periods to ensure JIT optimization and stable meas ## 🛠️ Customizing Benchmarks ### Adding New Tests + 1. Create a new benchmark file in the `benchmarks` directory 2. Follow the existing pattern for benchmark functions 3. Add the file to `BENCHMARK_FILES` in `index.js` ### Modifying Parameters + - Adjust `ITERATIONS` constants for different test durations - Modify test data sets for specific scenarios - Add new option combinations for testing ### Example Custom Benchmark + ```javascript -import { filesize } from '../dist/filesize.js'; +import { filesize } from "../dist/filesize.js"; const ITERATIONS = 10000; function benchmark(testName, testFunction, iterations = ITERATIONS) { - // Warmup - for (let i = 0; i < 1000; i++) { - testFunction(); - } - - const startTime = process.hrtime.bigint(); - for (let i = 0; i < iterations; i++) { - testFunction(); - } - const endTime = process.hrtime.bigint(); - - const totalTime = Number(endTime - startTime) / 1000000; - const avgTime = totalTime / iterations; - const opsPerSecond = Math.round(1000 / avgTime); - - return { testName, opsPerSecond, avgTime }; + // Warmup + for (let i = 0; i < 1000; i++) { + testFunction(); + } + + const startTime = process.hrtime.bigint(); + for (let i = 0; i < iterations; i++) { + testFunction(); + } + const endTime = process.hrtime.bigint(); + + const totalTime = Number(endTime - startTime) / 1000000; + const avgTime = totalTime / iterations; + const opsPerSecond = Math.round(1000 / avgTime); + + return { testName, opsPerSecond, avgTime }; } // Your custom test -const result = benchmark('Custom test', () => { - return filesize(1024 * 1024, { /* your options */ }); +const result = benchmark("Custom test", () => { + return filesize(1024 * 1024, { + /* your options */ + }); }); console.log(result); @@ -180,18 +209,21 @@ console.log(result); ## 🔍 Interpreting Results ### Performance Baselines + - **Excellent**: >1M ops/sec -- **Good**: 500K-1M ops/sec +- **Good**: 500K-1M ops/sec - **Acceptable**: 100K-500K ops/sec - **Slow**: <100K ops/sec ### When to Optimize + - If your use case requires >100K operations/sec - When performance regression is detected - Before production deployment with high load - When adding new features or options ### Profiling Your Application + 1. Run benchmarks with your specific usage patterns 2. Identify bottlenecks in your option combinations 3. Test with your actual data sizes @@ -200,6 +232,7 @@ console.log(result); ## 🤝 Contributing When contributing performance improvements: + 1. Run all benchmarks before and after changes 2. Document performance impacts in commit messages 3. Add new benchmarks for new features @@ -209,4 +242,4 @@ When contributing performance improvements: - [MDN Performance Best Practices](https://developer.mozilla.org/en-US/docs/Web/Performance) - [Node.js Performance Hooks](https://nodejs.org/api/perf_hooks.html) -- [V8 Performance Tips](https://v8.dev/blog/optimizing-cpp-and-js) \ No newline at end of file +- [V8 Performance Tips](https://v8.dev/blog/optimizing-cpp-and-js) diff --git a/benchmarks/basic-performance.js b/benchmarks/basic-performance.js index 348b11a..99fb831 100644 --- a/benchmarks/basic-performance.js +++ b/benchmarks/basic-performance.js @@ -1,10 +1,10 @@ /** * Basic Performance Benchmarks for filesize.js - * + * * Tests basic conversion performance with various input sizes */ -import { filesize } from '../dist/filesize.js'; +import { filesize } from "../dist/filesize.js"; const ITERATIONS = 100000; const WARMUP_ITERATIONS = 10000; @@ -17,30 +17,30 @@ const WARMUP_ITERATIONS = 10000; * @returns {Object} Performance results */ function benchmark(testName, testFunction, iterations = ITERATIONS) { - // Warmup - for (let i = 0; i < WARMUP_ITERATIONS; i++) { - testFunction(); - } - - // Actual benchmark - const startTime = process.hrtime.bigint(); - - for (let i = 0; i < iterations; i++) { - testFunction(); - } - - const endTime = process.hrtime.bigint(); - const totalTime = Number(endTime - startTime) / 1000000; // Convert to milliseconds - const avgTime = totalTime / iterations; - const opsPerSecond = 1000 / avgTime; - - return { - testName, - iterations, - totalTime: totalTime.toFixed(2), - avgTime: avgTime.toFixed(6), - opsPerSecond: Math.round(opsPerSecond) - }; + // Warmup + for (let i = 0; i < WARMUP_ITERATIONS; i++) { + testFunction(); + } + + // Actual benchmark + const startTime = process.hrtime.bigint(); + + for (let i = 0; i < iterations; i++) { + testFunction(); + } + + const endTime = process.hrtime.bigint(); + const totalTime = Number(endTime - startTime) / 1000000; // Convert to milliseconds + const avgTime = totalTime / iterations; + const opsPerSecond = 1000 / avgTime; + + return { + testName, + iterations, + totalTime: totalTime.toFixed(2), + avgTime: avgTime.toFixed(6), + opsPerSecond: Math.round(opsPerSecond), + }; } /** @@ -48,79 +48,59 @@ function benchmark(testName, testFunction, iterations = ITERATIONS) { * @param {Array} results - Array of benchmark results */ function printResults(results) { - console.log('\n📊 Basic Performance Benchmark Results'); - console.log('=' .repeat(80)); - console.log('Test Name'.padEnd(25) + 'Iterations'.padEnd(12) + 'Total (ms)'.padEnd(12) + 'Avg (ms)'.padEnd(12) + 'Ops/sec'); - console.log('-'.repeat(80)); - - results.forEach(result => { - console.log( - result.testName.padEnd(25) + - result.iterations.toString().padEnd(12) + - result.totalTime.padEnd(12) + - result.avgTime.padEnd(12) + - result.opsPerSecond.toLocaleString() - ); - }); - console.log('=' .repeat(80)); + console.log("\n📊 Basic Performance Benchmark Results"); + console.log("=".repeat(80)); + console.log( + "Test Name".padEnd(25) + + "Iterations".padEnd(12) + + "Total (ms)".padEnd(12) + + "Avg (ms)".padEnd(12) + + "Ops/sec", + ); + console.log("-".repeat(80)); + + results.forEach((result) => { + console.log( + result.testName.padEnd(25) + + result.iterations.toString().padEnd(12) + + result.totalTime.padEnd(12) + + result.avgTime.padEnd(12) + + result.opsPerSecond.toLocaleString(), + ); + }); + console.log("=".repeat(80)); } // Test data sets -const testSizes = [ - 0, - 512, - 1024, - 1048576, - 1073741824, - 1099511627776, - Number.MAX_SAFE_INTEGER -]; +const testSizes = [0, 512, 1024, 1048576, 1073741824, 1099511627776, Number.MAX_SAFE_INTEGER]; const results = []; // Basic filesize conversion tests -console.log('🚀 Starting Basic Performance Benchmarks...\n'); - -testSizes.forEach(size => { - const result = benchmark( - `filesize(${size})`, - () => filesize(size) - ); - results.push(result); +console.log("🚀 Starting Basic Performance Benchmarks...\n"); + +testSizes.forEach((size) => { + const result = benchmark(`filesize(${size})`, () => filesize(size)); + results.push(result); }); // Test with different options -results.push(benchmark( - 'filesize w/ bits=true', - () => filesize(1048576, { bits: true }) -)); - -results.push(benchmark( - 'filesize w/ standard=IEC', - () => filesize(1048576, { standard: 'iec' }) -)); - -results.push(benchmark( - 'filesize w/ round=4', - () => filesize(1048576, { round: 4 }) -)); - -results.push(benchmark( - 'filesize w/ fullform=true', - () => filesize(1048576, { fullform: true }) -)); - -results.push(benchmark( - 'filesize w/ output=object', - () => filesize(1048576, { output: 'object' }) -)); +results.push(benchmark("filesize w/ bits=true", () => filesize(1048576, { bits: true }))); + +results.push(benchmark("filesize w/ standard=IEC", () => filesize(1048576, { standard: "iec" }))); + +results.push(benchmark("filesize w/ round=4", () => filesize(1048576, { round: 4 }))); + +results.push(benchmark("filesize w/ fullform=true", () => filesize(1048576, { fullform: true }))); + +results.push(benchmark("filesize w/ output=object", () => filesize(1048576, { output: "object" }))); printResults(results); // Memory usage estimation const memUsage = process.memoryUsage(); -console.log('\n💾 Memory Usage:'); +console.log("\n💾 Memory Usage:"); console.log(` RSS: ${filesize(memUsage.rss)}`); console.log(` Heap Used: ${filesize(memUsage.heapUsed)}`); console.log(` Heap Total: ${filesize(memUsage.heapTotal)}`); -console.log(` External: ${filesize(memUsage.external)}`); \ No newline at end of file +console.log(` External: ${filesize(memUsage.external)}`); diff --git a/benchmarks/index.js b/benchmarks/index.js index b009ec3..c3624b3 100755 --- a/benchmarks/index.js +++ b/benchmarks/index.js @@ -2,23 +2,23 @@ /** * Benchmark Runner for filesize.js - * + * * Runs all benchmark suites and provides comprehensive performance analysis */ -import { spawn } from 'child_process'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; -import { cpus, totalmem, freemem } from 'os'; +import { spawn } from "child_process"; +import { fileURLToPath } from "url"; +import { dirname, join } from "path"; +import { cpus, totalmem, freemem } from "os"; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const BENCHMARK_FILES = [ - 'basic-performance.js', - 'options-benchmark.js', - 'stress-test.js', - 'partial-benchmark.js' + "basic-performance.js", + "options-benchmark.js", + "stress-test.js", + "partial-benchmark.js", ]; /** @@ -27,52 +27,52 @@ const BENCHMARK_FILES = [ * @returns {Promise} Benchmark execution results */ function runBenchmark(filename) { - return new Promise((resolve, reject) => { - const filepath = join(__dirname, filename); - const startTime = Date.now(); - - console.log(`\n🏃 Running ${filename}...`); - console.log('='.repeat(60)); - - const child = spawn('node', [filepath], { - stdio: 'inherit', - cwd: __dirname - }); - - child.on('close', (code) => { - const endTime = Date.now(); - const duration = endTime - startTime; - - if (code === 0) { - resolve({ - filename, - success: true, - duration, - code - }); - } else { - reject({ - filename, - success: false, - duration, - code, - error: `Process exited with code ${code}` - }); - } - }); - - child.on('error', (error) => { - const endTime = Date.now(); - const duration = endTime - startTime; - - reject({ - filename, - success: false, - duration, - error: error.message - }); - }); - }); + return new Promise((resolve, reject) => { + const filepath = join(__dirname, filename); + const startTime = Date.now(); + + console.log(`\n🏃 Running ${filename}...`); + console.log("=".repeat(60)); + + const child = spawn("node", [filepath], { + stdio: "inherit", + cwd: __dirname, + }); + + child.on("close", (code) => { + const endTime = Date.now(); + const duration = endTime - startTime; + + if (code === 0) { + resolve({ + filename, + success: true, + duration, + code, + }); + } else { + reject({ + filename, + success: false, + duration, + code, + error: `Process exited with code ${code}`, + }); + } + }); + + child.on("error", (error) => { + const endTime = Date.now(); + const duration = endTime - startTime; + + reject({ + filename, + success: false, + duration, + error: error.message, + }); + }); + }); } /** @@ -80,115 +80,117 @@ function runBenchmark(filename) { * @param {Array} results - Array of benchmark results */ function printSummary(results) { - const totalDuration = results.reduce((sum, result) => sum + result.duration, 0); - const successCount = results.filter(result => result.success).length; - - console.log('\n' + '='.repeat(80)); - console.log('📋 BENCHMARK SUMMARY'); - console.log('='.repeat(80)); - - console.log(`Total benchmarks: ${results.length}`); - console.log(`Successful: ${successCount}`); - console.log(`Failed: ${results.length - successCount}`); - console.log(`Total execution time: ${(totalDuration / 1000).toFixed(2)}s`); - - console.log('\nIndividual Results:'); - console.log('-'.repeat(50)); - - results.forEach(result => { - const status = result.success ? '✅ PASS' : '❌ FAIL'; - const duration = `${(result.duration / 1000).toFixed(2)}s`; - - console.log(`${status} ${result.filename.padEnd(25)} ${duration}`); - - if (!result.success) { - console.log(` Error: ${result.error}`); - } - }); - - if (successCount === results.length) { - console.log('\n🎉 All benchmarks completed successfully!'); - } else { - console.log(`\n⚠️ ${results.length - successCount} benchmark(s) failed.`); - } - - console.log('='.repeat(80)); + const totalDuration = results.reduce((sum, result) => sum + result.duration, 0); + const successCount = results.filter((result) => result.success).length; + + console.log("\n" + "=".repeat(80)); + console.log("📋 BENCHMARK SUMMARY"); + console.log("=".repeat(80)); + + console.log(`Total benchmarks: ${results.length}`); + console.log(`Successful: ${successCount}`); + console.log(`Failed: ${results.length - successCount}`); + console.log(`Total execution time: ${(totalDuration / 1000).toFixed(2)}s`); + + console.log("\nIndividual Results:"); + console.log("-".repeat(50)); + + results.forEach((result) => { + const status = result.success ? "✅ PASS" : "❌ FAIL"; + const duration = `${(result.duration / 1000).toFixed(2)}s`; + + console.log(`${status} ${result.filename.padEnd(25)} ${duration}`); + + if (!result.success) { + console.log(` Error: ${result.error}`); + } + }); + + if (successCount === results.length) { + console.log("\n🎉 All benchmarks completed successfully!"); + } else { + console.log(`\n⚠️ ${results.length - successCount} benchmark(s) failed.`); + } + + console.log("=".repeat(80)); } /** * Prints system information relevant to benchmarks */ function printSystemInfo() { - console.log('🔧 SYSTEM INFORMATION'); - console.log('='.repeat(50)); - console.log(`Node.js version: ${process.version}`); - console.log(`Platform: ${process.platform}`); - console.log(`Architecture: ${process.arch}`); - console.log(`CPU cores: ${cpus().length}`); - console.log(`Total memory: ${(totalmem() / 1024 / 1024 / 1024).toFixed(2)} GB`); - console.log(`Free memory: ${(freemem() / 1024 / 1024 / 1024).toFixed(2)} GB`); - - // Check if garbage collection is available - const gcAvailable = typeof global.gc === 'function'; - console.log(`GC available: ${gcAvailable ? 'Yes' : 'No'}`); - - if (!gcAvailable) { - console.log('💡 Tip: Run with --expose-gc for more accurate memory benchmarks'); - } - - console.log('='.repeat(50)); + console.log("🔧 SYSTEM INFORMATION"); + console.log("=".repeat(50)); + console.log(`Node.js version: ${process.version}`); + console.log(`Platform: ${process.platform}`); + console.log(`Architecture: ${process.arch}`); + console.log(`CPU cores: ${cpus().length}`); + console.log(`Total memory: ${(totalmem() / 1024 / 1024 / 1024).toFixed(2)} GB`); + console.log(`Free memory: ${(freemem() / 1024 / 1024 / 1024).toFixed(2)} GB`); + + // Check if garbage collection is available + const gcAvailable = typeof global.gc === "function"; + console.log(`GC available: ${gcAvailable ? "Yes" : "No"}`); + + if (!gcAvailable) { + console.log("💡 Tip: Run with --expose-gc for more accurate memory benchmarks"); + } + + console.log("=".repeat(50)); } /** * Main execution function */ async function main() { - console.log('🚀 FILESIZE.JS BENCHMARK SUITE'); - console.log('Starting comprehensive performance analysis...\n'); - - printSystemInfo(); - - const results = []; - const overallStartTime = Date.now(); - - for (const filename of BENCHMARK_FILES) { - try { - const result = await runBenchmark(filename); - results.push(result); - } catch (error) { - results.push(error); - console.error(`\n❌ Failed to run ${filename}: ${error.error}`); - } - - // Small delay between benchmarks to let system stabilize - await new Promise(resolve => setTimeout(resolve, 1000)); - } - - const overallEndTime = Date.now(); - const overallDuration = overallEndTime - overallStartTime; - - printSummary(results); - - console.log(`\n⏱️ Total benchmark suite execution time: ${(overallDuration / 1000).toFixed(2)}s`); - - // Exit with appropriate code - const hasFailures = results.some(result => !result.success); - process.exit(hasFailures ? 1 : 0); + console.log("🚀 FILESIZE.JS BENCHMARK SUITE"); + console.log("Starting comprehensive performance analysis...\n"); + + printSystemInfo(); + + const results = []; + const overallStartTime = Date.now(); + + for (const filename of BENCHMARK_FILES) { + try { + const result = await runBenchmark(filename); + results.push(result); + } catch (error) { + results.push(error); + console.error(`\n❌ Failed to run ${filename}: ${error.error}`); + } + + // Small delay between benchmarks to let system stabilize + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + + const overallEndTime = Date.now(); + const overallDuration = overallEndTime - overallStartTime; + + printSummary(results); + + console.log( + `\n⏱️ Total benchmark suite execution time: ${(overallDuration / 1000).toFixed(2)}s`, + ); + + // Exit with appropriate code + const hasFailures = results.some((result) => !result.success); + process.exit(hasFailures ? 1 : 0); } // Handle uncaught errors -process.on('unhandledRejection', (reason, promise) => { - console.error('❌ Unhandled Rejection at:', promise, 'reason:', reason); - process.exit(1); +process.on("unhandledRejection", (reason, promise) => { + console.error("❌ Unhandled Rejection at:", promise, "reason:", reason); + process.exit(1); }); -process.on('uncaughtException', (error) => { - console.error('❌ Uncaught Exception:', error); - process.exit(1); +process.on("uncaughtException", (error) => { + console.error("❌ Uncaught Exception:", error); + process.exit(1); }); // Run the main function -main().catch(error => { - console.error('❌ Fatal error:', error); - process.exit(1); -}); \ No newline at end of file +main().catch((error) => { + console.error("❌ Fatal error:", error); + process.exit(1); +}); diff --git a/benchmarks/options-benchmark.js b/benchmarks/options-benchmark.js index cc9c3fe..58dfadf 100644 --- a/benchmarks/options-benchmark.js +++ b/benchmarks/options-benchmark.js @@ -1,10 +1,10 @@ /** * Options Performance Benchmarks for filesize.js - * + * * Tests performance impact of different option combinations */ -import { filesize } from '../dist/filesize.js'; +import { filesize } from "../dist/filesize.js"; const ITERATIONS = 50000; const WARMUP_ITERATIONS = 5000; @@ -18,36 +18,36 @@ const TEST_SIZE = 1073741824; // 1GB * @returns {Object} Performance results */ function benchmark(testName, testFunction, iterations = ITERATIONS) { - // Warmup - for (let i = 0; i < WARMUP_ITERATIONS; i++) { - testFunction(); - } - - // Garbage collection if available - if (global.gc) { - global.gc(); - } - - // Actual benchmark - const startTime = process.hrtime.bigint(); - - for (let i = 0; i < iterations; i++) { - testFunction(); - } - - const endTime = process.hrtime.bigint(); - const totalTime = Number(endTime - startTime) / 1000000; // Convert to milliseconds - const avgTime = totalTime / iterations; - const opsPerSecond = 1000 / avgTime; - - return { - testName, - iterations, - totalTime: totalTime.toFixed(2), - avgTime: avgTime.toFixed(6), - opsPerSecond: Math.round(opsPerSecond), - relativeSpeed: 1 // Will be calculated later - }; + // Warmup + for (let i = 0; i < WARMUP_ITERATIONS; i++) { + testFunction(); + } + + // Garbage collection if available + if (global.gc) { + global.gc(); + } + + // Actual benchmark + const startTime = process.hrtime.bigint(); + + for (let i = 0; i < iterations; i++) { + testFunction(); + } + + const endTime = process.hrtime.bigint(); + const totalTime = Number(endTime - startTime) / 1000000; // Convert to milliseconds + const avgTime = totalTime / iterations; + const opsPerSecond = 1000 / avgTime; + + return { + testName, + iterations, + totalTime: totalTime.toFixed(2), + avgTime: avgTime.toFixed(6), + opsPerSecond: Math.round(opsPerSecond), + relativeSpeed: 1, // Will be calculated later + }; } /** @@ -55,154 +55,120 @@ function benchmark(testName, testFunction, iterations = ITERATIONS) { * @param {Array} results - Array of benchmark results */ function printResults(results) { - // Calculate relative speeds (compared to baseline) - const baseline = results[0]; - results.forEach(result => { - result.relativeSpeed = (baseline.opsPerSecond / result.opsPerSecond).toFixed(2); - }); - - console.log('\n📊 Options Performance Benchmark Results'); - console.log('=' .repeat(90)); - console.log('Test Name'.padEnd(30) + 'Ops/sec'.padEnd(12) + 'Avg (ms)'.padEnd(12) + 'Relative'.padEnd(10) + 'Notes'); - console.log('-'.repeat(90)); - - results.forEach((result, index) => { - const note = index === 0 ? '(baseline)' : `${result.relativeSpeed}x slower`; - console.log( - result.testName.padEnd(30) + - result.opsPerSecond.toLocaleString().padEnd(12) + - result.avgTime.padEnd(12) + - result.relativeSpeed.padEnd(10) + - note - ); - }); - console.log('=' .repeat(90)); + // Calculate relative speeds (compared to baseline) + const baseline = results[0]; + results.forEach((result) => { + result.relativeSpeed = (baseline.opsPerSecond / result.opsPerSecond).toFixed(2); + }); + + console.log("\n📊 Options Performance Benchmark Results"); + console.log("=".repeat(90)); + console.log( + "Test Name".padEnd(30) + + "Ops/sec".padEnd(12) + + "Avg (ms)".padEnd(12) + + "Relative".padEnd(10) + + "Notes", + ); + console.log("-".repeat(90)); + + results.forEach((result, index) => { + const note = index === 0 ? "(baseline)" : `${result.relativeSpeed}x slower`; + console.log( + result.testName.padEnd(30) + + result.opsPerSecond.toLocaleString().padEnd(12) + + result.avgTime.padEnd(12) + + result.relativeSpeed.padEnd(10) + + note, + ); + }); + console.log("=".repeat(90)); } -console.log('🚀 Starting Options Performance Benchmarks...\n'); +console.log("🚀 Starting Options Performance Benchmarks...\n"); const results = []; // Baseline test -results.push(benchmark( - 'Default options', - () => filesize(TEST_SIZE) -)); +results.push(benchmark("Default options", () => filesize(TEST_SIZE))); // Test individual options -results.push(benchmark( - 'bits=true', - () => filesize(TEST_SIZE, { bits: true }) -)); - -results.push(benchmark( - 'pad=true', - () => filesize(TEST_SIZE, { pad: true }) -)); - -results.push(benchmark( - 'base=2', - () => filesize(TEST_SIZE, { base: 2 }) -)); - -results.push(benchmark( - 'round=4', - () => filesize(TEST_SIZE, { round: 4 }) -)); - -results.push(benchmark( - 'locale=true', - () => filesize(TEST_SIZE, { locale: true }) -)); - -results.push(benchmark( - 'locale="en-US"', - () => filesize(TEST_SIZE, { locale: 'en-US' }) -)); - -results.push(benchmark( - 'separator=","', - () => filesize(TEST_SIZE, { separator: ',' }) -)); - -results.push(benchmark( - 'standard="iec"', - () => filesize(TEST_SIZE, { standard: 'iec' }) -)); - -results.push(benchmark( - 'standard="jedec"', - () => filesize(TEST_SIZE, { standard: 'jedec' }) -)); - -results.push(benchmark( - 'output="array"', - () => filesize(TEST_SIZE, { output: 'array' }) -)); - -results.push(benchmark( - 'output="object"', - () => filesize(TEST_SIZE, { output: 'object' }) -)); - -results.push(benchmark( - 'fullform=true', - () => filesize(TEST_SIZE, { fullform: true }) -)); - -results.push(benchmark( - 'precision=3', - () => filesize(TEST_SIZE, { precision: 3 }) -)); - -results.push(benchmark( - 'roundingMethod="ceil"', - () => filesize(TEST_SIZE, { roundingMethod: 'ceil' }) -)); +results.push(benchmark("bits=true", () => filesize(TEST_SIZE, { bits: true }))); + +results.push(benchmark("pad=true", () => filesize(TEST_SIZE, { pad: true }))); + +results.push(benchmark("base=2", () => filesize(TEST_SIZE, { base: 2 }))); + +results.push(benchmark("round=4", () => filesize(TEST_SIZE, { round: 4 }))); + +results.push(benchmark("locale=true", () => filesize(TEST_SIZE, { locale: true }))); + +results.push(benchmark('locale="en-US"', () => filesize(TEST_SIZE, { locale: "en-US" }))); + +results.push(benchmark('separator=","', () => filesize(TEST_SIZE, { separator: "," }))); + +results.push(benchmark('standard="iec"', () => filesize(TEST_SIZE, { standard: "iec" }))); + +results.push(benchmark('standard="jedec"', () => filesize(TEST_SIZE, { standard: "jedec" }))); + +results.push(benchmark('output="array"', () => filesize(TEST_SIZE, { output: "array" }))); + +results.push(benchmark('output="object"', () => filesize(TEST_SIZE, { output: "object" }))); + +results.push(benchmark("fullform=true", () => filesize(TEST_SIZE, { fullform: true }))); + +results.push(benchmark("precision=3", () => filesize(TEST_SIZE, { precision: 3 }))); + +results.push( + benchmark('roundingMethod="ceil"', () => filesize(TEST_SIZE, { roundingMethod: "ceil" })), +); // Test complex option combinations -results.push(benchmark( - 'Complex combo 1', - () => filesize(TEST_SIZE, { - bits: true, - standard: 'iec', - round: 3, - pad: true - }) -)); - -results.push(benchmark( - 'Complex combo 2', - () => filesize(TEST_SIZE, { - fullform: true, - locale: 'en-US', - precision: 2, - output: 'object' - }) -)); - -results.push(benchmark( - 'All options', - () => filesize(TEST_SIZE, { - bits: true, - pad: true, - base: 2, - round: 3, - locale: 'en-US', - separator: ',', - spacer: ' ', - standard: 'iec', - output: 'object', - fullform: true, - precision: 2, - roundingMethod: 'ceil' - }) -)); +results.push( + benchmark("Complex combo 1", () => + filesize(TEST_SIZE, { + bits: true, + standard: "iec", + round: 3, + pad: true, + }), + ), +); + +results.push( + benchmark("Complex combo 2", () => + filesize(TEST_SIZE, { + fullform: true, + locale: "en-US", + precision: 2, + output: "object", + }), + ), +); + +results.push( + benchmark("All options", () => + filesize(TEST_SIZE, { + bits: true, + pad: true, + base: 2, + round: 3, + locale: "en-US", + separator: ",", + spacer: " ", + standard: "iec", + output: "object", + fullform: true, + precision: 2, + roundingMethod: "ceil", + }), + ), +); printResults(results); -console.log('\n💡 Performance Insights:'); -console.log(' • Locale formatting has significant overhead'); -console.log(' • Object output is slightly slower than string'); -console.log(' • Fullform generation adds minimal overhead'); -console.log(' • Multiple options compound the performance impact'); \ No newline at end of file +console.log("\n💡 Performance Insights:"); +console.log(" • Locale formatting has significant overhead"); +console.log(" • Object output is slightly slower than string"); +console.log(" • Fullform generation adds minimal overhead"); +console.log(" • Multiple options compound the performance impact"); diff --git a/benchmarks/partial-benchmark.js b/benchmarks/partial-benchmark.js index ee949ec..211027a 100644 --- a/benchmarks/partial-benchmark.js +++ b/benchmarks/partial-benchmark.js @@ -1,10 +1,10 @@ /** * Partial Function Benchmarks for filesize.js - * + * * Tests performance of the partial function and compares it with direct calls */ -import { filesize, partial } from '../dist/filesize.js'; +import { filesize, partial } from "../dist/filesize.js"; const ITERATIONS = 100000; const WARMUP_ITERATIONS = 10000; @@ -17,34 +17,34 @@ const WARMUP_ITERATIONS = 10000; * @returns {Object} Performance results */ function benchmark(testName, testFunction, iterations = ITERATIONS) { - // Warmup - for (let i = 0; i < WARMUP_ITERATIONS; i++) { - testFunction(); - } - - if (global.gc) { - global.gc(); - } - - const startTime = process.hrtime.bigint(); - - for (let i = 0; i < iterations; i++) { - testFunction(); - } - - const endTime = process.hrtime.bigint(); - const totalTime = Number(endTime - startTime) / 1000000; - const avgTime = totalTime / iterations; - const opsPerSecond = Math.round(1000 / avgTime); - - return { - testName, - iterations, - totalTime: totalTime.toFixed(2), - avgTime: avgTime.toFixed(6), - opsPerSecond, - relativeSpeed: 1 - }; + // Warmup + for (let i = 0; i < WARMUP_ITERATIONS; i++) { + testFunction(); + } + + if (global.gc) { + global.gc(); + } + + const startTime = process.hrtime.bigint(); + + for (let i = 0; i < iterations; i++) { + testFunction(); + } + + const endTime = process.hrtime.bigint(); + const totalTime = Number(endTime - startTime) / 1000000; + const avgTime = totalTime / iterations; + const opsPerSecond = Math.round(1000 / avgTime); + + return { + testName, + iterations, + totalTime: totalTime.toFixed(2), + avgTime: avgTime.toFixed(6), + opsPerSecond, + relativeSpeed: 1, + }; } /** @@ -52,158 +52,134 @@ function benchmark(testName, testFunction, iterations = ITERATIONS) { * @param {Array} results - Array of benchmark results */ function printResults(results) { - console.log('\n📊 Partial Function Benchmark Results'); - console.log('=' .repeat(85)); - console.log('Test Name'.padEnd(30) + 'Ops/sec'.padEnd(15) + 'Avg (ms)'.padEnd(12) + 'vs Direct'.padEnd(12) + 'Notes'); - console.log('-'.repeat(85)); - - // Find direct call baseline for comparison - const directBaseline = results.find(r => r.testName.includes('Direct call')); - - results.forEach(result => { - let comparison = ''; - let note = ''; - - if (directBaseline && result !== directBaseline) { - const ratio = directBaseline.opsPerSecond / result.opsPerSecond; - if (ratio > 1) { - comparison = `${ratio.toFixed(2)}x slower`; - note = ratio > 2 ? '⚠️ Significant overhead' : '✓ Acceptable overhead'; - } else { - comparison = `${(1/ratio).toFixed(2)}x faster`; - note = '🚀 Faster than direct'; - } - } else if (result === directBaseline) { - comparison = 'baseline'; - note = '📊 Reference'; - } - - console.log( - result.testName.padEnd(30) + - result.opsPerSecond.toLocaleString().padEnd(15) + - result.avgTime.padEnd(12) + - comparison.padEnd(12) + - note - ); - }); - console.log('=' .repeat(85)); + console.log("\n📊 Partial Function Benchmark Results"); + console.log("=".repeat(85)); + console.log( + "Test Name".padEnd(30) + + "Ops/sec".padEnd(15) + + "Avg (ms)".padEnd(12) + + "vs Direct".padEnd(12) + + "Notes", + ); + console.log("-".repeat(85)); + + // Find direct call baseline for comparison + const directBaseline = results.find((r) => r.testName.includes("Direct call")); + + results.forEach((result) => { + let comparison = ""; + let note = ""; + + if (directBaseline && result !== directBaseline) { + const ratio = directBaseline.opsPerSecond / result.opsPerSecond; + if (ratio > 1) { + comparison = `${ratio.toFixed(2)}x slower`; + note = ratio > 2 ? "⚠️ Significant overhead" : "✓ Acceptable overhead"; + } else { + comparison = `${(1 / ratio).toFixed(2)}x faster`; + note = "🚀 Faster than direct"; + } + } else if (result === directBaseline) { + comparison = "baseline"; + note = "📊 Reference"; + } + + console.log( + result.testName.padEnd(30) + + result.opsPerSecond.toLocaleString().padEnd(15) + + result.avgTime.padEnd(12) + + comparison.padEnd(12) + + note, + ); + }); + console.log("=".repeat(85)); } -console.log('🚀 Starting Partial Function Benchmarks...\n'); +console.log("🚀 Starting Partial Function Benchmarks...\n"); const testValue = 1073741824; // 1GB const results = []; // Baseline: Direct filesize calls -results.push(benchmark( - 'Direct call (baseline)', - () => filesize(testValue) -)); +results.push(benchmark("Direct call (baseline)", () => filesize(testValue))); -results.push(benchmark( - 'Direct call w/ options', - () => filesize(testValue, { round: 2, standard: 'iec' }) -)); +results.push( + benchmark("Direct call w/ options", () => filesize(testValue, { round: 2, standard: "iec" })), +); // Test basic partial functions const simplePartial = partial(); -results.push(benchmark( - 'Simple partial()', - () => simplePartial(testValue) -)); +results.push(benchmark("Simple partial()", () => simplePartial(testValue))); -const partialWithOptions = partial({ round: 2, standard: 'iec' }); -results.push(benchmark( - 'Partial w/ options', - () => partialWithOptions(testValue) -)); +const partialWithOptions = partial({ round: 2, standard: "iec" }); +results.push(benchmark("Partial w/ options", () => partialWithOptions(testValue))); // Test various partial configurations const bitsPartial = partial({ bits: true }); -results.push(benchmark( - 'Partial bits=true', - () => bitsPartial(testValue) -)); - -const iecPartial = partial({ standard: 'iec', round: 3 }); -results.push(benchmark( - 'Partial IEC standard', - () => iecPartial(testValue) -)); - -const objectPartial = partial({ output: 'object' }); -results.push(benchmark( - 'Partial object output', - () => objectPartial(testValue) -)); - -const fullformPartial = partial({ fullform: true, spacer: '_' }); -results.push(benchmark( - 'Partial fullform', - () => fullformPartial(testValue) -)); - -const localePartial = partial({ locale: 'en-US', round: 1 }); -results.push(benchmark( - 'Partial w/ locale', - () => localePartial(testValue) -)); +results.push(benchmark("Partial bits=true", () => bitsPartial(testValue))); + +const iecPartial = partial({ standard: "iec", round: 3 }); +results.push(benchmark("Partial IEC standard", () => iecPartial(testValue))); + +const objectPartial = partial({ output: "object" }); +results.push(benchmark("Partial object output", () => objectPartial(testValue))); + +const fullformPartial = partial({ fullform: true, spacer: "_" }); +results.push(benchmark("Partial fullform", () => fullformPartial(testValue))); + +const localePartial = partial({ locale: "en-US", round: 1 }); +results.push(benchmark("Partial w/ locale", () => localePartial(testValue))); // Test complex partial configurations const complexPartial = partial({ - bits: true, - standard: 'iec', - round: 3, - pad: true, - output: 'object' + bits: true, + standard: "iec", + round: 3, + pad: true, + output: "object", }); -results.push(benchmark( - 'Complex partial', - () => complexPartial(testValue) -)); +results.push(benchmark("Complex partial", () => complexPartial(testValue))); // Test partial creation overhead -results.push(benchmark( - 'Partial creation', - () => { - const newPartial = partial({ round: 2 }); - return newPartial(testValue); - } -)); +results.push( + benchmark("Partial creation", () => { + const newPartial = partial({ round: 2 }); + return newPartial(testValue); + }), +); // Test multiple partial instances const partials = [ - partial({ round: 0 }), - partial({ round: 1 }), - partial({ round: 2 }), - partial({ round: 3 }), - partial({ round: 4 }) + partial({ round: 0 }), + partial({ round: 1 }), + partial({ round: 2 }), + partial({ round: 3 }), + partial({ round: 4 }), ]; let partialIndex = 0; -results.push(benchmark( - 'Multiple partials', - () => { - const p = partials[partialIndex % partials.length]; - partialIndex++; - return p(testValue); - } -)); +results.push( + benchmark("Multiple partials", () => { + const p = partials[partialIndex % partials.length]; + partialIndex++; + return p(testValue); + }), +); printResults(results); // Functional programming patterns test -console.log('\n🔧 Functional Programming Patterns:'); +console.log("\n🔧 Functional Programming Patterns:"); const sizes = [1024, 1048576, 1073741824, 1099511627776]; const formatters = [ - partial({ standard: 'iec', round: 1 }), - partial({ bits: true, round: 2 }), - partial({ fullform: true }), - partial({ output: 'object' }) + partial({ standard: "iec", round: 1 }), + partial({ bits: true, round: 2 }), + partial({ fullform: true }), + partial({ output: "object" }), ]; -console.log('\nTesting map operations:'); +console.log("\nTesting map operations:"); const mapStart = process.hrtime.bigint(); // Using partial with map @@ -213,36 +189,36 @@ const mapEnd = process.hrtime.bigint(); const mapTime = Number(mapEnd - mapStart) / 1000000; console.log(` Map with partial: ${mapTime.toFixed(3)}ms`); -console.log(` Results: ${formattedSizes.join(', ')}`); +console.log(` Results: ${formattedSizes.join(", ")}`); // Chain operations test -console.log('\nTesting function chaining:'); +console.log("\nTesting function chaining:"); const chainStart = process.hrtime.bigint(); const chainResult = sizes - .filter(size => size > 1024) - .map(formatters[1]) - .slice(0, 2); + .filter((size) => size > 1024) + .map(formatters[1]) + .slice(0, 2); const chainEnd = process.hrtime.bigint(); const chainTime = Number(chainEnd - chainStart) / 1000000; console.log(` Chain operations: ${chainTime.toFixed(3)}ms`); -console.log(` Results: ${chainResult.join(', ')}`); +console.log(` Results: ${chainResult.join(", ")}`); // Currying comparison -console.log('\nCurrying vs Direct Calls (1000 operations):'); +console.log("\nCurrying vs Direct Calls (1000 operations):"); const curryStart = process.hrtime.bigint(); for (let i = 0; i < 1000; i++) { - formatters[0](testValue); + formatters[0](testValue); } const curryEnd = process.hrtime.bigint(); const curryTime = Number(curryEnd - curryStart) / 1000000; const directStart = process.hrtime.bigint(); for (let i = 0; i < 1000; i++) { - filesize(testValue, { standard: 'iec', round: 1 }); + filesize(testValue, { standard: "iec", round: 1 }); } const directEnd = process.hrtime.bigint(); const directTime = Number(directEnd - directStart) / 1000000; @@ -251,8 +227,8 @@ console.log(` Curried calls: ${curryTime.toFixed(3)}ms`); console.log(` Direct calls: ${directTime.toFixed(3)}ms`); console.log(` Overhead: ${((curryTime / directTime - 1) * 100).toFixed(1)}%`); -console.log('\n💡 Partial Function Insights:'); -console.log(' • Partial functions add minimal overhead for reused configurations'); -console.log(' • Creation cost is amortized over multiple uses'); -console.log(' • Excellent for functional programming patterns'); -console.log(' • Best suited for repeated operations with same options'); \ No newline at end of file +console.log("\n💡 Partial Function Insights:"); +console.log(" • Partial functions add minimal overhead for reused configurations"); +console.log(" • Creation cost is amortized over multiple uses"); +console.log(" • Excellent for functional programming patterns"); +console.log(" • Best suited for repeated operations with same options"); diff --git a/benchmarks/stress-test.js b/benchmarks/stress-test.js index 1671510..8f5827c 100644 --- a/benchmarks/stress-test.js +++ b/benchmarks/stress-test.js @@ -1,10 +1,10 @@ /** * Stress Test Benchmarks for filesize.js - * + * * Tests performance with edge cases, extreme values, and stress conditions */ -import { filesize } from '../dist/filesize.js'; +import { filesize } from "../dist/filesize.js"; const STRESS_ITERATIONS = 10000; const WARMUP_ITERATIONS = 1000; @@ -17,48 +17,48 @@ const WARMUP_ITERATIONS = 1000; * @returns {Object} Performance results */ function benchmark(testName, testFunction, iterations = STRESS_ITERATIONS) { - // Warmup - for (let i = 0; i < WARMUP_ITERATIONS; i++) { - try { - testFunction(); - } catch (e) { - // Ignore warmup errors - } - } - - if (global.gc) { - global.gc(); - } - - let successCount = 0; - let errorCount = 0; - - const startTime = process.hrtime.bigint(); - - for (let i = 0; i < iterations; i++) { - try { - testFunction(); - successCount++; - } catch (e) { - errorCount++; - } - } - - const endTime = process.hrtime.bigint(); - const totalTime = Number(endTime - startTime) / 1000000; - const avgTime = totalTime / iterations; - const opsPerSecond = Math.round(1000 / avgTime); - - return { - testName, - iterations, - successCount, - errorCount, - totalTime: totalTime.toFixed(2), - avgTime: avgTime.toFixed(6), - opsPerSecond, - successRate: ((successCount / iterations) * 100).toFixed(1) - }; + // Warmup + for (let i = 0; i < WARMUP_ITERATIONS; i++) { + try { + testFunction(); + } catch { + // Ignore warmup errors + } + } + + if (global.gc) { + global.gc(); + } + + let successCount = 0; + let errorCount = 0; + + const startTime = process.hrtime.bigint(); + + for (let i = 0; i < iterations; i++) { + try { + testFunction(); + successCount++; + } catch { + errorCount++; + } + } + + const endTime = process.hrtime.bigint(); + const totalTime = Number(endTime - startTime) / 1000000; + const avgTime = totalTime / iterations; + const opsPerSecond = Math.round(1000 / avgTime); + + return { + testName, + iterations, + successCount, + errorCount, + totalTime: totalTime.toFixed(2), + avgTime: avgTime.toFixed(6), + opsPerSecond, + successRate: ((successCount / iterations) * 100).toFixed(1), + }; } /** @@ -67,20 +67,32 @@ function benchmark(testName, testFunction, iterations = STRESS_ITERATIONS) { * @returns {Array} Array of random values */ function generateRandomValues(count) { - const values = []; - for (let i = 0; i < count; i++) { - // Generate various types of values - const type = Math.floor(Math.random() * 6); - switch (type) { - case 0: values.push(0); break; - case 1: values.push(Math.random() * 1024); break; - case 2: values.push(Math.random() * 1048576); break; - case 3: values.push(Math.random() * 1073741824); break; - case 4: values.push(Math.random() * Number.MAX_SAFE_INTEGER); break; - case 5: values.push(-Math.random() * 1048576); break; // Negative values - } - } - return values; + const values = []; + for (let i = 0; i < count; i++) { + // Generate various types of values + const type = Math.floor(Math.random() * 6); + switch (type) { + case 0: + values.push(0); + break; + case 1: + values.push(Math.random() * 1024); + break; + case 2: + values.push(Math.random() * 1048576); + break; + case 3: + values.push(Math.random() * 1073741824); + break; + case 4: + values.push(Math.random() * Number.MAX_SAFE_INTEGER); + break; + case 5: + values.push(-Math.random() * 1048576); + break; // Negative values + } + } + return values; } /** @@ -88,166 +100,165 @@ function generateRandomValues(count) { * @param {Array} results - Array of benchmark results */ function printResults(results) { - console.log('\n📊 Stress Test Benchmark Results'); - console.log('=' .repeat(100)); - console.log('Test Name'.padEnd(25) + 'Ops/sec'.padEnd(12) + 'Success%'.padEnd(10) + 'Errors'.padEnd(8) + 'Avg (ms)'.padEnd(12) + 'Notes'); - console.log('-'.repeat(100)); - - results.forEach(result => { - const errorNote = result.errorCount > 0 ? `${result.errorCount} errors` : 'No errors'; - console.log( - result.testName.padEnd(25) + - result.opsPerSecond.toLocaleString().padEnd(12) + - `${result.successRate}%`.padEnd(10) + - result.errorCount.toString().padEnd(8) + - result.avgTime.padEnd(12) + - errorNote - ); - }); - console.log('=' .repeat(100)); + console.log("\n📊 Stress Test Benchmark Results"); + console.log("=".repeat(100)); + console.log( + "Test Name".padEnd(25) + + "Ops/sec".padEnd(12) + + "Success%".padEnd(10) + + "Errors".padEnd(8) + + "Avg (ms)".padEnd(12) + + "Notes", + ); + console.log("-".repeat(100)); + + results.forEach((result) => { + const errorNote = result.errorCount > 0 ? `${result.errorCount} errors` : "No errors"; + console.log( + result.testName.padEnd(25) + + result.opsPerSecond.toLocaleString().padEnd(12) + + `${result.successRate}%`.padEnd(10) + + result.errorCount.toString().padEnd(8) + + result.avgTime.padEnd(12) + + errorNote, + ); + }); + console.log("=".repeat(100)); } -console.log('🚀 Starting Stress Test Benchmarks...\n'); +console.log("🚀 Starting Stress Test Benchmarks...\n"); const results = []; // Edge case values const edgeCases = [ - 0, - 1, - -1, - Number.MIN_VALUE, - Number.MAX_VALUE, - Number.MAX_SAFE_INTEGER, - Number.MIN_SAFE_INTEGER, - Infinity, - -Infinity, - NaN + 0, + 1, + -1, + Number.MIN_VALUE, + Number.MAX_VALUE, + Number.MAX_SAFE_INTEGER, + Number.MIN_SAFE_INTEGER, + Infinity, + -Infinity, + NaN, ]; // Test edge cases let edgeCaseIndex = 0; -results.push(benchmark( - 'Edge cases', - () => { - const value = edgeCases[edgeCaseIndex % edgeCases.length]; - edgeCaseIndex++; - return filesize(value); - } -)); +results.push( + benchmark("Edge cases", () => { + const value = edgeCases[edgeCaseIndex % edgeCases.length]; + edgeCaseIndex++; + return filesize(value); + }), +); // Test very large numbers -results.push(benchmark( - 'Very large numbers', - () => filesize(Math.random() * Number.MAX_SAFE_INTEGER) -)); +results.push( + benchmark("Very large numbers", () => filesize(Math.random() * Number.MAX_SAFE_INTEGER)), +); // Test very small numbers -results.push(benchmark( - 'Very small numbers', - () => filesize(Math.random() * 0.001) -)); +results.push(benchmark("Very small numbers", () => filesize(Math.random() * 0.001))); // Test negative numbers -results.push(benchmark( - 'Negative numbers', - () => filesize(-Math.random() * 1048576) -)); +results.push(benchmark("Negative numbers", () => filesize(-Math.random() * 1048576))); // Test with random options const optionSets = [ - { bits: true, round: Math.floor(Math.random() * 10) }, - { standard: Math.random() > 0.5 ? 'iec' : 'jedec', pad: true }, - { output: ['string', 'array', 'object'][Math.floor(Math.random() * 3)] }, - { locale: Math.random() > 0.5 ? 'en-US' : true, precision: Math.floor(Math.random() * 5) }, - { fullform: true, spacer: Math.random() > 0.5 ? ' ' : '_' } + { bits: true, round: Math.floor(Math.random() * 10) }, + { standard: Math.random() > 0.5 ? "iec" : "jedec", pad: true }, + { output: ["string", "array", "object"][Math.floor(Math.random() * 3)] }, + { locale: Math.random() > 0.5 ? "en-US" : true, precision: Math.floor(Math.random() * 5) }, + { fullform: true, spacer: Math.random() > 0.5 ? " " : "_" }, ]; let optionIndex = 0; -results.push(benchmark( - 'Random options', - () => { - const options = optionSets[optionIndex % optionSets.length]; - optionIndex++; - return filesize(Math.random() * 1073741824, options); - } -)); +results.push( + benchmark("Random options", () => { + const options = optionSets[optionIndex % optionSets.length]; + optionIndex++; + return filesize(Math.random() * 1073741824, options); + }), +); // Test rapid consecutive calls const rapidValues = generateRandomValues(1000); let rapidIndex = 0; -results.push(benchmark( - 'Rapid consecutive', - () => { - const value = rapidValues[rapidIndex % rapidValues.length]; - rapidIndex++; - return filesize(value); - }, - STRESS_ITERATIONS * 2 // Double the iterations for this test -)); +results.push( + benchmark( + "Rapid consecutive", + () => { + const value = rapidValues[rapidIndex % rapidValues.length]; + rapidIndex++; + return filesize(value); + }, + STRESS_ITERATIONS * 2, // Double the iterations for this test + ), +); // Test with BigInt values (if supported) -results.push(benchmark( - 'BigInt values', - () => { - const bigIntValue = BigInt(Math.floor(Math.random() * 1000000000000)); - return filesize(bigIntValue); - } -)); +results.push( + benchmark("BigInt values", () => { + const bigIntValue = BigInt(Math.floor(Math.random() * 1000000000000)); + return filesize(bigIntValue); + }), +); // Memory pressure test -results.push(benchmark( - 'Memory pressure', - () => { - // Create some memory pressure - const tempArray = new Array(1000).fill(0).map(() => Math.random()); - const result = filesize(Math.random() * 1073741824, { - output: 'object', - fullform: true, - locale: 'en-US' - }); - tempArray.length = 0; // Clear the array - return result; - }, - STRESS_ITERATIONS / 10 // Reduce iterations for memory test -)); +results.push( + benchmark( + "Memory pressure", + () => { + // Create some memory pressure + const tempArray = Array.from({ length: 1000 }, () => Math.random()); + const result = filesize(Math.random() * 1073741824, { + output: "object", + fullform: true, + locale: "en-US", + }); + tempArray.length = 0; // Clear the array + return result; + }, + STRESS_ITERATIONS / 10, // Reduce iterations for memory test + ), +); // Test error handling performance -results.push(benchmark( - 'Error conditions', - () => { - const invalidInputs = ['invalid', null, undefined, {}, []]; - const input = invalidInputs[Math.floor(Math.random() * invalidInputs.length)]; - return filesize(input); - } -)); +results.push( + benchmark("Error conditions", () => { + const invalidInputs = ["invalid", null, undefined, {}, []]; + const input = invalidInputs[Math.floor(Math.random() * invalidInputs.length)]; + return filesize(input); + }), +); printResults(results); // Performance consistency test -console.log('\n🔍 Performance Consistency Test (10 runs):'); +console.log("\n🔍 Performance Consistency Test (10 runs):"); const consistencyResults = []; for (let i = 0; i < 10; i++) { - const result = benchmark( - `Run ${i + 1}`, - () => filesize(1073741824), - 1000 - ); - consistencyResults.push(result.opsPerSecond); + const result = benchmark(`Run ${i + 1}`, () => filesize(1073741824), 1000); + consistencyResults.push(result.opsPerSecond); } const avgOps = consistencyResults.reduce((a, b) => a + b, 0) / consistencyResults.length; const minOps = Math.min(...consistencyResults); const maxOps = Math.max(...consistencyResults); -const variance = Math.sqrt(consistencyResults.reduce((acc, val) => acc + Math.pow(val - avgOps, 2), 0) / consistencyResults.length); +const variance = Math.sqrt( + consistencyResults.reduce((acc, val) => acc + Math.pow(val - avgOps, 2), 0) / + consistencyResults.length, +); console.log(` Average: ${Math.round(avgOps).toLocaleString()} ops/sec`); console.log(` Range: ${minOps.toLocaleString()} - ${maxOps.toLocaleString()} ops/sec`); console.log(` Variance: ${Math.round(variance).toLocaleString()}`); console.log(` Consistency: ${((1 - variance / avgOps) * 100).toFixed(1)}%`); -console.log('\n💡 Stress Test Insights:'); -console.log(' • Edge cases and invalid inputs are handled gracefully'); -console.log(' • Performance remains consistent under various conditions'); -console.log(' • BigInt support works efficiently'); -console.log(' • Memory pressure has minimal impact on performance'); \ No newline at end of file +console.log("\n💡 Stress Test Insights:"); +console.log(" • Edge cases and invalid inputs are handled gracefully"); +console.log(" • Performance remains consistent under various conditions"); +console.log(" • BigInt support works efficiently"); +console.log(" • Memory pressure has minimal impact on performance"); diff --git a/dist/filesize.cjs b/dist/filesize.cjs index 0bf837a..0957115 100644 --- a/dist/filesize.cjs +++ b/dist/filesize.cjs @@ -1,7 +1,7 @@ /** * filesize * - * @copyright 2025 Jason Mulligan + * @copyright 2026 Jason Mulligan * @license BSD-3-Clause * @version 11.0.13 */ @@ -47,17 +47,17 @@ const STRINGS = { symbol: { iec: { bits: ["bit", "Kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"], - bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] + bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], }, jedec: { bits: ["bit", "Kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"], - bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] - } + bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], + }, }, fullform: { iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"], - jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"] - } + jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"], + }, }; // Pre-computed lookup tables for performance optimization @@ -70,7 +70,7 @@ const BINARY_POWERS = [ 1125899906842624, // 2^50 1152921504606846976, // 2^60 1180591620717411303424, // 2^70 - 1208925819614629174706176 // 2^80 + 1208925819614629174706176, // 2^80 ]; const DECIMAL_POWERS = [ @@ -82,7 +82,7 @@ const DECIMAL_POWERS = [ 1000000000000000, // 10^15 1000000000000000000, // 10^18 1000000000000000000000, // 10^21 - 1000000000000000000000000 // 10^24 + 1000000000000000000000000, // 10^24 ]; // Pre-computed log values for faster exponent calculation @@ -91,9 +91,9 @@ const LOG_10_1000 = Math.log(1000); // Cached configuration lookup for better performance const STANDARD_CONFIGS = { - [SI]: {isDecimal: true, ceil: 1000, actualStandard: JEDEC}, - [IEC]: {isDecimal: false, ceil: 1024, actualStandard: IEC}, - [JEDEC]: {isDecimal: false, ceil: 1024, actualStandard: JEDEC} + [SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC }, + [IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC }, + [JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC }, }; /** @@ -102,7 +102,7 @@ const STANDARD_CONFIGS = { * @param {number} base - Base number * @returns {Object} Configuration object */ -function getBaseConfiguration (standard, base) { +function getBaseConfiguration(standard, base) { // Use cached lookup table for better performance if (STANDARD_CONFIGS[standard]) { return STANDARD_CONFIGS[standard]; @@ -110,11 +110,11 @@ function getBaseConfiguration (standard, base) { // Base override if (base === 2) { - return {isDecimal: false, ceil: 1024, actualStandard: IEC}; + return { isDecimal: false, ceil: 1024, actualStandard: IEC }; } // Default - return {isDecimal: true, ceil: 1000, actualStandard: JEDEC}; + return { isDecimal: true, ceil: 1000, actualStandard: JEDEC }; } /** @@ -127,34 +127,53 @@ function getBaseConfiguration (standard, base) { * @param {Array} fullforms - Custom full forms * @param {string} output - Output format * @param {string} spacer - Spacer character + * @param {string} [symbol] - Symbol to use (defaults based on bits/standard) * @returns {string|Array|Object|number} Formatted result */ -function handleZeroValue (precision, actualStandard, bits, symbols, full, fullforms, output, spacer) { - const result = []; - result[0] = precision > 0 ? (0).toPrecision(precision) : 0; - const u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0]; +function handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + symbol, +) { + const value = precision > 0 ? (0).toPrecision(precision) : 0; if (output === EXPONENT) { return 0; } + // Set default symbol if not provided + if (!symbol) { + symbol = bits + ? STRINGS.symbol[actualStandard].bits[0] + : STRINGS.symbol[actualStandard].bytes[0]; + } + // Apply symbol customization - if (symbols[result[1]]) { - result[1] = symbols[result[1]]; + if (symbols[symbol]) { + symbol = symbols[symbol]; } // Apply full form if (full) { - result[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); + symbol = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); } // Return in requested format - return output === ARRAY ? result : output === OBJECT ? { - value: result[0], - symbol: result[1], - exponent: 0, - unit: u - } : result.join(spacer); + if (output === ARRAY) { + return [value, symbol]; + } + + if (output === OBJECT) { + return { value, symbol, exponent: 0, unit: symbol }; + } + + return value + spacer + symbol; } /** @@ -164,22 +183,23 @@ function handleZeroValue (precision, actualStandard, bits, symbols, full, fullfo * @param {boolean} isDecimal - Whether to use decimal powers * @param {boolean} bits - Whether to calculate bits * @param {number} ceil - Ceiling value for auto-increment - * @returns {Object} Object with val and e properties + * @param {boolean} autoExponent - Whether exponent is auto (-1 or NaN) + * @returns {Object} Object with result and e properties */ -function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { +function calculateOptimizedValue(num, e, isDecimal, bits, ceil, autoExponent = true) { const d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e]; let result = num / d; if (bits) { result *= 8; - // Handle auto-increment for bits - if (result >= ceil && e < 8) { + // Handle auto-increment for bits (only when exponent is auto) + if (autoExponent && result >= ceil && e < 8) { result /= ceil; e++; } } - return {result, e}; + return { result, e }; } /** @@ -193,20 +213,36 @@ function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { * @param {number} ceil - Ceiling value * @param {Function} roundingFunc - Rounding function * @param {number} round - Round value + * @param {number} exponent - Forced exponent (-1 for auto) * @returns {Object} Object with value and e properties */ -function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil, roundingFunc, round) { +function applyPrecisionHandling( + value, + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, +) { let result = value.toPrecision(precision); + const autoExponent = exponent === -1 || isNaN(exponent); + // Handle scientific notation by recalculating with incremented exponent - if (result.includes(E) && e < 8) { + if (result.includes(E) && e < 8 && autoExponent) { e++; - const {result: valueResult} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult } = calculateOptimizedValue(num, e, isDecimal, bits, ceil); const p = round > 0 ? Math.pow(10, round) : 1; - result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(precision); + result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision( + precision, + ); } - return {value: result, e}; + return { value: result, e }; } /** @@ -219,7 +255,7 @@ function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil * @param {number} round - Round value * @returns {string|number} Formatted value */ -function applyNumberFormatting (value, locale, localeOptions, separator, pad, round) { +function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) { let result = value; // Apply locale formatting @@ -234,9 +270,10 @@ function applyNumberFormatting (value, locale, localeOptions, separator, pad, ro // Apply padding if (pad && round > 0) { const resultStr = result.toString(); - const x = separator || ((resultStr.match(/(\D)/g) || []).pop() || PERIOD); + const x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD; const tmp = resultStr.split(x); const s = tmp[1] || EMPTY; + const l = s.length; const n = round - l; @@ -269,28 +306,31 @@ function applyNumberFormatting (value, locale, localeOptions, separator, pad, ro * @returns {string|Array|Object|number} Formatted file size based on output option * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid * @example - * filesize(1024) // "1 KB" - * filesize(1024, {bits: true}) // "8 Kb" - * filesize(1024, {output: "object"}) // {value: 1, symbol: "KB", exponent: 1, unit: "KB"} + * filesize(1024) // "1.02 kB" + * filesize(1024, {bits: true}) // "8.19 kbit" + * filesize(1024, {output: "object"}) // {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"} */ -function filesize (arg, { - bits = false, - pad = false, - base = -1, - round = 2, - locale = EMPTY, - localeOptions = {}, - separator = EMPTY, - spacer = SPACE, - symbols = {}, - standard = EMPTY, - output = STRING, - fullform = false, - fullforms = [], - exponent = -1, - roundingMethod = ROUND, - precision = 0 -} = {}) { +function filesize( + arg, + { + bits = false, + pad = false, + base = -1, + round = 2, + locale = EMPTY, + localeOptions = {}, + separator = EMPTY, + spacer = SPACE, + symbols = {}, + standard = EMPTY, + output = STRING, + fullform = false, + fullforms = [], + exponent = -1, + roundingMethod = ROUND, + precision = 0, + } = {}, +) { let e = exponent, num = Number(arg), result = [], @@ -298,7 +338,7 @@ function filesize (arg, { u = EMPTY; // Optimized base & standard configuration lookup - const {isDecimal, ceil, actualStandard} = getBaseConfiguration(standard, base); + const { isDecimal, ceil, actualStandard } = getBaseConfiguration(standard, base); const full = fullform === true, neg = num < 0, @@ -319,12 +359,23 @@ function filesize (arg, { // Fast path for zero if (num === 0) { - return handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer); + return handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + ); } // Optimized exponent calculation using pre-computed log values if (e === -1 || isNaN(e)) { - e = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024); + e = isDecimal + ? Math.floor(Math.log(num) / LOG_10_1000) + : Math.floor(Math.log(num) / LOG_2_1024); if (e < 0) { e = 0; } @@ -338,12 +389,21 @@ function filesize (arg, { e = 8; } + const autoExponent = exponent === -1 || isNaN(exponent); + if (output === EXPONENT) { return e; } // Calculate value with optimized lookup and bits handling - const {result: valueResult, e: valueExponent} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult, e: valueExponent } = calculateOptimizedValue( + num, + e, + isDecimal, + bits, + ceil, + autoExponent, + ); val = valueResult; e = valueExponent; @@ -351,21 +411,32 @@ function filesize (arg, { const p = e > 0 && round > 0 ? Math.pow(10, round) : 1; result[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p; - if (result[0] === ceil && e < 8 && exponent === -1) { + if (result[0] === ceil && e < 8 && autoExponent) { result[0] = 1; e++; } // Apply precision handling if (precision > 0) { - const precisionResult = applyPrecisionHandling(result[0], precision, e, num, isDecimal, bits, ceil, roundingFunc, round); + const precisionResult = applyPrecisionHandling( + result[0], + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, + ); result[0] = precisionResult.value; e = precisionResult.e; } // Cache symbol lookup const symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES]; - u = result[1] = (isDecimal && e === 1) ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; + u = result[1] = isDecimal && e === 1 ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; // Decorating a 'diff' if (neg) { @@ -381,7 +452,9 @@ function filesize (arg, { result[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round); if (full) { - result[1] = fullforms[e] || STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); + result[1] = + fullforms[e] || + STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); } // Optimized return logic @@ -394,7 +467,7 @@ function filesize (arg, { value: result[0], symbol: result[1], exponent: e, - unit: u + unit: u, }; } @@ -403,7 +476,7 @@ function filesize (arg, { /** * Creates a partially applied version of filesize with preset options - * @param {Object} [options={}] - Default options to apply to the returned function + * @param {Object} [options={}] - Configuration options (same as filesize) * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto) @@ -422,12 +495,12 @@ function filesize (arg, { * @param {number} [options.precision=0] - Number of significant digits (0 for auto) * @returns {Function} A function that takes a file size and returns formatted output * @example - * const formatBytes = partial({round: 1, standard: "IEC"}); - * formatBytes(1024) // "1.0 KiB" - * formatBytes(2048) // "2.0 KiB" + * const formatBytes = partial({round: 1, standard: "iec"}); + * formatBytes(1024) // "1 KiB" + * formatBytes(2048) // "2 KiB" + * formatBytes(1536) // "1.5 KiB" */ -// Partial application for functional programming -function partial ({ +function partial({ bits = false, pad = false, base = -1, @@ -443,26 +516,27 @@ function partial ({ fullforms = [], exponent = -1, roundingMethod = ROUND, - precision = 0 + precision = 0, } = {}) { - return arg => filesize(arg, { - bits, - pad, - base, - round, - locale, - localeOptions, - separator, - spacer, - symbols, - standard, - output, - fullform, - fullforms, - exponent, - roundingMethod, - precision - }); + return (arg) => + filesize(arg, { + bits, + pad, + base, + round, + locale, + localeOptions, + separator, + spacer, + symbols, + standard, + output, + fullform, + fullforms, + exponent, + roundingMethod, + precision, + }); } exports.filesize = filesize; diff --git a/dist/filesize.js b/dist/filesize.js index 49aa1a0..6fe865b 100644 --- a/dist/filesize.js +++ b/dist/filesize.js @@ -1,7 +1,7 @@ /** * filesize * - * @copyright 2025 Jason Mulligan + * @copyright 2026 Jason Mulligan * @license BSD-3-Clause * @version 11.0.13 */ @@ -45,17 +45,17 @@ const STRINGS = { symbol: { iec: { bits: ["bit", "Kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"], - bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] + bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], }, jedec: { bits: ["bit", "Kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"], - bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] - } + bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], + }, }, fullform: { iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"], - jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"] - } + jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"], + }, }; // Pre-computed lookup tables for performance optimization @@ -68,7 +68,7 @@ const BINARY_POWERS = [ 1125899906842624, // 2^50 1152921504606846976, // 2^60 1180591620717411303424, // 2^70 - 1208925819614629174706176 // 2^80 + 1208925819614629174706176, // 2^80 ]; const DECIMAL_POWERS = [ @@ -80,16 +80,16 @@ const DECIMAL_POWERS = [ 1000000000000000, // 10^15 1000000000000000000, // 10^18 1000000000000000000000, // 10^21 - 1000000000000000000000000 // 10^24 + 1000000000000000000000000, // 10^24 ]; // Pre-computed log values for faster exponent calculation const LOG_2_1024 = Math.log(1024); const LOG_10_1000 = Math.log(1000);// Cached configuration lookup for better performance const STANDARD_CONFIGS = { - [SI]: {isDecimal: true, ceil: 1000, actualStandard: JEDEC}, - [IEC]: {isDecimal: false, ceil: 1024, actualStandard: IEC}, - [JEDEC]: {isDecimal: false, ceil: 1024, actualStandard: JEDEC} + [SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC }, + [IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC }, + [JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC }, }; /** @@ -98,7 +98,7 @@ const STANDARD_CONFIGS = { * @param {number} base - Base number * @returns {Object} Configuration object */ -function getBaseConfiguration (standard, base) { +function getBaseConfiguration(standard, base) { // Use cached lookup table for better performance if (STANDARD_CONFIGS[standard]) { return STANDARD_CONFIGS[standard]; @@ -106,11 +106,11 @@ function getBaseConfiguration (standard, base) { // Base override if (base === 2) { - return {isDecimal: false, ceil: 1024, actualStandard: IEC}; + return { isDecimal: false, ceil: 1024, actualStandard: IEC }; } // Default - return {isDecimal: true, ceil: 1000, actualStandard: JEDEC}; + return { isDecimal: true, ceil: 1000, actualStandard: JEDEC }; } /** @@ -123,34 +123,53 @@ function getBaseConfiguration (standard, base) { * @param {Array} fullforms - Custom full forms * @param {string} output - Output format * @param {string} spacer - Spacer character + * @param {string} [symbol] - Symbol to use (defaults based on bits/standard) * @returns {string|Array|Object|number} Formatted result */ -function handleZeroValue (precision, actualStandard, bits, symbols, full, fullforms, output, spacer) { - const result = []; - result[0] = precision > 0 ? (0).toPrecision(precision) : 0; - const u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0]; +function handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + symbol, +) { + const value = precision > 0 ? (0).toPrecision(precision) : 0; if (output === EXPONENT) { return 0; } + // Set default symbol if not provided + if (!symbol) { + symbol = bits + ? STRINGS.symbol[actualStandard].bits[0] + : STRINGS.symbol[actualStandard].bytes[0]; + } + // Apply symbol customization - if (symbols[result[1]]) { - result[1] = symbols[result[1]]; + if (symbols[symbol]) { + symbol = symbols[symbol]; } // Apply full form if (full) { - result[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); + symbol = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); } // Return in requested format - return output === ARRAY ? result : output === OBJECT ? { - value: result[0], - symbol: result[1], - exponent: 0, - unit: u - } : result.join(spacer); + if (output === ARRAY) { + return [value, symbol]; + } + + if (output === OBJECT) { + return { value, symbol, exponent: 0, unit: symbol }; + } + + return value + spacer + symbol; } /** @@ -160,22 +179,23 @@ function handleZeroValue (precision, actualStandard, bits, symbols, full, fullfo * @param {boolean} isDecimal - Whether to use decimal powers * @param {boolean} bits - Whether to calculate bits * @param {number} ceil - Ceiling value for auto-increment - * @returns {Object} Object with val and e properties + * @param {boolean} autoExponent - Whether exponent is auto (-1 or NaN) + * @returns {Object} Object with result and e properties */ -function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { +function calculateOptimizedValue(num, e, isDecimal, bits, ceil, autoExponent = true) { const d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e]; let result = num / d; if (bits) { result *= 8; - // Handle auto-increment for bits - if (result >= ceil && e < 8) { + // Handle auto-increment for bits (only when exponent is auto) + if (autoExponent && result >= ceil && e < 8) { result /= ceil; e++; } } - return {result, e}; + return { result, e }; } /** @@ -189,20 +209,36 @@ function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { * @param {number} ceil - Ceiling value * @param {Function} roundingFunc - Rounding function * @param {number} round - Round value + * @param {number} exponent - Forced exponent (-1 for auto) * @returns {Object} Object with value and e properties */ -function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil, roundingFunc, round) { +function applyPrecisionHandling( + value, + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, +) { let result = value.toPrecision(precision); + const autoExponent = exponent === -1 || isNaN(exponent); + // Handle scientific notation by recalculating with incremented exponent - if (result.includes(E) && e < 8) { + if (result.includes(E) && e < 8 && autoExponent) { e++; - const {result: valueResult} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult } = calculateOptimizedValue(num, e, isDecimal, bits, ceil); const p = round > 0 ? Math.pow(10, round) : 1; - result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(precision); + result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision( + precision, + ); } - return {value: result, e}; + return { value: result, e }; } /** @@ -215,7 +251,7 @@ function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil * @param {number} round - Round value * @returns {string|number} Formatted value */ -function applyNumberFormatting (value, locale, localeOptions, separator, pad, round) { +function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) { let result = value; // Apply locale formatting @@ -230,9 +266,10 @@ function applyNumberFormatting (value, locale, localeOptions, separator, pad, ro // Apply padding if (pad && round > 0) { const resultStr = result.toString(); - const x = separator || ((resultStr.match(/(\D)/g) || []).pop() || PERIOD); + const x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD; const tmp = resultStr.split(x); const s = tmp[1] || EMPTY; + const l = s.length; const n = round - l; @@ -263,28 +300,31 @@ function applyNumberFormatting (value, locale, localeOptions, separator, pad, ro * @returns {string|Array|Object|number} Formatted file size based on output option * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid * @example - * filesize(1024) // "1 KB" - * filesize(1024, {bits: true}) // "8 Kb" - * filesize(1024, {output: "object"}) // {value: 1, symbol: "KB", exponent: 1, unit: "KB"} + * filesize(1024) // "1.02 kB" + * filesize(1024, {bits: true}) // "8.19 kbit" + * filesize(1024, {output: "object"}) // {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"} */ -function filesize (arg, { - bits = false, - pad = false, - base = -1, - round = 2, - locale = EMPTY, - localeOptions = {}, - separator = EMPTY, - spacer = SPACE, - symbols = {}, - standard = EMPTY, - output = STRING, - fullform = false, - fullforms = [], - exponent = -1, - roundingMethod = ROUND, - precision = 0 -} = {}) { +function filesize( + arg, + { + bits = false, + pad = false, + base = -1, + round = 2, + locale = EMPTY, + localeOptions = {}, + separator = EMPTY, + spacer = SPACE, + symbols = {}, + standard = EMPTY, + output = STRING, + fullform = false, + fullforms = [], + exponent = -1, + roundingMethod = ROUND, + precision = 0, + } = {}, +) { let e = exponent, num = Number(arg), result = [], @@ -292,7 +332,7 @@ function filesize (arg, { u = EMPTY; // Optimized base & standard configuration lookup - const {isDecimal, ceil, actualStandard} = getBaseConfiguration(standard, base); + const { isDecimal, ceil, actualStandard } = getBaseConfiguration(standard, base); const full = fullform === true, neg = num < 0, @@ -313,12 +353,23 @@ function filesize (arg, { // Fast path for zero if (num === 0) { - return handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer); + return handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + ); } // Optimized exponent calculation using pre-computed log values if (e === -1 || isNaN(e)) { - e = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024); + e = isDecimal + ? Math.floor(Math.log(num) / LOG_10_1000) + : Math.floor(Math.log(num) / LOG_2_1024); if (e < 0) { e = 0; } @@ -332,12 +383,21 @@ function filesize (arg, { e = 8; } + const autoExponent = exponent === -1 || isNaN(exponent); + if (output === EXPONENT) { return e; } // Calculate value with optimized lookup and bits handling - const {result: valueResult, e: valueExponent} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult, e: valueExponent } = calculateOptimizedValue( + num, + e, + isDecimal, + bits, + ceil, + autoExponent, + ); val = valueResult; e = valueExponent; @@ -345,21 +405,32 @@ function filesize (arg, { const p = e > 0 && round > 0 ? Math.pow(10, round) : 1; result[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p; - if (result[0] === ceil && e < 8 && exponent === -1) { + if (result[0] === ceil && e < 8 && autoExponent) { result[0] = 1; e++; } // Apply precision handling if (precision > 0) { - const precisionResult = applyPrecisionHandling(result[0], precision, e, num, isDecimal, bits, ceil, roundingFunc, round); + const precisionResult = applyPrecisionHandling( + result[0], + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, + ); result[0] = precisionResult.value; e = precisionResult.e; } // Cache symbol lookup const symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES]; - u = result[1] = (isDecimal && e === 1) ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; + u = result[1] = isDecimal && e === 1 ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; // Decorating a 'diff' if (neg) { @@ -375,7 +446,9 @@ function filesize (arg, { result[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round); if (full) { - result[1] = fullforms[e] || STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); + result[1] = + fullforms[e] || + STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); } // Optimized return logic @@ -388,7 +461,7 @@ function filesize (arg, { value: result[0], symbol: result[1], exponent: e, - unit: u + unit: u, }; } @@ -397,7 +470,7 @@ function filesize (arg, { /** * Creates a partially applied version of filesize with preset options - * @param {Object} [options={}] - Default options to apply to the returned function + * @param {Object} [options={}] - Configuration options (same as filesize) * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto) @@ -416,12 +489,12 @@ function filesize (arg, { * @param {number} [options.precision=0] - Number of significant digits (0 for auto) * @returns {Function} A function that takes a file size and returns formatted output * @example - * const formatBytes = partial({round: 1, standard: "IEC"}); - * formatBytes(1024) // "1.0 KiB" - * formatBytes(2048) // "2.0 KiB" + * const formatBytes = partial({round: 1, standard: "iec"}); + * formatBytes(1024) // "1 KiB" + * formatBytes(2048) // "2 KiB" + * formatBytes(1536) // "1.5 KiB" */ -// Partial application for functional programming -function partial ({ +function partial({ bits = false, pad = false, base = -1, @@ -437,24 +510,25 @@ function partial ({ fullforms = [], exponent = -1, roundingMethod = ROUND, - precision = 0 + precision = 0, } = {}) { - return arg => filesize(arg, { - bits, - pad, - base, - round, - locale, - localeOptions, - separator, - spacer, - symbols, - standard, - output, - fullform, - fullforms, - exponent, - roundingMethod, - precision - }); -}export{filesize,partial}; \ No newline at end of file + return (arg) => + filesize(arg, { + bits, + pad, + base, + round, + locale, + localeOptions, + separator, + spacer, + symbols, + standard, + output, + fullform, + fullforms, + exponent, + roundingMethod, + precision, + }); +}export{filesize,partial}; diff --git a/dist/filesize.min.js b/dist/filesize.min.js index 1fd7a1a..0419a4f 100644 --- a/dist/filesize.min.js +++ b/dist/filesize.min.js @@ -1,5 +1,5 @@ /*! - 2025 Jason Mulligan + 2026 Jason Mulligan @version 11.0.13 */ -const t="iec",e="jedec",i="si",n="bits",a="byte",o="bytes",r="array",l="object",s="string",c="exponent",u="round",b={symbol:{iec:{bits:["bit","Kibit","Mibit","Gibit","Tibit","Pibit","Eibit","Zibit","Yibit"],bytes:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"]},jedec:{bits:["bit","Kbit","Mbit","Gbit","Tbit","Pbit","Ebit","Zbit","Ybit"],bytes:["B","KB","MB","GB","TB","PB","EB","ZB","YB"]}},fullform:{iec:["","kibi","mebi","gibi","tebi","pebi","exbi","zebi","yobi"],jedec:["","kilo","mega","giga","tera","peta","exa","zetta","yotta"]}},d=[1,1024,1048576,1073741824,1099511627776,0x4000000000000,0x1000000000000000,11805916207174113e5,12089258196146292e8],f=[1,1e3,1e6,1e9,1e12,1e15,1e18,1e21,1e24],p=Math.log(1024),m=Math.log(1e3),B={[i]:{isDecimal:!0,ceil:1e3,actualStandard:e},[t]:{isDecimal:!1,ceil:1024,actualStandard:t},[e]:{isDecimal:!1,ceil:1024,actualStandard:e}};function y(t,e,i,n,a){let o=t/(i?f[e]:d[e]);return n&&(o*=8,o>=a&&e<8&&(o/=a,e++)),{result:o,e:e}}function M(i,{bits:d=!1,pad:f=!1,base:M=-1,round:h=2,locale:g="",localeOptions:x={},separator:D="",spacer:E=" ",symbols:S={},standard:T="",output:v=s,fullform:N=!1,fullforms:$=[],exponent:j=-1,roundingMethod:k=u,precision:w=0}={}){let G=j,K=Number(i),P=[],Y=0,Z="";const{isDecimal:O,ceil:z,actualStandard:I}=function(i,n){return B[i]?B[i]:2===n?{isDecimal:!1,ceil:1024,actualStandard:t}:{isDecimal:!0,ceil:1e3,actualStandard:e}}(T,M),q=!0===N,A=K<0,C=Math[k];if("bigint"!=typeof i&&isNaN(i))throw new TypeError("Invalid number");if("function"!=typeof C)throw new TypeError("Invalid rounding method");if(A&&(K=-K),0===K)return function(t,e,i,s,u,d,f,p){const m=[];m[0]=t>0?(0).toPrecision(t):0;const B=m[1]=b.symbol[e][i?n:o][0];return f===c?0:(s[m[1]]&&(m[1]=s[m[1]]),u&&(m[1]=d[0]||b.fullform[e][0]+(i?"bit":a)),f===r?m:f===l?{value:m[0],symbol:m[1],exponent:0,unit:B}:m.join(p))}(w,I,d,S,q,$,v,E);if((-1===G||isNaN(G))&&(G=O?Math.floor(Math.log(K)/m):Math.floor(Math.log(K)/p),G<0&&(G=0)),G>8&&(w>0&&(w+=8-G),G=8),v===c)return G;const{result:F,e:H}=y(K,G,O,d,z);Y=F,G=H;const J=G>0&&h>0?Math.pow(10,h):1;if(P[0]=1===J?C(Y):C(Y*J)/J,P[0]===z&&G<8&&-1===j&&(P[0]=1,G++),w>0){const t=function(t,e,i,n,a,o,r,l,s){let c=t.toPrecision(e);if(c.includes("e")&&i<8){i++;const{result:t}=y(n,i,a,o,r),u=s>0?Math.pow(10,s):1;c=(1===u?l(t):l(t*u)/u).toPrecision(e)}return{value:c,e:i}}(P[0],w,G,K,O,d,z,C,h);P[0]=t.value,G=t.e}const L=b.symbol[I][d?n:o];return Z=P[1]=O&&1===G?d?"kbit":"kB":L[G],A&&(P[0]=-P[0]),S[P[1]]&&(P[1]=S[P[1]]),P[0]=function(t,e,i,n,a,o){let r=t;if(!0===e?r=r.toLocaleString():e.length>0?r=r.toLocaleString(e,i):n.length>0&&(r=r.toString().replace(".",n)),a&&o>0){const t=r.toString(),e=n||(t.match(/(\D)/g)||[]).pop()||".",i=t.split(e),a=i[1]||"",l=a.length,s=o-l;r=`${i[0]}${e}${a.padEnd(l+s,"0")}`}return r}(P[0],g,x,D,f,h),q&&(P[1]=$[G]||b.fullform[I][G]+(d?"bit":a)+(1===P[0]?"":"s")),v===r?P:v===l?{value:P[0],symbol:P[1],exponent:G,unit:Z}:" "===E?`${P[0]} ${P[1]}`:P.join(E)}function h({bits:t=!1,pad:e=!1,base:i=-1,round:n=2,locale:a="",localeOptions:o={},separator:r="",spacer:l=" ",symbols:c={},standard:b="",output:d=s,fullform:f=!1,fullforms:p=[],exponent:m=-1,roundingMethod:B=u,precision:y=0}={}){return s=>M(s,{bits:t,pad:e,base:i,round:n,locale:a,localeOptions:o,separator:r,spacer:l,symbols:c,standard:b,output:d,fullform:f,fullforms:p,exponent:m,roundingMethod:B,precision:y})}export{M as filesize,h as partial};//# sourceMappingURL=filesize.min.js.map +const t="iec",i="jedec",e="si",a="byte",n="array",o="object",r="string",s="exponent",l="round",c={symbol:{iec:{bits:["bit","Kibit","Mibit","Gibit","Tibit","Pibit","Eibit","Zibit","Yibit"],bytes:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"]},jedec:{bits:["bit","Kbit","Mbit","Gbit","Tbit","Pbit","Ebit","Zbit","Ybit"],bytes:["B","KB","MB","GB","TB","PB","EB","ZB","YB"]}},fullform:{iec:["","kibi","mebi","gibi","tebi","pebi","exbi","zebi","yobi"],jedec:["","kilo","mega","giga","tera","peta","exa","zetta","yotta"]}},u=[1,1024,1048576,1073741824,1099511627776,0x4000000000000,0x1000000000000000,11805916207174113e5,12089258196146292e8],b=[1,1e3,1e6,1e9,1e12,1e15,1e18,1e21,1e24],d=Math.log(1024),f=Math.log(1e3),p={[e]:{isDecimal:!0,ceil:1e3,actualStandard:i},[t]:{isDecimal:!1,ceil:1024,actualStandard:t},[i]:{isDecimal:!1,ceil:1024,actualStandard:i}};function m(t,i,e,a,n,o=!0){let r=t/(e?b[i]:u[i]);return a&&(r*=8,o&&r>=n&&i<8&&(r/=n,i++)),{result:r,e:i}}function B(e,{bits:u=!1,pad:b=!1,base:B=-1,round:y=2,locale:M="",localeOptions:h={},separator:g="",spacer:x=" ",symbols:N={},standard:D="",output:E=r,fullform:S=!1,fullforms:T=[],exponent:v=-1,roundingMethod:$=l,precision:j=0}={}){let k=v,w=Number(e),G=[],K=0,P="";const{isDecimal:Y,ceil:Z,actualStandard:O}=function(e,a){return p[e]?p[e]:2===a?{isDecimal:!1,ceil:1024,actualStandard:t}:{isDecimal:!0,ceil:1e3,actualStandard:i}}(D,B),z=!0===S,I=w<0,q=Math[$];if("bigint"!=typeof e&&isNaN(e))throw new TypeError("Invalid number");if("function"!=typeof q)throw new TypeError("Invalid rounding method");if(I&&(w=-w),0===w)return function(t,i,e,r,l,u,b,d,f){const p=t>0?(0).toPrecision(t):0;return b===s?0:(f||(f=e?c.symbol[i].bits[0]:c.symbol[i].bytes[0]),r[f]&&(f=r[f]),l&&(f=u[0]||c.fullform[i][0]+(e?"bit":a)),b===n?[p,f]:b===o?{value:p,symbol:f,exponent:0,unit:f}:p+d+f)}(j,O,u,N,z,T,E,x);(-1===k||isNaN(k))&&(k=Y?Math.floor(Math.log(w)/f):Math.floor(Math.log(w)/d),k<0&&(k=0)),k>8&&(j>0&&(j+=8-k),k=8);const A=-1===v||isNaN(v);if(E===s)return k;const{result:C,e:F}=m(w,k,Y,u,Z,A);K=C,k=F;const H=k>0&&y>0?Math.pow(10,y):1;if(G[0]=1===H?q(K):q(K*H)/H,G[0]===Z&&k<8&&A&&(G[0]=1,k++),j>0){const t=function(t,i,e,a,n,o,r,s,l,c){let u=t.toPrecision(i);const b=-1===c||isNaN(c);if(u.includes("e")&&e<8&&b){e++;const{result:t}=m(a,e,n,o,r),c=l>0?Math.pow(10,l):1;u=(1===c?s(t):s(t*c)/c).toPrecision(i)}return{value:u,e:e}}(G[0],j,k,w,Y,u,Z,q,y,v);G[0]=t.value,k=t.e}const J=c.symbol[O][u?"bits":"bytes"];return P=G[1]=Y&&1===k?u?"kbit":"kB":J[k],I&&(G[0]=-G[0]),N[G[1]]&&(G[1]=N[G[1]]),G[0]=function(t,i,e,a,n,o){let r=t;if(!0===i?r=r.toLocaleString():i.length>0?r=r.toLocaleString(i,e):a.length>0&&(r=r.toString().replace(".",a)),n&&o>0){const t=r.toString(),i=a||(t.slice(1).match(/[.,]/g)||[]).pop()||".",e=t.split(i),n=e[1]||"",s=n.length,l=o-s;r=`${e[0]}${i}${n.padEnd(s+l,"0")}`}return r}(G[0],M,h,g,b,y),z&&(G[1]=T[k]||c.fullform[O][k]+(u?"bit":a)+(1===G[0]?"":"s")),E===n?G:E===o?{value:G[0],symbol:G[1],exponent:k,unit:P}:" "===x?`${G[0]} ${G[1]}`:G.join(x)}function y({bits:t=!1,pad:i=!1,base:e=-1,round:a=2,locale:n="",localeOptions:o={},separator:s="",spacer:c=" ",symbols:u={},standard:b="",output:d=r,fullform:f=!1,fullforms:p=[],exponent:m=-1,roundingMethod:y=l,precision:M=0}={}){return r=>B(r,{bits:t,pad:i,base:e,round:a,locale:n,localeOptions:o,separator:s,spacer:c,symbols:u,standard:b,output:d,fullform:f,fullforms:p,exponent:m,roundingMethod:y,precision:M})}export{B as filesize,y as partial};//# sourceMappingURL=filesize.min.js.map diff --git a/dist/filesize.min.js.map b/dist/filesize.min.js.map index f6ce450..af2641a 100644 --- a/dist/filesize.min.js.map +++ b/dist/filesize.min.js.map @@ -1 +1 @@ -{"version":3,"file":"filesize.min.js","sources":["../src/constants.js","../src/helpers.js","../src/filesize.js"],"sourcesContent":["// Error Messages\nexport const INVALID_NUMBER = \"Invalid number\";\nexport const INVALID_ROUND = \"Invalid rounding method\";\n\n// Standard Types\nexport const IEC = \"iec\";\nexport const JEDEC = \"jedec\";\nexport const SI = \"si\";\n\n// Unit Types\nexport const BIT = \"bit\";\nexport const BITS = \"bits\";\nexport const BYTE = \"byte\";\nexport const BYTES = \"bytes\";\nexport const SI_KBIT = \"kbit\";\nexport const SI_KBYTE = \"kB\";\n\n// Output Format Types\nexport const ARRAY = \"array\";\nexport const FUNCTION = \"function\";\nexport const OBJECT = \"object\";\nexport const STRING = \"string\";\n\n// Processing Constants\nexport const EXPONENT = \"exponent\";\nexport const ROUND = \"round\";\n\n// Special Characters and Values\nexport const E = \"e\";\nexport const EMPTY = \"\";\nexport const PERIOD = \".\";\nexport const S = \"s\";\nexport const SPACE = \" \";\nexport const ZERO = \"0\";\n\n// Data Structures\nexport const STRINGS = {\n\tsymbol: {\n\t\tiec: {\n\t\t\tbits: [\"bit\", \"Kibit\", \"Mibit\", \"Gibit\", \"Tibit\", \"Pibit\", \"Eibit\", \"Zibit\", \"Yibit\"],\n\t\t\tbytes: [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\", \"ZiB\", \"YiB\"]\n\t\t},\n\t\tjedec: {\n\t\t\tbits: [\"bit\", \"Kbit\", \"Mbit\", \"Gbit\", \"Tbit\", \"Pbit\", \"Ebit\", \"Zbit\", \"Ybit\"],\n\t\t\tbytes: [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n\t\t}\n\t},\n\tfullform: {\n\t\tiec: [\"\", \"kibi\", \"mebi\", \"gibi\", \"tebi\", \"pebi\", \"exbi\", \"zebi\", \"yobi\"],\n\t\tjedec: [\"\", \"kilo\", \"mega\", \"giga\", \"tera\", \"peta\", \"exa\", \"zetta\", \"yotta\"]\n\t}\n};\n\n// Pre-computed lookup tables for performance optimization\nexport const BINARY_POWERS = [\n\t1, // 2^0\n\t1024, // 2^10\n\t1048576, // 2^20\n\t1073741824, // 2^30\n\t1099511627776, // 2^40\n\t1125899906842624, // 2^50\n\t1152921504606846976, // 2^60\n\t1180591620717411303424, // 2^70\n\t1208925819614629174706176 // 2^80\n];\n\nexport const DECIMAL_POWERS = [\n\t1, // 10^0\n\t1000, // 10^3\n\t1000000, // 10^6\n\t1000000000, // 10^9\n\t1000000000000, // 10^12\n\t1000000000000000, // 10^15\n\t1000000000000000000, // 10^18\n\t1000000000000000000000, // 10^21\n\t1000000000000000000000000 // 10^24\n];\n\n// Pre-computed log values for faster exponent calculation\nexport const LOG_2_1024 = Math.log(1024);\nexport const LOG_10_1000 = Math.log(1000);\n","import {\n\tARRAY,\n\tBINARY_POWERS,\n\tBIT,\n\tBITS,\n\tBYTE,\n\tBYTES,\n\tDECIMAL_POWERS,\n\tE,\n\tEMPTY,\n\tEXPONENT,\n\tIEC,\n\tJEDEC,\n\tOBJECT,\n\tPERIOD,\n\tSI,\n\tSTRINGS,\n\tZERO\n} from \"./constants.js\";\n\n// Cached configuration lookup for better performance\nconst STANDARD_CONFIGS = {\n\t[SI]: {isDecimal: true, ceil: 1000, actualStandard: JEDEC},\n\t[IEC]: {isDecimal: false, ceil: 1024, actualStandard: IEC},\n\t[JEDEC]: {isDecimal: false, ceil: 1024, actualStandard: JEDEC}\n};\n\n/**\n * Optimized base configuration lookup\n * @param {string} standard - Standard type\n * @param {number} base - Base number\n * @returns {Object} Configuration object\n */\nexport function getBaseConfiguration (standard, base) {\n\t// Use cached lookup table for better performance\n\tif (STANDARD_CONFIGS[standard]) {\n\t\treturn STANDARD_CONFIGS[standard];\n\t}\n\n\t// Base override\n\tif (base === 2) {\n\t\treturn {isDecimal: false, ceil: 1024, actualStandard: IEC};\n\t}\n\n\t// Default\n\treturn {isDecimal: true, ceil: 1000, actualStandard: JEDEC};\n}\n\n/**\n * Optimized zero value handling\n * @param {number} precision - Precision value\n * @param {string} actualStandard - Standard to use\n * @param {boolean} bits - Whether to use bits\n * @param {Object} symbols - Custom symbols\n * @param {boolean} full - Whether to use full form\n * @param {Array} fullforms - Custom full forms\n * @param {string} output - Output format\n * @param {string} spacer - Spacer character\n * @returns {string|Array|Object|number} Formatted result\n */\nexport function handleZeroValue (precision, actualStandard, bits, symbols, full, fullforms, output, spacer) {\n\tconst result = [];\n\tresult[0] = precision > 0 ? (0).toPrecision(precision) : 0;\n\tconst u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0];\n\n\tif (output === EXPONENT) {\n\t\treturn 0;\n\t}\n\n\t// Apply symbol customization\n\tif (symbols[result[1]]) {\n\t\tresult[1] = symbols[result[1]];\n\t}\n\n\t// Apply full form\n\tif (full) {\n\t\tresult[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE);\n\t}\n\n\t// Return in requested format\n\treturn output === ARRAY ? result : output === OBJECT ? {\n\t\tvalue: result[0],\n\t\tsymbol: result[1],\n\t\texponent: 0,\n\t\tunit: u\n\t} : result.join(spacer);\n}\n\n/**\n * Optimized value calculation with bits handling\n * @param {number} num - Input number\n * @param {number} e - Exponent\n * @param {boolean} isDecimal - Whether to use decimal powers\n * @param {boolean} bits - Whether to calculate bits\n * @param {number} ceil - Ceiling value for auto-increment\n * @returns {Object} Object with val and e properties\n */\nexport function calculateOptimizedValue (num, e, isDecimal, bits, ceil) {\n\tconst d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e];\n\tlet result = num / d;\n\n\tif (bits) {\n\t\tresult *= 8;\n\t\t// Handle auto-increment for bits\n\t\tif (result >= ceil && e < 8) {\n\t\t\tresult /= ceil;\n\t\t\te++;\n\t\t}\n\t}\n\n\treturn {result, e};\n}\n\n/**\n * Optimized precision handling with scientific notation correction\n * @param {number} value - Current value\n * @param {number} precision - Precision to apply\n * @param {number} e - Current exponent\n * @param {number} num - Original number\n * @param {boolean} isDecimal - Whether using decimal base\n * @param {boolean} bits - Whether calculating bits\n * @param {number} ceil - Ceiling value\n * @param {Function} roundingFunc - Rounding function\n * @param {number} round - Round value\n * @returns {Object} Object with value and e properties\n */\nexport function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil, roundingFunc, round) {\n\tlet result = value.toPrecision(precision);\n\n\t// Handle scientific notation by recalculating with incremented exponent\n\tif (result.includes(E) && e < 8) {\n\t\te++;\n\t\tconst {result: valueResult} = calculateOptimizedValue(num, e, isDecimal, bits, ceil);\n\t\tconst p = round > 0 ? Math.pow(10, round) : 1;\n\t\tresult = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(precision);\n\t}\n\n\treturn {value: result, e};\n}\n\n/**\n * Optimized number formatting with locale, separator, and padding\n * @param {number|string} value - Value to format\n * @param {string|boolean} locale - Locale setting\n * @param {Object} localeOptions - Locale options\n * @param {string} separator - Custom separator\n * @param {boolean} pad - Whether to pad\n * @param {number} round - Round value\n * @returns {string|number} Formatted value\n */\nexport function applyNumberFormatting (value, locale, localeOptions, separator, pad, round) {\n\tlet result = value;\n\n\t// Apply locale formatting\n\tif (locale === true) {\n\t\tresult = result.toLocaleString();\n\t} else if (locale.length > 0) {\n\t\tresult = result.toLocaleString(locale, localeOptions);\n\t} else if (separator.length > 0) {\n\t\tresult = result.toString().replace(PERIOD, separator);\n\t}\n\n\t// Apply padding\n\tif (pad && round > 0) {\n\t\tconst resultStr = result.toString();\n\t\tconst x = separator || ((resultStr.match(/(\\D)/g) || []).pop() || PERIOD);\n\t\tconst tmp = resultStr.split(x);\n\t\tconst s = tmp[1] || EMPTY;\n\t\tconst l = s.length;\n\t\tconst n = round - l;\n\n\t\tresult = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;\n\t}\n\n\treturn result;\n}\n","import {\n\tARRAY,\n\tBIT,\n\tBITS,\n\tBYTE,\n\tBYTES,\n\tEMPTY,\n\tEXPONENT,\n\tFUNCTION,\n\tINVALID_NUMBER,\n\tINVALID_ROUND,\n\tLOG_10_1000,\n\tLOG_2_1024,\n\tOBJECT,\n\tROUND,\n\tS,\n\tSI_KBIT,\n\tSI_KBYTE,\n\tSPACE,\n\tSTRING,\n\tSTRINGS,\n} from \"./constants.js\";\nimport {\n\tapplyNumberFormatting,\n\tapplyPrecisionHandling,\n\tcalculateOptimizedValue,\n\tgetBaseConfiguration,\n\thandleZeroValue\n} from \"./helpers.js\";\n\n/**\n * Converts a file size in bytes to a human-readable string with appropriate units\n * @param {number|string|bigint} arg - The file size in bytes to convert\n * @param {Object} [options={}] - Configuration options for formatting\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {string|Array|Object|number} Formatted file size based on output option\n * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid\n * @example\n * filesize(1024) // \"1 KB\"\n * filesize(1024, {bits: true}) // \"8 Kb\"\n * filesize(1024, {output: \"object\"}) // {value: 1, symbol: \"KB\", exponent: 1, unit: \"KB\"}\n */\nexport function filesize (arg, {\n\tbits = false,\n\tpad = false,\n\tbase = -1,\n\tround = 2,\n\tlocale = EMPTY,\n\tlocaleOptions = {},\n\tseparator = EMPTY,\n\tspacer = SPACE,\n\tsymbols = {},\n\tstandard = EMPTY,\n\toutput = STRING,\n\tfullform = false,\n\tfullforms = [],\n\texponent = -1,\n\troundingMethod = ROUND,\n\tprecision = 0\n} = {}) {\n\tlet e = exponent,\n\t\tnum = Number(arg),\n\t\tresult = [],\n\t\tval = 0,\n\t\tu = EMPTY;\n\n\t// Optimized base & standard configuration lookup\n\tconst {isDecimal, ceil, actualStandard} = getBaseConfiguration(standard, base);\n\n\tconst full = fullform === true,\n\t\tneg = num < 0,\n\t\troundingFunc = Math[roundingMethod];\n\n\tif (typeof arg !== \"bigint\" && isNaN(arg)) {\n\t\tthrow new TypeError(INVALID_NUMBER);\n\t}\n\n\tif (typeof roundingFunc !== FUNCTION) {\n\t\tthrow new TypeError(INVALID_ROUND);\n\t}\n\n\t// Flipping a negative number to determine the size\n\tif (neg) {\n\t\tnum = -num;\n\t}\n\n\t// Fast path for zero\n\tif (num === 0) {\n\t\treturn handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer);\n\t}\n\n\t// Optimized exponent calculation using pre-computed log values\n\tif (e === -1 || isNaN(e)) {\n\t\te = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024);\n\t\tif (e < 0) {\n\t\t\te = 0;\n\t\t}\n\t}\n\n\t// Exceeding supported length, time to reduce & multiply\n\tif (e > 8) {\n\t\tif (precision > 0) {\n\t\t\tprecision += 8 - e;\n\t\t}\n\t\te = 8;\n\t}\n\n\tif (output === EXPONENT) {\n\t\treturn e;\n\t}\n\n\t// Calculate value with optimized lookup and bits handling\n\tconst {result: valueResult, e: valueExponent} = calculateOptimizedValue(num, e, isDecimal, bits, ceil);\n\tval = valueResult;\n\te = valueExponent;\n\n\t// Optimize rounding calculation\n\tconst p = e > 0 && round > 0 ? Math.pow(10, round) : 1;\n\tresult[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p;\n\n\tif (result[0] === ceil && e < 8 && exponent === -1) {\n\t\tresult[0] = 1;\n\t\te++;\n\t}\n\n\t// Apply precision handling\n\tif (precision > 0) {\n\t\tconst precisionResult = applyPrecisionHandling(result[0], precision, e, num, isDecimal, bits, ceil, roundingFunc, round);\n\t\tresult[0] = precisionResult.value;\n\t\te = precisionResult.e;\n\t}\n\n\t// Cache symbol lookup\n\tconst symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES];\n\tu = result[1] = (isDecimal && e === 1) ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e];\n\n\t// Decorating a 'diff'\n\tif (neg) {\n\t\tresult[0] = -result[0];\n\t}\n\n\t// Applying custom symbol\n\tif (symbols[result[1]]) {\n\t\tresult[1] = symbols[result[1]];\n\t}\n\n\t// Apply locale, separator, and padding formatting\n\tresult[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round);\n\n\tif (full) {\n\t\tresult[1] = fullforms[e] || STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S);\n\t}\n\n\t// Optimized return logic\n\tif (output === ARRAY) {\n\t\treturn result;\n\t}\n\n\tif (output === OBJECT) {\n\t\treturn {\n\t\t\tvalue: result[0],\n\t\t\tsymbol: result[1],\n\t\t\texponent: e,\n\t\t\tunit: u\n\t\t};\n\t}\n\n\treturn spacer === SPACE ? `${result[0]} ${result[1]}` : result.join(spacer);\n}\n\n/**\n * Creates a partially applied version of filesize with preset options\n * @param {Object} [options={}] - Default options to apply to the returned function\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {Function} A function that takes a file size and returns formatted output\n * @example\n * const formatBytes = partial({round: 1, standard: \"IEC\"});\n * formatBytes(1024) // \"1.0 KiB\"\n * formatBytes(2048) // \"2.0 KiB\"\n */\n// Partial application for functional programming\nexport function partial ({\n\tbits = false,\n\tpad = false,\n\tbase = -1,\n\tround = 2,\n\tlocale = EMPTY,\n\tlocaleOptions = {},\n\tseparator = EMPTY,\n\tspacer = SPACE,\n\tsymbols = {},\n\tstandard = EMPTY,\n\toutput = STRING,\n\tfullform = false,\n\tfullforms = [],\n\texponent = -1,\n\troundingMethod = ROUND,\n\tprecision = 0\n} = {}) {\n\treturn arg => filesize(arg, {\n\t\tbits,\n\t\tpad,\n\t\tbase,\n\t\tround,\n\t\tlocale,\n\t\tlocaleOptions,\n\t\tseparator,\n\t\tspacer,\n\t\tsymbols,\n\t\tstandard,\n\t\toutput,\n\t\tfullform,\n\t\tfullforms,\n\t\texponent,\n\t\troundingMethod,\n\t\tprecision\n\t});\n}\n"],"names":["IEC","JEDEC","SI","BITS","BYTE","BYTES","ARRAY","OBJECT","STRING","EXPONENT","ROUND","STRINGS","symbol","iec","bits","bytes","jedec","fullform","BINARY_POWERS","DECIMAL_POWERS","LOG_2_1024","Math","log","LOG_10_1000","STANDARD_CONFIGS","isDecimal","ceil","actualStandard","calculateOptimizedValue","num","e","result","filesize","arg","pad","base","round","locale","EMPTY","localeOptions","separator","spacer","symbols","standard","output","fullforms","exponent","roundingMethod","precision","Number","val","u","getBaseConfiguration","full","neg","roundingFunc","isNaN","TypeError","toPrecision","value","unit","join","handleZeroValue","floor","valueResult","valueExponent","p","pow","precisionResult","includes","applyPrecisionHandling","symbolTable","toLocaleString","length","toString","replace","resultStr","x","match","pop","tmp","split","s","l","n","padEnd","applyNumberFormatting","partial"],"mappings":";;;;AACO,MAIMA,EAAM,MACNC,EAAQ,QACRC,EAAK,KAILC,EAAO,OACPC,EAAO,OACPC,EAAQ,QAKRC,EAAQ,QAERC,EAAS,SACTC,EAAS,SAGTC,EAAW,WACXC,EAAQ,QAWRC,EAAU,CACtBC,OAAQ,CACPC,IAAK,CACJC,KAAM,CAAC,MAAO,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,SAC7EC,MAAO,CAAC,IAAK,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,QAE/DC,MAAO,CACNF,KAAM,CAAC,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QACtEC,MAAO,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,QAGzDE,SAAU,CACTJ,IAAK,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QAClEG,MAAO,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,MAAO,QAAS,WAKzDE,EAAgB,CAC5B,EACA,KACA,QACA,WACA,cACA,gBACA,mBACA,oBACA,qBAGYC,EAAiB,CAC7B,EACA,IACA,IACA,IACA,KACA,KACA,KACA,KACA,MAIYC,EAAaC,KAAKC,IAAI,MACtBC,EAAcF,KAAKC,IAAI,KC3D9BE,EAAmB,CACxBtB,CAACA,GAAK,CAACuB,WAAW,EAAMC,KAAM,IAAMC,eAAgB1B,GACpDD,CAACA,GAAM,CAACyB,WAAW,EAAOC,KAAM,KAAMC,eAAgB3B,GACtDC,CAACA,GAAQ,CAACwB,WAAW,EAAOC,KAAM,KAAMC,eAAgB1B,IAyElD,SAAS2B,EAAyBC,EAAKC,EAAGL,EAAWX,EAAMY,GAEjE,IAAIK,EAASF,GADHJ,EAAYN,EAAeW,GAAKZ,EAAcY,IAYxD,OATIhB,IACHiB,GAAU,EAENA,GAAUL,GAAQI,EAAI,IACzBC,GAAUL,EACVI,MAIK,CAACC,SAAQD,IACjB,CCtDO,SAASE,EAAUC,GAAKnB,KAC9BA,GAAO,EAAKoB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EFjCoB,IEiCNC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASpC,EAAMS,SACfA,GAAW,EAAK4B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBrC,EAAKsC,UACtBA,EAAY,GACT,IACH,IAAIlB,EAAIgB,EACPjB,EAAMoB,OAAOhB,GACbF,EAAS,GACTmB,EAAM,EACNC,EFlDmB,GEqDpB,MAAM1B,UAACA,EAASC,KAAEA,EAAIC,eAAEA,GDjDlB,SAA+BgB,EAAUR,GAE/C,OAAIX,EAAiBmB,GACbnB,EAAiBmB,GAIZ,IAATR,EACI,CAACV,WAAW,EAAOC,KAAM,KAAMC,eAAgB3B,GAIhD,CAACyB,WAAW,EAAMC,KAAM,IAAMC,eAAgB1B,EACtD,CCoC2CmD,CAAqBT,EAAUR,GAEnEkB,GAAoB,IAAbpC,EACZqC,EAAMzB,EAAM,EACZ0B,EAAelC,KAAK0B,GAErB,GAAmB,iBAARd,GAAoBuB,MAAMvB,GACpC,MAAM,IAAIwB,UFxFkB,kBE2F7B,GFzEuB,mBEyEZF,EACV,MAAM,IAAIE,UF3FiB,2BEoG5B,GALIH,IACHzB,GAAOA,GAII,IAARA,EACH,OD3CK,SAA0BmB,EAAWrB,EAAgBb,EAAM4B,EAASW,EAAMR,EAAWD,EAAQH,GACnG,MAAMV,EAAS,GACfA,EAAO,GAAKiB,EAAY,GAAI,GAAIU,YAAYV,GAAa,EACzD,MAAMG,EAAIpB,EAAO,GAAKpB,EAAQC,OAAOe,GAAgBb,EAAOX,EAAOE,GAAO,GAE1E,OAAIuC,IAAWnC,EACP,GAIJiC,EAAQX,EAAO,MAClBA,EAAO,GAAKW,EAAQX,EAAO,KAIxBsB,IACHtB,EAAO,GAAKc,EAAU,IAAMlC,EAAQM,SAASU,GAAgB,IAAMb,EDlElD,MCkE+DV,IAI1EwC,IAAWtC,EAAQyB,EAASa,IAAWrC,EAAS,CACtDoD,MAAO5B,EAAO,GACdnB,OAAQmB,EAAO,GACfe,SAAU,EACVc,KAAMT,GACHpB,EAAO8B,KAAKpB,GACjB,CCiBSqB,CAAgBd,EAAWrB,EAAgBb,EAAM4B,EAASW,EAAMR,EAAWD,EAAQH,GAmB3F,KAfU,IAANX,GAAY0B,MAAM1B,MACrBA,EAAIL,EAAYJ,KAAK0C,MAAM1C,KAAKC,IAAIO,GAAON,GAAeF,KAAK0C,MAAM1C,KAAKC,IAAIO,GAAOT,GACjFU,EAAI,IACPA,EAAI,IAKFA,EAAI,IACHkB,EAAY,IACfA,GAAa,EAAIlB,GAElBA,EAAI,GAGDc,IAAWnC,EACd,OAAOqB,EAIR,MAAOC,OAAQiC,EAAalC,EAAGmC,GAAiBrC,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,GACjGwB,EAAMc,EACNlC,EAAImC,EAGJ,MAAMC,EAAIpC,EAAI,GAAKM,EAAQ,EAAIf,KAAK8C,IAAI,GAAI/B,GAAS,EASrD,GARAL,EAAO,GAAW,IAANmC,EAAUX,EAAaL,GAAOK,EAAaL,EAAMgB,GAAKA,EAE9DnC,EAAO,KAAOL,GAAQI,EAAI,QAAKgB,IAClCf,EAAO,GAAK,EACZD,KAIGkB,EAAY,EAAG,CAClB,MAAMoB,EDhBD,SAAiCT,EAAOX,EAAWlB,EAAGD,EAAKJ,EAAWX,EAAMY,EAAM6B,EAAcnB,GACtG,IAAIL,EAAS4B,EAAMD,YAAYV,GAG/B,GAAIjB,EAAOsC,SDtGK,MCsGUvC,EAAI,EAAG,CAChCA,IACA,MAAOC,OAAQiC,GAAepC,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,GACzEwC,EAAI9B,EAAQ,EAAIf,KAAK8C,IAAI,GAAI/B,GAAS,EAC5CL,GAAgB,IAANmC,EAAUX,EAAaS,GAAeT,EAAaS,EAAcE,GAAKA,GAAGR,YAAYV,EAChG,CAEA,MAAO,CAACW,MAAO5B,EAAQD,IACxB,CCI0BwC,CAAuBvC,EAAO,GAAIiB,EAAWlB,EAAGD,EAAKJ,EAAWX,EAAMY,EAAM6B,EAAcnB,GAClHL,EAAO,GAAKqC,EAAgBT,MAC5B7B,EAAIsC,EAAgBtC,CACrB,CAGA,MAAMyC,EAAc5D,EAAQC,OAAOe,GAAgBb,EAAOX,EAAOE,GAqBjE,OApBA8C,EAAIpB,EAAO,GAAMN,GAAmB,IAANK,EAAYhB,EFvIpB,OACC,KEsIgDyD,EAAYzC,GAG/EwB,IACHvB,EAAO,IAAMA,EAAO,IAIjBW,EAAQX,EAAO,MAClBA,EAAO,GAAKW,EAAQX,EAAO,KAI5BA,EAAO,GDZD,SAAgC4B,EAAOtB,EAAQE,EAAeC,EAAWN,EAAKE,GACpF,IAAIL,EAAS4B,EAYb,IATe,IAAXtB,EACHN,EAASA,EAAOyC,iBACNnC,EAAOoC,OAAS,EAC1B1C,EAASA,EAAOyC,eAAenC,EAAQE,GAC7BC,EAAUiC,OAAS,IAC7B1C,EAASA,EAAO2C,WAAWC,QDjIP,ICiIuBnC,IAIxCN,GAAOE,EAAQ,EAAG,CACrB,MAAMwC,EAAY7C,EAAO2C,WACnBG,EAAIrC,IAAeoC,EAAUE,MAAM,UAAY,IAAIC,ODvIrC,ICwIdC,EAAMJ,EAAUK,MAAMJ,GACtBK,EAAIF,EAAI,ID1IK,GC2IbG,EAAID,EAAET,OACNW,EAAIhD,EAAQ+C,EAElBpD,EAAS,GAAGiD,EAAI,KAAKH,IAAIK,EAAEG,OAAOF,EAAIC,ED1IpB,MC2InB,CAEA,OAAOrD,CACR,CCbauD,CAAsBvD,EAAO,GAAIM,EAAQE,EAAeC,EAAWN,EAAKE,GAEhFiB,IACHtB,EAAO,GAAKc,EAAUf,IAAMnB,EAAQM,SAASU,GAAgBG,IAAMhB,EF3JlD,ME2J+DV,IAAuB,IAAd2B,EAAO,GFxI7E,GAEJ,ME0IZa,IAAWtC,EACPyB,EAGJa,IAAWrC,EACP,CACNoD,MAAO5B,EAAO,GACdnB,OAAQmB,EAAO,GACfe,SAAUhB,EACV8B,KAAMT,GFlJY,MEsJbV,EAAmB,GAAGV,EAAO,MAAMA,EAAO,KAAOA,EAAO8B,KAAKpB,EACrE,CA4BO,SAAS8C,GAASzE,KACxBA,GAAO,EAAKoB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EF3LoB,IE2LNC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASpC,EAAMS,SACfA,GAAW,EAAK4B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBrC,EAAKsC,UACtBA,EAAY,GACT,IACH,OAAOf,GAAOD,EAASC,EAAK,CAC3BnB,OACAoB,MACAC,OACAC,QACAC,SACAE,gBACAC,YACAC,SACAC,UACAC,WACAC,SACA3B,WACA4B,YACAC,WACAC,iBACAC,aAEF,QAAAhB,cAAAuD"} \ No newline at end of file +{"version":3,"file":"filesize.min.js","sources":["../src/constants.js","../src/helpers.js","../src/filesize.js"],"sourcesContent":["// Error Messages\nexport const INVALID_NUMBER = \"Invalid number\";\nexport const INVALID_ROUND = \"Invalid rounding method\";\n\n// Standard Types\nexport const IEC = \"iec\";\nexport const JEDEC = \"jedec\";\nexport const SI = \"si\";\n\n// Unit Types\nexport const BIT = \"bit\";\nexport const BITS = \"bits\";\nexport const BYTE = \"byte\";\nexport const BYTES = \"bytes\";\nexport const SI_KBIT = \"kbit\";\nexport const SI_KBYTE = \"kB\";\n\n// Output Format Types\nexport const ARRAY = \"array\";\nexport const FUNCTION = \"function\";\nexport const OBJECT = \"object\";\nexport const STRING = \"string\";\n\n// Processing Constants\nexport const EXPONENT = \"exponent\";\nexport const ROUND = \"round\";\n\n// Special Characters and Values\nexport const E = \"e\";\nexport const EMPTY = \"\";\nexport const PERIOD = \".\";\nexport const S = \"s\";\nexport const SPACE = \" \";\nexport const ZERO = \"0\";\n\n// Data Structures\nexport const STRINGS = {\n\tsymbol: {\n\t\tiec: {\n\t\t\tbits: [\"bit\", \"Kibit\", \"Mibit\", \"Gibit\", \"Tibit\", \"Pibit\", \"Eibit\", \"Zibit\", \"Yibit\"],\n\t\t\tbytes: [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\", \"ZiB\", \"YiB\"],\n\t\t},\n\t\tjedec: {\n\t\t\tbits: [\"bit\", \"Kbit\", \"Mbit\", \"Gbit\", \"Tbit\", \"Pbit\", \"Ebit\", \"Zbit\", \"Ybit\"],\n\t\t\tbytes: [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"],\n\t\t},\n\t},\n\tfullform: {\n\t\tiec: [\"\", \"kibi\", \"mebi\", \"gibi\", \"tebi\", \"pebi\", \"exbi\", \"zebi\", \"yobi\"],\n\t\tjedec: [\"\", \"kilo\", \"mega\", \"giga\", \"tera\", \"peta\", \"exa\", \"zetta\", \"yotta\"],\n\t},\n};\n\n// Pre-computed lookup tables for performance optimization\nexport const BINARY_POWERS = [\n\t1, // 2^0\n\t1024, // 2^10\n\t1048576, // 2^20\n\t1073741824, // 2^30\n\t1099511627776, // 2^40\n\t1125899906842624, // 2^50\n\t1152921504606846976, // 2^60\n\t1180591620717411303424, // 2^70\n\t1208925819614629174706176, // 2^80\n];\n\nexport const DECIMAL_POWERS = [\n\t1, // 10^0\n\t1000, // 10^3\n\t1000000, // 10^6\n\t1000000000, // 10^9\n\t1000000000000, // 10^12\n\t1000000000000000, // 10^15\n\t1000000000000000000, // 10^18\n\t1000000000000000000000, // 10^21\n\t1000000000000000000000000, // 10^24\n];\n\n// Pre-computed log values for faster exponent calculation\nexport const LOG_2_1024 = Math.log(1024);\nexport const LOG_10_1000 = Math.log(1000);\n","import {\n\tARRAY,\n\tBINARY_POWERS,\n\tBIT,\n\tBYTE,\n\tDECIMAL_POWERS,\n\tE,\n\tEMPTY,\n\tEXPONENT,\n\tIEC,\n\tJEDEC,\n\tOBJECT,\n\tPERIOD,\n\tSI,\n\tSTRINGS,\n\tZERO,\n} from \"./constants.js\";\n\n// Cached configuration lookup for better performance\nconst STANDARD_CONFIGS = {\n\t[SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC },\n\t[IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC },\n\t[JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC },\n};\n\n/**\n * Optimized base configuration lookup\n * @param {string} standard - Standard type\n * @param {number} base - Base number\n * @returns {Object} Configuration object\n */\nexport function getBaseConfiguration(standard, base) {\n\t// Use cached lookup table for better performance\n\tif (STANDARD_CONFIGS[standard]) {\n\t\treturn STANDARD_CONFIGS[standard];\n\t}\n\n\t// Base override\n\tif (base === 2) {\n\t\treturn { isDecimal: false, ceil: 1024, actualStandard: IEC };\n\t}\n\n\t// Default\n\treturn { isDecimal: true, ceil: 1000, actualStandard: JEDEC };\n}\n\n/**\n * Optimized zero value handling\n * @param {number} precision - Precision value\n * @param {string} actualStandard - Standard to use\n * @param {boolean} bits - Whether to use bits\n * @param {Object} symbols - Custom symbols\n * @param {boolean} full - Whether to use full form\n * @param {Array} fullforms - Custom full forms\n * @param {string} output - Output format\n * @param {string} spacer - Spacer character\n * @param {string} [symbol] - Symbol to use (defaults based on bits/standard)\n * @returns {string|Array|Object|number} Formatted result\n */\nexport function handleZeroValue(\n\tprecision,\n\tactualStandard,\n\tbits,\n\tsymbols,\n\tfull,\n\tfullforms,\n\toutput,\n\tspacer,\n\tsymbol,\n) {\n\tconst value = precision > 0 ? (0).toPrecision(precision) : 0;\n\n\tif (output === EXPONENT) {\n\t\treturn 0;\n\t}\n\n\t// Set default symbol if not provided\n\tif (!symbol) {\n\t\tsymbol = bits\n\t\t\t? STRINGS.symbol[actualStandard].bits[0]\n\t\t\t: STRINGS.symbol[actualStandard].bytes[0];\n\t}\n\n\t// Apply symbol customization\n\tif (symbols[symbol]) {\n\t\tsymbol = symbols[symbol];\n\t}\n\n\t// Apply full form\n\tif (full) {\n\t\tsymbol = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE);\n\t}\n\n\t// Return in requested format\n\tif (output === ARRAY) {\n\t\treturn [value, symbol];\n\t}\n\n\tif (output === OBJECT) {\n\t\treturn { value, symbol, exponent: 0, unit: symbol };\n\t}\n\n\treturn value + spacer + symbol;\n}\n\n/**\n * Optimized value calculation with bits handling\n * @param {number} num - Input number\n * @param {number} e - Exponent\n * @param {boolean} isDecimal - Whether to use decimal powers\n * @param {boolean} bits - Whether to calculate bits\n * @param {number} ceil - Ceiling value for auto-increment\n * @param {boolean} autoExponent - Whether exponent is auto (-1 or NaN)\n * @returns {Object} Object with result and e properties\n */\nexport function calculateOptimizedValue(num, e, isDecimal, bits, ceil, autoExponent = true) {\n\tconst d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e];\n\tlet result = num / d;\n\n\tif (bits) {\n\t\tresult *= 8;\n\t\t// Handle auto-increment for bits (only when exponent is auto)\n\t\tif (autoExponent && result >= ceil && e < 8) {\n\t\t\tresult /= ceil;\n\t\t\te++;\n\t\t}\n\t}\n\n\treturn { result, e };\n}\n\n/**\n * Optimized precision handling with scientific notation correction\n * @param {number} value - Current value\n * @param {number} precision - Precision to apply\n * @param {number} e - Current exponent\n * @param {number} num - Original number\n * @param {boolean} isDecimal - Whether using decimal base\n * @param {boolean} bits - Whether calculating bits\n * @param {number} ceil - Ceiling value\n * @param {Function} roundingFunc - Rounding function\n * @param {number} round - Round value\n * @param {number} exponent - Forced exponent (-1 for auto)\n * @returns {Object} Object with value and e properties\n */\nexport function applyPrecisionHandling(\n\tvalue,\n\tprecision,\n\te,\n\tnum,\n\tisDecimal,\n\tbits,\n\tceil,\n\troundingFunc,\n\tround,\n\texponent,\n) {\n\tlet result = value.toPrecision(precision);\n\n\tconst autoExponent = exponent === -1 || isNaN(exponent);\n\n\t// Handle scientific notation by recalculating with incremented exponent\n\tif (result.includes(E) && e < 8 && autoExponent) {\n\t\te++;\n\t\tconst { result: valueResult } = calculateOptimizedValue(num, e, isDecimal, bits, ceil);\n\t\tconst p = round > 0 ? Math.pow(10, round) : 1;\n\t\tresult = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(\n\t\t\tprecision,\n\t\t);\n\t}\n\n\treturn { value: result, e };\n}\n\n/**\n * Optimized number formatting with locale, separator, and padding\n * @param {number|string} value - Value to format\n * @param {string|boolean} locale - Locale setting\n * @param {Object} localeOptions - Locale options\n * @param {string} separator - Custom separator\n * @param {boolean} pad - Whether to pad\n * @param {number} round - Round value\n * @returns {string|number} Formatted value\n */\nexport function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) {\n\tlet result = value;\n\n\t// Apply locale formatting\n\tif (locale === true) {\n\t\tresult = result.toLocaleString();\n\t} else if (locale.length > 0) {\n\t\tresult = result.toLocaleString(locale, localeOptions);\n\t} else if (separator.length > 0) {\n\t\tresult = result.toString().replace(PERIOD, separator);\n\t}\n\n\t// Apply padding\n\tif (pad && round > 0) {\n\t\tconst resultStr = result.toString();\n\t\tconst x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD;\n\t\tconst tmp = resultStr.split(x);\n\t\tconst s = tmp[1] || EMPTY;\n\n\t\tconst l = s.length;\n\t\tconst n = round - l;\n\n\t\tresult = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;\n\t}\n\n\treturn result;\n}\n","import {\n\tARRAY,\n\tBIT,\n\tBITS,\n\tBYTE,\n\tBYTES,\n\tEMPTY,\n\tEXPONENT,\n\tFUNCTION,\n\tINVALID_NUMBER,\n\tINVALID_ROUND,\n\tLOG_10_1000,\n\tLOG_2_1024,\n\tOBJECT,\n\tROUND,\n\tS,\n\tSI_KBIT,\n\tSI_KBYTE,\n\tSPACE,\n\tSTRING,\n\tSTRINGS,\n} from \"./constants.js\";\nimport {\n\tapplyNumberFormatting,\n\tapplyPrecisionHandling,\n\tcalculateOptimizedValue,\n\tgetBaseConfiguration,\n\thandleZeroValue,\n} from \"./helpers.js\";\n\n/**\n * Converts a file size in bytes to a human-readable string with appropriate units\n * @param {number|string|bigint} arg - The file size in bytes to convert\n * @param {Object} [options={}] - Configuration options for formatting\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {string|Array|Object|number} Formatted file size based on output option\n * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid\n * @example\n * filesize(1024) // \"1.02 kB\"\n * filesize(1024, {bits: true}) // \"8.19 kbit\"\n * filesize(1024, {output: \"object\"}) // {value: 1.02, symbol: \"kB\", exponent: 1, unit: \"kB\"}\n */\nexport function filesize(\n\targ,\n\t{\n\t\tbits = false,\n\t\tpad = false,\n\t\tbase = -1,\n\t\tround = 2,\n\t\tlocale = EMPTY,\n\t\tlocaleOptions = {},\n\t\tseparator = EMPTY,\n\t\tspacer = SPACE,\n\t\tsymbols = {},\n\t\tstandard = EMPTY,\n\t\toutput = STRING,\n\t\tfullform = false,\n\t\tfullforms = [],\n\t\texponent = -1,\n\t\troundingMethod = ROUND,\n\t\tprecision = 0,\n\t} = {},\n) {\n\tlet e = exponent,\n\t\tnum = Number(arg),\n\t\tresult = [],\n\t\tval = 0,\n\t\tu = EMPTY;\n\n\t// Optimized base & standard configuration lookup\n\tconst { isDecimal, ceil, actualStandard } = getBaseConfiguration(standard, base);\n\n\tconst full = fullform === true,\n\t\tneg = num < 0,\n\t\troundingFunc = Math[roundingMethod];\n\n\tif (typeof arg !== \"bigint\" && isNaN(arg)) {\n\t\tthrow new TypeError(INVALID_NUMBER);\n\t}\n\n\tif (typeof roundingFunc !== FUNCTION) {\n\t\tthrow new TypeError(INVALID_ROUND);\n\t}\n\n\t// Flipping a negative number to determine the size\n\tif (neg) {\n\t\tnum = -num;\n\t}\n\n\t// Fast path for zero\n\tif (num === 0) {\n\t\treturn handleZeroValue(\n\t\t\tprecision,\n\t\t\tactualStandard,\n\t\t\tbits,\n\t\t\tsymbols,\n\t\t\tfull,\n\t\t\tfullforms,\n\t\t\toutput,\n\t\t\tspacer,\n\t\t);\n\t}\n\n\t// Optimized exponent calculation using pre-computed log values\n\tif (e === -1 || isNaN(e)) {\n\t\te = isDecimal\n\t\t\t? Math.floor(Math.log(num) / LOG_10_1000)\n\t\t\t: Math.floor(Math.log(num) / LOG_2_1024);\n\t\tif (e < 0) {\n\t\t\te = 0;\n\t\t}\n\t}\n\n\t// Exceeding supported length, time to reduce & multiply\n\tif (e > 8) {\n\t\tif (precision > 0) {\n\t\t\tprecision += 8 - e;\n\t\t}\n\t\te = 8;\n\t}\n\n\tconst autoExponent = exponent === -1 || isNaN(exponent);\n\n\tif (output === EXPONENT) {\n\t\treturn e;\n\t}\n\n\t// Calculate value with optimized lookup and bits handling\n\tconst { result: valueResult, e: valueExponent } = calculateOptimizedValue(\n\t\tnum,\n\t\te,\n\t\tisDecimal,\n\t\tbits,\n\t\tceil,\n\t\tautoExponent,\n\t);\n\tval = valueResult;\n\te = valueExponent;\n\n\t// Optimize rounding calculation\n\tconst p = e > 0 && round > 0 ? Math.pow(10, round) : 1;\n\tresult[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p;\n\n\tif (result[0] === ceil && e < 8 && autoExponent) {\n\t\tresult[0] = 1;\n\t\te++;\n\t}\n\n\t// Apply precision handling\n\tif (precision > 0) {\n\t\tconst precisionResult = applyPrecisionHandling(\n\t\t\tresult[0],\n\t\t\tprecision,\n\t\t\te,\n\t\t\tnum,\n\t\t\tisDecimal,\n\t\t\tbits,\n\t\t\tceil,\n\t\t\troundingFunc,\n\t\t\tround,\n\t\t\texponent,\n\t\t);\n\t\tresult[0] = precisionResult.value;\n\t\te = precisionResult.e;\n\t}\n\n\t// Cache symbol lookup\n\tconst symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES];\n\tu = result[1] = isDecimal && e === 1 ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e];\n\n\t// Decorating a 'diff'\n\tif (neg) {\n\t\tresult[0] = -result[0];\n\t}\n\n\t// Applying custom symbol\n\tif (symbols[result[1]]) {\n\t\tresult[1] = symbols[result[1]];\n\t}\n\n\t// Apply locale, separator, and padding formatting\n\tresult[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round);\n\n\tif (full) {\n\t\tresult[1] =\n\t\t\tfullforms[e] ||\n\t\t\tSTRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S);\n\t}\n\n\t// Optimized return logic\n\tif (output === ARRAY) {\n\t\treturn result;\n\t}\n\n\tif (output === OBJECT) {\n\t\treturn {\n\t\t\tvalue: result[0],\n\t\t\tsymbol: result[1],\n\t\t\texponent: e,\n\t\t\tunit: u,\n\t\t};\n\t}\n\n\treturn spacer === SPACE ? `${result[0]} ${result[1]}` : result.join(spacer);\n}\n\n/**\n * Creates a partially applied version of filesize with preset options\n * @param {Object} [options={}] - Configuration options (same as filesize)\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {Function} A function that takes a file size and returns formatted output\n * @example\n * const formatBytes = partial({round: 1, standard: \"iec\"});\n * formatBytes(1024) // \"1 KiB\"\n * formatBytes(2048) // \"2 KiB\"\n * formatBytes(1536) // \"1.5 KiB\"\n */\nexport function partial({\n\tbits = false,\n\tpad = false,\n\tbase = -1,\n\tround = 2,\n\tlocale = EMPTY,\n\tlocaleOptions = {},\n\tseparator = EMPTY,\n\tspacer = SPACE,\n\tsymbols = {},\n\tstandard = EMPTY,\n\toutput = STRING,\n\tfullform = false,\n\tfullforms = [],\n\texponent = -1,\n\troundingMethod = ROUND,\n\tprecision = 0,\n} = {}) {\n\treturn (arg) =>\n\t\tfilesize(arg, {\n\t\t\tbits,\n\t\t\tpad,\n\t\t\tbase,\n\t\t\tround,\n\t\t\tlocale,\n\t\t\tlocaleOptions,\n\t\t\tseparator,\n\t\t\tspacer,\n\t\t\tsymbols,\n\t\t\tstandard,\n\t\t\toutput,\n\t\t\tfullform,\n\t\t\tfullforms,\n\t\t\texponent,\n\t\t\troundingMethod,\n\t\t\tprecision,\n\t\t});\n}\n"],"names":["IEC","JEDEC","SI","BYTE","ARRAY","OBJECT","STRING","EXPONENT","ROUND","STRINGS","symbol","iec","bits","bytes","jedec","fullform","BINARY_POWERS","DECIMAL_POWERS","LOG_2_1024","Math","log","LOG_10_1000","STANDARD_CONFIGS","isDecimal","ceil","actualStandard","calculateOptimizedValue","num","e","autoExponent","result","filesize","arg","pad","base","round","locale","EMPTY","localeOptions","separator","spacer","symbols","standard","output","fullforms","exponent","roundingMethod","precision","Number","val","u","getBaseConfiguration","full","neg","roundingFunc","isNaN","TypeError","value","toPrecision","unit","handleZeroValue","floor","valueResult","valueExponent","p","pow","precisionResult","includes","applyPrecisionHandling","symbolTable","toLocaleString","length","toString","replace","resultStr","x","slice","match","pop","tmp","split","s","l","n","padEnd","applyNumberFormatting","join","partial"],"mappings":";;;;AACO,MAIMA,EAAM,MACNC,EAAQ,QACRC,EAAK,KAKLC,EAAO,OAMPC,EAAQ,QAERC,EAAS,SACTC,EAAS,SAGTC,EAAW,WACXC,EAAQ,QAWRC,EAAU,CACtBC,OAAQ,CACPC,IAAK,CACJC,KAAM,CAAC,MAAO,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,SAC7EC,MAAO,CAAC,IAAK,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,QAE/DC,MAAO,CACNF,KAAM,CAAC,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QACtEC,MAAO,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,QAGzDE,SAAU,CACTJ,IAAK,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QAClEG,MAAO,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,MAAO,QAAS,WAKzDE,EAAgB,CAC5B,EACA,KACA,QACA,WACA,cACA,gBACA,mBACA,oBACA,qBAGYC,EAAiB,CAC7B,EACA,IACA,IACA,IACA,KACA,KACA,KACA,KACA,MAIYC,EAAaC,KAAKC,IAAI,MACtBC,EAAcF,KAAKC,IAAI,KC7D9BE,EAAmB,CACxBpB,CAACA,GAAK,CAAEqB,WAAW,EAAMC,KAAM,IAAMC,eAAgBxB,GACrDD,CAACA,GAAM,CAAEuB,WAAW,EAAOC,KAAM,KAAMC,eAAgBzB,GACvDC,CAACA,GAAQ,CAAEsB,WAAW,EAAOC,KAAM,KAAMC,eAAgBxB,IA6FnD,SAASyB,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,EAAMK,GAAe,GAErF,IAAIC,EAASH,GADHJ,EAAYN,EAAeW,GAAKZ,EAAcY,IAYxD,OATIhB,IACHkB,GAAU,EAEND,GAAgBC,GAAUN,GAAQI,EAAI,IACzCE,GAAUN,EACVI,MAIK,CAAEE,SAAQF,IAClB,CCxEO,SAASG,EACfC,GACApB,KACCA,GAAO,EAAKqB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EFnCmB,IEmCLC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASrC,EAAMS,SACfA,GAAW,EAAK6B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBtC,EAAKuC,UACtBA,EAAY,GACT,CAAA,GAEJ,IAAInB,EAAIiB,EACPlB,EAAMqB,OAAOhB,GACbF,EAAS,GACTmB,EAAM,EACNC,EFrDmB,GEwDpB,MAAM3B,UAAEA,EAASC,KAAEA,EAAIC,eAAEA,GDtDnB,SAA8BiB,EAAUR,GAE9C,OAAIZ,EAAiBoB,GACbpB,EAAiBoB,GAIZ,IAATR,EACI,CAAEX,WAAW,EAAOC,KAAM,KAAMC,eAAgBzB,GAIjD,CAAEuB,WAAW,EAAMC,KAAM,IAAMC,eAAgBxB,EACvD,CCyC6CkD,CAAqBT,EAAUR,GAErEkB,GAAoB,IAAbrC,EACZsC,EAAM1B,EAAM,EACZ2B,EAAenC,KAAK2B,GAErB,GAAmB,iBAARd,GAAoBuB,MAAMvB,GACpC,MAAM,IAAIwB,UF3FkB,kBE8F7B,GF5EuB,mBE4EZF,EACV,MAAM,IAAIE,UF9FiB,2BEuG5B,GALIH,IACH1B,GAAOA,GAII,IAARA,EACH,OD/CK,SACNoB,EACAtB,EACAb,EACA6B,EACAW,EACAR,EACAD,EACAH,EACA9B,GAEA,MAAM+C,EAAQV,EAAY,GAAI,GAAIW,YAAYX,GAAa,EAE3D,OAAIJ,IAAWpC,EACP,GAIHG,IACJA,EAASE,EACNH,EAAQC,OAAOe,GAAgBb,KAAK,GACpCH,EAAQC,OAAOe,GAAgBZ,MAAM,IAIrC4B,EAAQ/B,KACXA,EAAS+B,EAAQ/B,IAId0C,IACH1C,EAASkC,EAAU,IAAMnC,EAAQM,SAASU,GAAgB,IAAMb,EDhF/C,MCgF4DT,IAI1EwC,IAAWvC,EACP,CAACqD,EAAO/C,GAGZiC,IAAWtC,EACP,CAAEoD,QAAO/C,SAAQmC,SAAU,EAAGc,KAAMjD,GAGrC+C,EAAQjB,EAAS9B,EACzB,CCGSkD,CACNb,EACAtB,EACAb,EACA6B,EACAW,EACAR,EACAD,EACAH,KAKQ,IAANZ,GAAY2B,MAAM3B,MACrBA,EAAIL,EACDJ,KAAK0C,MAAM1C,KAAKC,IAAIO,GAAON,GAC3BF,KAAK0C,MAAM1C,KAAKC,IAAIO,GAAOT,GAC1BU,EAAI,IACPA,EAAI,IAKFA,EAAI,IACHmB,EAAY,IACfA,GAAa,EAAInB,GAElBA,EAAI,GAGL,MAAMC,OAAegB,GAAmBU,MAAMV,GAE9C,GAAIF,IAAWpC,EACd,OAAOqB,EAIR,MAAQE,OAAQgC,EAAalC,EAAGmC,GAAkBrC,EACjDC,EACAC,EACAL,EACAX,EACAY,EACAK,GAEDoB,EAAMa,EACNlC,EAAImC,EAGJ,MAAMC,EAAIpC,EAAI,GAAKO,EAAQ,EAAIhB,KAAK8C,IAAI,GAAI9B,GAAS,EASrD,GARAL,EAAO,GAAW,IAANkC,EAAUV,EAAaL,GAAOK,EAAaL,EAAMe,GAAKA,EAE9DlC,EAAO,KAAON,GAAQI,EAAI,GAAKC,IAClCC,EAAO,GAAK,EACZF,KAIGmB,EAAY,EAAG,CAClB,MAAMmB,EDpBD,SACNT,EACAV,EACAnB,EACAD,EACAJ,EACAX,EACAY,EACA8B,EACAnB,EACAU,GAEA,IAAIf,EAAS2B,EAAMC,YAAYX,GAE/B,MAAMlB,OAAegB,GAAmBU,MAAMV,GAG9C,GAAIf,EAAOqC,SDtIK,MCsIUvC,EAAI,GAAKC,EAAc,CAChDD,IACA,MAAQE,OAAQgC,GAAgBpC,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,GAC3EwC,EAAI7B,EAAQ,EAAIhB,KAAK8C,IAAI,GAAI9B,GAAS,EAC5CL,GAAgB,IAANkC,EAAUV,EAAaQ,GAAeR,EAAaQ,EAAcE,GAAKA,GAAGN,YAClFX,EAEF,CAEA,MAAO,CAAEU,MAAO3B,EAAQF,IACzB,CCP0BwC,CACvBtC,EAAO,GACPiB,EACAnB,EACAD,EACAJ,EACAX,EACAY,EACA8B,EACAnB,EACAU,GAEDf,EAAO,GAAKoC,EAAgBT,MAC5B7B,EAAIsC,EAAgBtC,CACrB,CAGA,MAAMyC,EAAc5D,EAAQC,OAAOe,GAAgBb,EF3KhC,OAEC,SEgMpB,OAtBAsC,EAAIpB,EAAO,GAAKP,GAAmB,IAANK,EAAWhB,EFzKlB,OACC,KEwK8CyD,EAAYzC,GAG7EyB,IACHvB,EAAO,IAAMA,EAAO,IAIjBW,EAAQX,EAAO,MAClBA,EAAO,GAAKW,EAAQX,EAAO,KAI5BA,EAAO,GDZD,SAA+B2B,EAAOrB,EAAQE,EAAeC,EAAWN,EAAKE,GACnF,IAAIL,EAAS2B,EAYb,IATe,IAAXrB,EACHN,EAASA,EAAOwC,iBACNlC,EAAOmC,OAAS,EAC1BzC,EAASA,EAAOwC,eAAelC,EAAQE,GAC7BC,EAAUgC,OAAS,IAC7BzC,EAASA,EAAO0C,WAAWC,QDnKP,ICmKuBlC,IAIxCN,GAAOE,EAAQ,EAAG,CACrB,MAAMuC,EAAY5C,EAAO0C,WACnBG,EAAIpC,IAAcmC,EAAUE,MAAM,GAAGC,MAAM,UAAY,IAAIC,ODzK7C,IC0KdC,EAAML,EAAUM,MAAML,GACtBM,EAAIF,EAAI,ID5KK,GC8KbG,EAAID,EAAEV,OACNY,EAAIhD,EAAQ+C,EAElBpD,EAAS,GAAGiD,EAAI,KAAKJ,IAAIM,EAAEG,OAAOF,EAAIC,ED7KpB,MC8KnB,CAEA,OAAOrD,CACR,CCdauD,CAAsBvD,EAAO,GAAIM,EAAQE,EAAeC,EAAWN,EAAKE,GAEhFiB,IACHtB,EAAO,GACNc,EAAUhB,IACVnB,EAAQM,SAASU,GAAgBG,IAAMhB,EF/LvB,ME+LoCT,IAAuB,IAAd2B,EAAO,GF5KlD,GAEJ,ME8KZa,IAAWvC,EACP0B,EAGJa,IAAWtC,EACP,CACNoD,MAAO3B,EAAO,GACdpB,OAAQoB,EAAO,GACfe,SAAUjB,EACV+B,KAAMT,GFtLY,ME0LbV,EAAmB,GAAGV,EAAO,MAAMA,EAAO,KAAOA,EAAOwD,KAAK9C,EACrE,CA4BO,SAAS+C,GAAQ3E,KACvBA,GAAO,EAAKqB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EF/NoB,IE+NNC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASrC,EAAMS,SACfA,GAAW,EAAK6B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBtC,EAAKuC,UACtBA,EAAY,GACT,IACH,OAAQf,GACPD,EAASC,EAAK,CACbpB,OACAqB,MACAC,OACAC,QACAC,SACAE,gBACAC,YACAC,SACAC,UACAC,WACAC,SACA5B,WACA6B,YACAC,WACAC,iBACAC,aAEH,QAAAhB,cAAAwD"} diff --git a/dist/filesize.umd.js b/dist/filesize.umd.js index a43a08f..d0f44af 100644 --- a/dist/filesize.umd.js +++ b/dist/filesize.umd.js @@ -1,7 +1,7 @@ /** * filesize * - * @copyright 2025 Jason Mulligan + * @copyright 2026 Jason Mulligan * @license BSD-3-Clause * @version 11.0.13 */ @@ -45,17 +45,17 @@ const STRINGS = { symbol: { iec: { bits: ["bit", "Kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"], - bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] + bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], }, jedec: { bits: ["bit", "Kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"], - bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] - } + bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], + }, }, fullform: { iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"], - jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"] - } + jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"], + }, }; // Pre-computed lookup tables for performance optimization @@ -68,7 +68,7 @@ const BINARY_POWERS = [ 1125899906842624, // 2^50 1152921504606846976, // 2^60 1180591620717411303424, // 2^70 - 1208925819614629174706176 // 2^80 + 1208925819614629174706176, // 2^80 ]; const DECIMAL_POWERS = [ @@ -80,16 +80,16 @@ const DECIMAL_POWERS = [ 1000000000000000, // 10^15 1000000000000000000, // 10^18 1000000000000000000000, // 10^21 - 1000000000000000000000000 // 10^24 + 1000000000000000000000000, // 10^24 ]; // Pre-computed log values for faster exponent calculation const LOG_2_1024 = Math.log(1024); const LOG_10_1000 = Math.log(1000);// Cached configuration lookup for better performance const STANDARD_CONFIGS = { - [SI]: {isDecimal: true, ceil: 1000, actualStandard: JEDEC}, - [IEC]: {isDecimal: false, ceil: 1024, actualStandard: IEC}, - [JEDEC]: {isDecimal: false, ceil: 1024, actualStandard: JEDEC} + [SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC }, + [IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC }, + [JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC }, }; /** @@ -98,7 +98,7 @@ const STANDARD_CONFIGS = { * @param {number} base - Base number * @returns {Object} Configuration object */ -function getBaseConfiguration (standard, base) { +function getBaseConfiguration(standard, base) { // Use cached lookup table for better performance if (STANDARD_CONFIGS[standard]) { return STANDARD_CONFIGS[standard]; @@ -106,11 +106,11 @@ function getBaseConfiguration (standard, base) { // Base override if (base === 2) { - return {isDecimal: false, ceil: 1024, actualStandard: IEC}; + return { isDecimal: false, ceil: 1024, actualStandard: IEC }; } // Default - return {isDecimal: true, ceil: 1000, actualStandard: JEDEC}; + return { isDecimal: true, ceil: 1000, actualStandard: JEDEC }; } /** @@ -123,34 +123,53 @@ function getBaseConfiguration (standard, base) { * @param {Array} fullforms - Custom full forms * @param {string} output - Output format * @param {string} spacer - Spacer character + * @param {string} [symbol] - Symbol to use (defaults based on bits/standard) * @returns {string|Array|Object|number} Formatted result */ -function handleZeroValue (precision, actualStandard, bits, symbols, full, fullforms, output, spacer) { - const result = []; - result[0] = precision > 0 ? (0).toPrecision(precision) : 0; - const u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0]; +function handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + symbol, +) { + const value = precision > 0 ? (0).toPrecision(precision) : 0; if (output === EXPONENT) { return 0; } + // Set default symbol if not provided + if (!symbol) { + symbol = bits + ? STRINGS.symbol[actualStandard].bits[0] + : STRINGS.symbol[actualStandard].bytes[0]; + } + // Apply symbol customization - if (symbols[result[1]]) { - result[1] = symbols[result[1]]; + if (symbols[symbol]) { + symbol = symbols[symbol]; } // Apply full form if (full) { - result[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); + symbol = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); } // Return in requested format - return output === ARRAY ? result : output === OBJECT ? { - value: result[0], - symbol: result[1], - exponent: 0, - unit: u - } : result.join(spacer); + if (output === ARRAY) { + return [value, symbol]; + } + + if (output === OBJECT) { + return { value, symbol, exponent: 0, unit: symbol }; + } + + return value + spacer + symbol; } /** @@ -160,22 +179,23 @@ function handleZeroValue (precision, actualStandard, bits, symbols, full, fullfo * @param {boolean} isDecimal - Whether to use decimal powers * @param {boolean} bits - Whether to calculate bits * @param {number} ceil - Ceiling value for auto-increment - * @returns {Object} Object with val and e properties + * @param {boolean} autoExponent - Whether exponent is auto (-1 or NaN) + * @returns {Object} Object with result and e properties */ -function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { +function calculateOptimizedValue(num, e, isDecimal, bits, ceil, autoExponent = true) { const d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e]; let result = num / d; if (bits) { result *= 8; - // Handle auto-increment for bits - if (result >= ceil && e < 8) { + // Handle auto-increment for bits (only when exponent is auto) + if (autoExponent && result >= ceil && e < 8) { result /= ceil; e++; } } - return {result, e}; + return { result, e }; } /** @@ -189,20 +209,36 @@ function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { * @param {number} ceil - Ceiling value * @param {Function} roundingFunc - Rounding function * @param {number} round - Round value + * @param {number} exponent - Forced exponent (-1 for auto) * @returns {Object} Object with value and e properties */ -function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil, roundingFunc, round) { +function applyPrecisionHandling( + value, + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, +) { let result = value.toPrecision(precision); + const autoExponent = exponent === -1 || isNaN(exponent); + // Handle scientific notation by recalculating with incremented exponent - if (result.includes(E) && e < 8) { + if (result.includes(E) && e < 8 && autoExponent) { e++; - const {result: valueResult} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult } = calculateOptimizedValue(num, e, isDecimal, bits, ceil); const p = round > 0 ? Math.pow(10, round) : 1; - result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(precision); + result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision( + precision, + ); } - return {value: result, e}; + return { value: result, e }; } /** @@ -215,7 +251,7 @@ function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil * @param {number} round - Round value * @returns {string|number} Formatted value */ -function applyNumberFormatting (value, locale, localeOptions, separator, pad, round) { +function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) { let result = value; // Apply locale formatting @@ -230,9 +266,10 @@ function applyNumberFormatting (value, locale, localeOptions, separator, pad, ro // Apply padding if (pad && round > 0) { const resultStr = result.toString(); - const x = separator || ((resultStr.match(/(\D)/g) || []).pop() || PERIOD); + const x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD; const tmp = resultStr.split(x); const s = tmp[1] || EMPTY; + const l = s.length; const n = round - l; @@ -263,28 +300,31 @@ function applyNumberFormatting (value, locale, localeOptions, separator, pad, ro * @returns {string|Array|Object|number} Formatted file size based on output option * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid * @example - * filesize(1024) // "1 KB" - * filesize(1024, {bits: true}) // "8 Kb" - * filesize(1024, {output: "object"}) // {value: 1, symbol: "KB", exponent: 1, unit: "KB"} + * filesize(1024) // "1.02 kB" + * filesize(1024, {bits: true}) // "8.19 kbit" + * filesize(1024, {output: "object"}) // {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"} */ -function filesize (arg, { - bits = false, - pad = false, - base = -1, - round = 2, - locale = EMPTY, - localeOptions = {}, - separator = EMPTY, - spacer = SPACE, - symbols = {}, - standard = EMPTY, - output = STRING, - fullform = false, - fullforms = [], - exponent = -1, - roundingMethod = ROUND, - precision = 0 -} = {}) { +function filesize( + arg, + { + bits = false, + pad = false, + base = -1, + round = 2, + locale = EMPTY, + localeOptions = {}, + separator = EMPTY, + spacer = SPACE, + symbols = {}, + standard = EMPTY, + output = STRING, + fullform = false, + fullforms = [], + exponent = -1, + roundingMethod = ROUND, + precision = 0, + } = {}, +) { let e = exponent, num = Number(arg), result = [], @@ -292,7 +332,7 @@ function filesize (arg, { u = EMPTY; // Optimized base & standard configuration lookup - const {isDecimal, ceil, actualStandard} = getBaseConfiguration(standard, base); + const { isDecimal, ceil, actualStandard } = getBaseConfiguration(standard, base); const full = fullform === true, neg = num < 0, @@ -313,12 +353,23 @@ function filesize (arg, { // Fast path for zero if (num === 0) { - return handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer); + return handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + ); } // Optimized exponent calculation using pre-computed log values if (e === -1 || isNaN(e)) { - e = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024); + e = isDecimal + ? Math.floor(Math.log(num) / LOG_10_1000) + : Math.floor(Math.log(num) / LOG_2_1024); if (e < 0) { e = 0; } @@ -332,12 +383,21 @@ function filesize (arg, { e = 8; } + const autoExponent = exponent === -1 || isNaN(exponent); + if (output === EXPONENT) { return e; } // Calculate value with optimized lookup and bits handling - const {result: valueResult, e: valueExponent} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult, e: valueExponent } = calculateOptimizedValue( + num, + e, + isDecimal, + bits, + ceil, + autoExponent, + ); val = valueResult; e = valueExponent; @@ -345,21 +405,32 @@ function filesize (arg, { const p = e > 0 && round > 0 ? Math.pow(10, round) : 1; result[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p; - if (result[0] === ceil && e < 8 && exponent === -1) { + if (result[0] === ceil && e < 8 && autoExponent) { result[0] = 1; e++; } // Apply precision handling if (precision > 0) { - const precisionResult = applyPrecisionHandling(result[0], precision, e, num, isDecimal, bits, ceil, roundingFunc, round); + const precisionResult = applyPrecisionHandling( + result[0], + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, + ); result[0] = precisionResult.value; e = precisionResult.e; } // Cache symbol lookup const symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES]; - u = result[1] = (isDecimal && e === 1) ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; + u = result[1] = isDecimal && e === 1 ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; // Decorating a 'diff' if (neg) { @@ -375,7 +446,9 @@ function filesize (arg, { result[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round); if (full) { - result[1] = fullforms[e] || STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); + result[1] = + fullforms[e] || + STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); } // Optimized return logic @@ -388,7 +461,7 @@ function filesize (arg, { value: result[0], symbol: result[1], exponent: e, - unit: u + unit: u, }; } @@ -397,7 +470,7 @@ function filesize (arg, { /** * Creates a partially applied version of filesize with preset options - * @param {Object} [options={}] - Default options to apply to the returned function + * @param {Object} [options={}] - Configuration options (same as filesize) * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto) @@ -416,12 +489,12 @@ function filesize (arg, { * @param {number} [options.precision=0] - Number of significant digits (0 for auto) * @returns {Function} A function that takes a file size and returns formatted output * @example - * const formatBytes = partial({round: 1, standard: "IEC"}); - * formatBytes(1024) // "1.0 KiB" - * formatBytes(2048) // "2.0 KiB" + * const formatBytes = partial({round: 1, standard: "iec"}); + * formatBytes(1024) // "1 KiB" + * formatBytes(2048) // "2 KiB" + * formatBytes(1536) // "1.5 KiB" */ -// Partial application for functional programming -function partial ({ +function partial({ bits = false, pad = false, base = -1, @@ -437,24 +510,25 @@ function partial ({ fullforms = [], exponent = -1, roundingMethod = ROUND, - precision = 0 + precision = 0, } = {}) { - return arg => filesize(arg, { - bits, - pad, - base, - round, - locale, - localeOptions, - separator, - spacer, - symbols, - standard, - output, - fullform, - fullforms, - exponent, - roundingMethod, - precision - }); -}exports.filesize=filesize;exports.partial=partial;})); \ No newline at end of file + return (arg) => + filesize(arg, { + bits, + pad, + base, + round, + locale, + localeOptions, + separator, + spacer, + symbols, + standard, + output, + fullform, + fullforms, + exponent, + roundingMethod, + precision, + }); +}exports.filesize=filesize;exports.partial=partial;})); diff --git a/dist/filesize.umd.min.js b/dist/filesize.umd.min.js index 9da7fae..45b43ca 100644 --- a/dist/filesize.umd.min.js +++ b/dist/filesize.umd.min.js @@ -1,5 +1,5 @@ /*! - 2025 Jason Mulligan + 2026 Jason Mulligan @version 11.0.13 */ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).filesize={})}(this,function(t){"use strict";const e="iec",i="jedec",n="si",o="bits",a="byte",r="bytes",s="array",l="object",u="string",c="exponent",b="round",d={symbol:{iec:{bits:["bit","Kibit","Mibit","Gibit","Tibit","Pibit","Eibit","Zibit","Yibit"],bytes:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"]},jedec:{bits:["bit","Kbit","Mbit","Gbit","Tbit","Pbit","Ebit","Zbit","Ybit"],bytes:["B","KB","MB","GB","TB","PB","EB","ZB","YB"]}},fullform:{iec:["","kibi","mebi","gibi","tebi","pebi","exbi","zebi","yobi"],jedec:["","kilo","mega","giga","tera","peta","exa","zetta","yotta"]}},f=[1,1024,1048576,1073741824,1099511627776,0x4000000000000,0x1000000000000000,11805916207174113e5,12089258196146292e8],p=[1,1e3,1e6,1e9,1e12,1e15,1e18,1e21,1e24],m=Math.log(1024),y=Math.log(1e3),B={[n]:{isDecimal:!0,ceil:1e3,actualStandard:i},[e]:{isDecimal:!1,ceil:1024,actualStandard:e},[i]:{isDecimal:!1,ceil:1024,actualStandard:i}};function h(t,e,i,n,o){let a=t/(i?p[e]:f[e]);return n&&(a*=8,a>=o&&e<8&&(a/=o,e++)),{result:a,e:e}}function M(t,{bits:n=!1,pad:f=!1,base:p=-1,round:M=2,locale:g="",localeOptions:x={},separator:T="",spacer:D=" ",symbols:E={},standard:S="",output:j=u,fullform:v=!1,fullforms:N=[],exponent:$=-1,roundingMethod:k=b,precision:w=0}={}){let G=$,K=Number(t),P=[],Y=0,Z="";const{isDecimal:O,ceil:z,actualStandard:I}=function(t,n){return B[t]?B[t]:2===n?{isDecimal:!1,ceil:1024,actualStandard:e}:{isDecimal:!0,ceil:1e3,actualStandard:i}}(S,p),q=!0===v,A=K<0,C=Math[k];if("bigint"!=typeof t&&isNaN(t))throw new TypeError("Invalid number");if("function"!=typeof C)throw new TypeError("Invalid rounding method");if(A&&(K=-K),0===K)return function(t,e,i,n,u,b,f,p){const m=[];m[0]=t>0?(0).toPrecision(t):0;const y=m[1]=d.symbol[e][i?o:r][0];return f===c?0:(n[m[1]]&&(m[1]=n[m[1]]),u&&(m[1]=b[0]||d.fullform[e][0]+(i?"bit":a)),f===s?m:f===l?{value:m[0],symbol:m[1],exponent:0,unit:y}:m.join(p))}(w,I,n,E,q,N,j,D);if((-1===G||isNaN(G))&&(G=O?Math.floor(Math.log(K)/y):Math.floor(Math.log(K)/m),G<0&&(G=0)),G>8&&(w>0&&(w+=8-G),G=8),j===c)return G;const{result:F,e:H}=h(K,G,O,n,z);Y=F,G=H;const J=G>0&&M>0?Math.pow(10,M):1;if(P[0]=1===J?C(Y):C(Y*J)/J,P[0]===z&&G<8&&-1===$&&(P[0]=1,G++),w>0){const t=function(t,e,i,n,o,a,r,s,l){let u=t.toPrecision(e);if(u.includes("e")&&i<8){i++;const{result:t}=h(n,i,o,a,r),c=l>0?Math.pow(10,l):1;u=(1===c?s(t):s(t*c)/c).toPrecision(e)}return{value:u,e:i}}(P[0],w,G,K,O,n,z,C,M);P[0]=t.value,G=t.e}const L=d.symbol[I][n?o:r];return Z=P[1]=O&&1===G?n?"kbit":"kB":L[G],A&&(P[0]=-P[0]),E[P[1]]&&(P[1]=E[P[1]]),P[0]=function(t,e,i,n,o,a){let r=t;if(!0===e?r=r.toLocaleString():e.length>0?r=r.toLocaleString(e,i):n.length>0&&(r=r.toString().replace(".",n)),o&&a>0){const t=r.toString(),e=n||(t.match(/(\D)/g)||[]).pop()||".",i=t.split(e),o=i[1]||"",s=o.length,l=a-s;r=`${i[0]}${e}${o.padEnd(s+l,"0")}`}return r}(P[0],g,x,T,f,M),q&&(P[1]=N[G]||d.fullform[I][G]+(n?"bit":a)+(1===P[0]?"":"s")),j===s?P:j===l?{value:P[0],symbol:P[1],exponent:G,unit:Z}:" "===D?`${P[0]} ${P[1]}`:P.join(D)}t.filesize=M,t.partial=function({bits:t=!1,pad:e=!1,base:i=-1,round:n=2,locale:o="",localeOptions:a={},separator:r="",spacer:s=" ",symbols:l={},standard:c="",output:d=u,fullform:f=!1,fullforms:p=[],exponent:m=-1,roundingMethod:y=b,precision:B=0}={}){return u=>M(u,{bits:t,pad:e,base:i,round:n,locale:o,localeOptions:a,separator:r,spacer:s,symbols:l,standard:c,output:d,fullform:f,fullforms:p,exponent:m,roundingMethod:y,precision:B})}});//# sourceMappingURL=filesize.umd.min.js.map +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).filesize={})}(this,function(t){"use strict";const e="iec",i="jedec",n="si",o="byte",a="array",r="object",s="string",l="exponent",c="round",u={symbol:{iec:{bits:["bit","Kibit","Mibit","Gibit","Tibit","Pibit","Eibit","Zibit","Yibit"],bytes:["B","KiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB"]},jedec:{bits:["bit","Kbit","Mbit","Gbit","Tbit","Pbit","Ebit","Zbit","Ybit"],bytes:["B","KB","MB","GB","TB","PB","EB","ZB","YB"]}},fullform:{iec:["","kibi","mebi","gibi","tebi","pebi","exbi","zebi","yobi"],jedec:["","kilo","mega","giga","tera","peta","exa","zetta","yotta"]}},b=[1,1024,1048576,1073741824,1099511627776,0x4000000000000,0x1000000000000000,11805916207174113e5,12089258196146292e8],d=[1,1e3,1e6,1e9,1e12,1e15,1e18,1e21,1e24],f=Math.log(1024),p=Math.log(1e3),m={[n]:{isDecimal:!0,ceil:1e3,actualStandard:i},[e]:{isDecimal:!1,ceil:1024,actualStandard:e},[i]:{isDecimal:!1,ceil:1024,actualStandard:i}};function y(t,e,i,n,o,a=!0){let r=t/(i?d[e]:b[e]);return n&&(r*=8,a&&r>=o&&e<8&&(r/=o,e++)),{result:r,e:e}}function B(t,{bits:n=!1,pad:b=!1,base:d=-1,round:B=2,locale:h="",localeOptions:M={},separator:g="",spacer:x=" ",symbols:N={},standard:T="",output:D=s,fullform:E=!1,fullforms:S=[],exponent:j=-1,roundingMethod:v=c,precision:$=0}={}){let k=j,w=Number(t),G=[],K=0,P="";const{isDecimal:Y,ceil:Z,actualStandard:O}=function(t,n){return m[t]?m[t]:2===n?{isDecimal:!1,ceil:1024,actualStandard:e}:{isDecimal:!0,ceil:1e3,actualStandard:i}}(T,d),z=!0===E,I=w<0,q=Math[v];if("bigint"!=typeof t&&isNaN(t))throw new TypeError("Invalid number");if("function"!=typeof q)throw new TypeError("Invalid rounding method");if(I&&(w=-w),0===w)return function(t,e,i,n,s,c,b,d,f){const p=t>0?(0).toPrecision(t):0;return b===l?0:(f||(f=i?u.symbol[e].bits[0]:u.symbol[e].bytes[0]),n[f]&&(f=n[f]),s&&(f=c[0]||u.fullform[e][0]+(i?"bit":o)),b===a?[p,f]:b===r?{value:p,symbol:f,exponent:0,unit:f}:p+d+f)}($,O,n,N,z,S,D,x);(-1===k||isNaN(k))&&(k=Y?Math.floor(Math.log(w)/p):Math.floor(Math.log(w)/f),k<0&&(k=0)),k>8&&($>0&&($+=8-k),k=8);const A=-1===j||isNaN(j);if(D===l)return k;const{result:C,e:F}=y(w,k,Y,n,Z,A);K=C,k=F;const H=k>0&&B>0?Math.pow(10,B):1;if(G[0]=1===H?q(K):q(K*H)/H,G[0]===Z&&k<8&&A&&(G[0]=1,k++),$>0){const t=function(t,e,i,n,o,a,r,s,l,c){let u=t.toPrecision(e);const b=-1===c||isNaN(c);if(u.includes("e")&&i<8&&b){i++;const{result:t}=y(n,i,o,a,r),c=l>0?Math.pow(10,l):1;u=(1===c?s(t):s(t*c)/c).toPrecision(e)}return{value:u,e:i}}(G[0],$,k,w,Y,n,Z,q,B,j);G[0]=t.value,k=t.e}const J=u.symbol[O][n?"bits":"bytes"];return P=G[1]=Y&&1===k?n?"kbit":"kB":J[k],I&&(G[0]=-G[0]),N[G[1]]&&(G[1]=N[G[1]]),G[0]=function(t,e,i,n,o,a){let r=t;if(!0===e?r=r.toLocaleString():e.length>0?r=r.toLocaleString(e,i):n.length>0&&(r=r.toString().replace(".",n)),o&&a>0){const t=r.toString(),e=n||(t.slice(1).match(/[.,]/g)||[]).pop()||".",i=t.split(e),o=i[1]||"",s=o.length,l=a-s;r=`${i[0]}${e}${o.padEnd(s+l,"0")}`}return r}(G[0],h,M,g,b,B),z&&(G[1]=S[k]||u.fullform[O][k]+(n?"bit":o)+(1===G[0]?"":"s")),D===a?G:D===r?{value:G[0],symbol:G[1],exponent:k,unit:P}:" "===x?`${G[0]} ${G[1]}`:G.join(x)}t.filesize=B,t.partial=function({bits:t=!1,pad:e=!1,base:i=-1,round:n=2,locale:o="",localeOptions:a={},separator:r="",spacer:l=" ",symbols:u={},standard:b="",output:d=s,fullform:f=!1,fullforms:p=[],exponent:m=-1,roundingMethod:y=c,precision:h=0}={}){return s=>B(s,{bits:t,pad:e,base:i,round:n,locale:o,localeOptions:a,separator:r,spacer:l,symbols:u,standard:b,output:d,fullform:f,fullforms:p,exponent:m,roundingMethod:y,precision:h})}});//# sourceMappingURL=filesize.umd.min.js.map diff --git a/dist/filesize.umd.min.js.map b/dist/filesize.umd.min.js.map index f3241d1..c3c37ef 100644 --- a/dist/filesize.umd.min.js.map +++ b/dist/filesize.umd.min.js.map @@ -1 +1 @@ -{"version":3,"file":"filesize.umd.min.js","sources":["../src/constants.js","../src/helpers.js","../src/filesize.js"],"sourcesContent":["// Error Messages\nexport const INVALID_NUMBER = \"Invalid number\";\nexport const INVALID_ROUND = \"Invalid rounding method\";\n\n// Standard Types\nexport const IEC = \"iec\";\nexport const JEDEC = \"jedec\";\nexport const SI = \"si\";\n\n// Unit Types\nexport const BIT = \"bit\";\nexport const BITS = \"bits\";\nexport const BYTE = \"byte\";\nexport const BYTES = \"bytes\";\nexport const SI_KBIT = \"kbit\";\nexport const SI_KBYTE = \"kB\";\n\n// Output Format Types\nexport const ARRAY = \"array\";\nexport const FUNCTION = \"function\";\nexport const OBJECT = \"object\";\nexport const STRING = \"string\";\n\n// Processing Constants\nexport const EXPONENT = \"exponent\";\nexport const ROUND = \"round\";\n\n// Special Characters and Values\nexport const E = \"e\";\nexport const EMPTY = \"\";\nexport const PERIOD = \".\";\nexport const S = \"s\";\nexport const SPACE = \" \";\nexport const ZERO = \"0\";\n\n// Data Structures\nexport const STRINGS = {\n\tsymbol: {\n\t\tiec: {\n\t\t\tbits: [\"bit\", \"Kibit\", \"Mibit\", \"Gibit\", \"Tibit\", \"Pibit\", \"Eibit\", \"Zibit\", \"Yibit\"],\n\t\t\tbytes: [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\", \"ZiB\", \"YiB\"]\n\t\t},\n\t\tjedec: {\n\t\t\tbits: [\"bit\", \"Kbit\", \"Mbit\", \"Gbit\", \"Tbit\", \"Pbit\", \"Ebit\", \"Zbit\", \"Ybit\"],\n\t\t\tbytes: [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"]\n\t\t}\n\t},\n\tfullform: {\n\t\tiec: [\"\", \"kibi\", \"mebi\", \"gibi\", \"tebi\", \"pebi\", \"exbi\", \"zebi\", \"yobi\"],\n\t\tjedec: [\"\", \"kilo\", \"mega\", \"giga\", \"tera\", \"peta\", \"exa\", \"zetta\", \"yotta\"]\n\t}\n};\n\n// Pre-computed lookup tables for performance optimization\nexport const BINARY_POWERS = [\n\t1, // 2^0\n\t1024, // 2^10\n\t1048576, // 2^20\n\t1073741824, // 2^30\n\t1099511627776, // 2^40\n\t1125899906842624, // 2^50\n\t1152921504606846976, // 2^60\n\t1180591620717411303424, // 2^70\n\t1208925819614629174706176 // 2^80\n];\n\nexport const DECIMAL_POWERS = [\n\t1, // 10^0\n\t1000, // 10^3\n\t1000000, // 10^6\n\t1000000000, // 10^9\n\t1000000000000, // 10^12\n\t1000000000000000, // 10^15\n\t1000000000000000000, // 10^18\n\t1000000000000000000000, // 10^21\n\t1000000000000000000000000 // 10^24\n];\n\n// Pre-computed log values for faster exponent calculation\nexport const LOG_2_1024 = Math.log(1024);\nexport const LOG_10_1000 = Math.log(1000);\n","import {\n\tARRAY,\n\tBINARY_POWERS,\n\tBIT,\n\tBITS,\n\tBYTE,\n\tBYTES,\n\tDECIMAL_POWERS,\n\tE,\n\tEMPTY,\n\tEXPONENT,\n\tIEC,\n\tJEDEC,\n\tOBJECT,\n\tPERIOD,\n\tSI,\n\tSTRINGS,\n\tZERO\n} from \"./constants.js\";\n\n// Cached configuration lookup for better performance\nconst STANDARD_CONFIGS = {\n\t[SI]: {isDecimal: true, ceil: 1000, actualStandard: JEDEC},\n\t[IEC]: {isDecimal: false, ceil: 1024, actualStandard: IEC},\n\t[JEDEC]: {isDecimal: false, ceil: 1024, actualStandard: JEDEC}\n};\n\n/**\n * Optimized base configuration lookup\n * @param {string} standard - Standard type\n * @param {number} base - Base number\n * @returns {Object} Configuration object\n */\nexport function getBaseConfiguration (standard, base) {\n\t// Use cached lookup table for better performance\n\tif (STANDARD_CONFIGS[standard]) {\n\t\treturn STANDARD_CONFIGS[standard];\n\t}\n\n\t// Base override\n\tif (base === 2) {\n\t\treturn {isDecimal: false, ceil: 1024, actualStandard: IEC};\n\t}\n\n\t// Default\n\treturn {isDecimal: true, ceil: 1000, actualStandard: JEDEC};\n}\n\n/**\n * Optimized zero value handling\n * @param {number} precision - Precision value\n * @param {string} actualStandard - Standard to use\n * @param {boolean} bits - Whether to use bits\n * @param {Object} symbols - Custom symbols\n * @param {boolean} full - Whether to use full form\n * @param {Array} fullforms - Custom full forms\n * @param {string} output - Output format\n * @param {string} spacer - Spacer character\n * @returns {string|Array|Object|number} Formatted result\n */\nexport function handleZeroValue (precision, actualStandard, bits, symbols, full, fullforms, output, spacer) {\n\tconst result = [];\n\tresult[0] = precision > 0 ? (0).toPrecision(precision) : 0;\n\tconst u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0];\n\n\tif (output === EXPONENT) {\n\t\treturn 0;\n\t}\n\n\t// Apply symbol customization\n\tif (symbols[result[1]]) {\n\t\tresult[1] = symbols[result[1]];\n\t}\n\n\t// Apply full form\n\tif (full) {\n\t\tresult[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE);\n\t}\n\n\t// Return in requested format\n\treturn output === ARRAY ? result : output === OBJECT ? {\n\t\tvalue: result[0],\n\t\tsymbol: result[1],\n\t\texponent: 0,\n\t\tunit: u\n\t} : result.join(spacer);\n}\n\n/**\n * Optimized value calculation with bits handling\n * @param {number} num - Input number\n * @param {number} e - Exponent\n * @param {boolean} isDecimal - Whether to use decimal powers\n * @param {boolean} bits - Whether to calculate bits\n * @param {number} ceil - Ceiling value for auto-increment\n * @returns {Object} Object with val and e properties\n */\nexport function calculateOptimizedValue (num, e, isDecimal, bits, ceil) {\n\tconst d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e];\n\tlet result = num / d;\n\n\tif (bits) {\n\t\tresult *= 8;\n\t\t// Handle auto-increment for bits\n\t\tif (result >= ceil && e < 8) {\n\t\t\tresult /= ceil;\n\t\t\te++;\n\t\t}\n\t}\n\n\treturn {result, e};\n}\n\n/**\n * Optimized precision handling with scientific notation correction\n * @param {number} value - Current value\n * @param {number} precision - Precision to apply\n * @param {number} e - Current exponent\n * @param {number} num - Original number\n * @param {boolean} isDecimal - Whether using decimal base\n * @param {boolean} bits - Whether calculating bits\n * @param {number} ceil - Ceiling value\n * @param {Function} roundingFunc - Rounding function\n * @param {number} round - Round value\n * @returns {Object} Object with value and e properties\n */\nexport function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil, roundingFunc, round) {\n\tlet result = value.toPrecision(precision);\n\n\t// Handle scientific notation by recalculating with incremented exponent\n\tif (result.includes(E) && e < 8) {\n\t\te++;\n\t\tconst {result: valueResult} = calculateOptimizedValue(num, e, isDecimal, bits, ceil);\n\t\tconst p = round > 0 ? Math.pow(10, round) : 1;\n\t\tresult = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(precision);\n\t}\n\n\treturn {value: result, e};\n}\n\n/**\n * Optimized number formatting with locale, separator, and padding\n * @param {number|string} value - Value to format\n * @param {string|boolean} locale - Locale setting\n * @param {Object} localeOptions - Locale options\n * @param {string} separator - Custom separator\n * @param {boolean} pad - Whether to pad\n * @param {number} round - Round value\n * @returns {string|number} Formatted value\n */\nexport function applyNumberFormatting (value, locale, localeOptions, separator, pad, round) {\n\tlet result = value;\n\n\t// Apply locale formatting\n\tif (locale === true) {\n\t\tresult = result.toLocaleString();\n\t} else if (locale.length > 0) {\n\t\tresult = result.toLocaleString(locale, localeOptions);\n\t} else if (separator.length > 0) {\n\t\tresult = result.toString().replace(PERIOD, separator);\n\t}\n\n\t// Apply padding\n\tif (pad && round > 0) {\n\t\tconst resultStr = result.toString();\n\t\tconst x = separator || ((resultStr.match(/(\\D)/g) || []).pop() || PERIOD);\n\t\tconst tmp = resultStr.split(x);\n\t\tconst s = tmp[1] || EMPTY;\n\t\tconst l = s.length;\n\t\tconst n = round - l;\n\n\t\tresult = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;\n\t}\n\n\treturn result;\n}\n","import {\n\tARRAY,\n\tBIT,\n\tBITS,\n\tBYTE,\n\tBYTES,\n\tEMPTY,\n\tEXPONENT,\n\tFUNCTION,\n\tINVALID_NUMBER,\n\tINVALID_ROUND,\n\tLOG_10_1000,\n\tLOG_2_1024,\n\tOBJECT,\n\tROUND,\n\tS,\n\tSI_KBIT,\n\tSI_KBYTE,\n\tSPACE,\n\tSTRING,\n\tSTRINGS,\n} from \"./constants.js\";\nimport {\n\tapplyNumberFormatting,\n\tapplyPrecisionHandling,\n\tcalculateOptimizedValue,\n\tgetBaseConfiguration,\n\thandleZeroValue\n} from \"./helpers.js\";\n\n/**\n * Converts a file size in bytes to a human-readable string with appropriate units\n * @param {number|string|bigint} arg - The file size in bytes to convert\n * @param {Object} [options={}] - Configuration options for formatting\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {string|Array|Object|number} Formatted file size based on output option\n * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid\n * @example\n * filesize(1024) // \"1 KB\"\n * filesize(1024, {bits: true}) // \"8 Kb\"\n * filesize(1024, {output: \"object\"}) // {value: 1, symbol: \"KB\", exponent: 1, unit: \"KB\"}\n */\nexport function filesize (arg, {\n\tbits = false,\n\tpad = false,\n\tbase = -1,\n\tround = 2,\n\tlocale = EMPTY,\n\tlocaleOptions = {},\n\tseparator = EMPTY,\n\tspacer = SPACE,\n\tsymbols = {},\n\tstandard = EMPTY,\n\toutput = STRING,\n\tfullform = false,\n\tfullforms = [],\n\texponent = -1,\n\troundingMethod = ROUND,\n\tprecision = 0\n} = {}) {\n\tlet e = exponent,\n\t\tnum = Number(arg),\n\t\tresult = [],\n\t\tval = 0,\n\t\tu = EMPTY;\n\n\t// Optimized base & standard configuration lookup\n\tconst {isDecimal, ceil, actualStandard} = getBaseConfiguration(standard, base);\n\n\tconst full = fullform === true,\n\t\tneg = num < 0,\n\t\troundingFunc = Math[roundingMethod];\n\n\tif (typeof arg !== \"bigint\" && isNaN(arg)) {\n\t\tthrow new TypeError(INVALID_NUMBER);\n\t}\n\n\tif (typeof roundingFunc !== FUNCTION) {\n\t\tthrow new TypeError(INVALID_ROUND);\n\t}\n\n\t// Flipping a negative number to determine the size\n\tif (neg) {\n\t\tnum = -num;\n\t}\n\n\t// Fast path for zero\n\tif (num === 0) {\n\t\treturn handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer);\n\t}\n\n\t// Optimized exponent calculation using pre-computed log values\n\tif (e === -1 || isNaN(e)) {\n\t\te = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024);\n\t\tif (e < 0) {\n\t\t\te = 0;\n\t\t}\n\t}\n\n\t// Exceeding supported length, time to reduce & multiply\n\tif (e > 8) {\n\t\tif (precision > 0) {\n\t\t\tprecision += 8 - e;\n\t\t}\n\t\te = 8;\n\t}\n\n\tif (output === EXPONENT) {\n\t\treturn e;\n\t}\n\n\t// Calculate value with optimized lookup and bits handling\n\tconst {result: valueResult, e: valueExponent} = calculateOptimizedValue(num, e, isDecimal, bits, ceil);\n\tval = valueResult;\n\te = valueExponent;\n\n\t// Optimize rounding calculation\n\tconst p = e > 0 && round > 0 ? Math.pow(10, round) : 1;\n\tresult[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p;\n\n\tif (result[0] === ceil && e < 8 && exponent === -1) {\n\t\tresult[0] = 1;\n\t\te++;\n\t}\n\n\t// Apply precision handling\n\tif (precision > 0) {\n\t\tconst precisionResult = applyPrecisionHandling(result[0], precision, e, num, isDecimal, bits, ceil, roundingFunc, round);\n\t\tresult[0] = precisionResult.value;\n\t\te = precisionResult.e;\n\t}\n\n\t// Cache symbol lookup\n\tconst symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES];\n\tu = result[1] = (isDecimal && e === 1) ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e];\n\n\t// Decorating a 'diff'\n\tif (neg) {\n\t\tresult[0] = -result[0];\n\t}\n\n\t// Applying custom symbol\n\tif (symbols[result[1]]) {\n\t\tresult[1] = symbols[result[1]];\n\t}\n\n\t// Apply locale, separator, and padding formatting\n\tresult[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round);\n\n\tif (full) {\n\t\tresult[1] = fullforms[e] || STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S);\n\t}\n\n\t// Optimized return logic\n\tif (output === ARRAY) {\n\t\treturn result;\n\t}\n\n\tif (output === OBJECT) {\n\t\treturn {\n\t\t\tvalue: result[0],\n\t\t\tsymbol: result[1],\n\t\t\texponent: e,\n\t\t\tunit: u\n\t\t};\n\t}\n\n\treturn spacer === SPACE ? `${result[0]} ${result[1]}` : result.join(spacer);\n}\n\n/**\n * Creates a partially applied version of filesize with preset options\n * @param {Object} [options={}] - Default options to apply to the returned function\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {Function} A function that takes a file size and returns formatted output\n * @example\n * const formatBytes = partial({round: 1, standard: \"IEC\"});\n * formatBytes(1024) // \"1.0 KiB\"\n * formatBytes(2048) // \"2.0 KiB\"\n */\n// Partial application for functional programming\nexport function partial ({\n\tbits = false,\n\tpad = false,\n\tbase = -1,\n\tround = 2,\n\tlocale = EMPTY,\n\tlocaleOptions = {},\n\tseparator = EMPTY,\n\tspacer = SPACE,\n\tsymbols = {},\n\tstandard = EMPTY,\n\toutput = STRING,\n\tfullform = false,\n\tfullforms = [],\n\texponent = -1,\n\troundingMethod = ROUND,\n\tprecision = 0\n} = {}) {\n\treturn arg => filesize(arg, {\n\t\tbits,\n\t\tpad,\n\t\tbase,\n\t\tround,\n\t\tlocale,\n\t\tlocaleOptions,\n\t\tseparator,\n\t\tspacer,\n\t\tsymbols,\n\t\tstandard,\n\t\toutput,\n\t\tfullform,\n\t\tfullforms,\n\t\texponent,\n\t\troundingMethod,\n\t\tprecision\n\t});\n}\n"],"names":["g","f","exports","module","define","amd","globalThis","self","filesize","this","IEC","JEDEC","SI","BITS","BYTE","BYTES","ARRAY","OBJECT","STRING","EXPONENT","ROUND","STRINGS","symbol","iec","bits","bytes","jedec","fullform","BINARY_POWERS","DECIMAL_POWERS","LOG_2_1024","Math","log","LOG_10_1000","STANDARD_CONFIGS","isDecimal","ceil","actualStandard","calculateOptimizedValue","num","e","result","arg","pad","base","round","locale","EMPTY","localeOptions","separator","spacer","symbols","standard","output","fullforms","exponent","roundingMethod","precision","Number","val","u","getBaseConfiguration","full","neg","roundingFunc","isNaN","TypeError","toPrecision","value","unit","join","handleZeroValue","floor","valueResult","valueExponent","p","pow","precisionResult","includes","applyPrecisionHandling","symbolTable","toLocaleString","length","toString","replace","resultStr","x","match","pop","tmp","split","s","l","n","padEnd","applyNumberFormatting","partial"],"mappings":";;;;CAAA,SAAAA,EAAAC,GAAA,iBAAAC,SAAA,oBAAAC,OAAAF,EAAAC,SAAA,mBAAAE,QAAAA,OAAAC,IAAAD,OAAA,CAAA,WAAAH,GAAAA,GAAAD,EAAA,oBAAAM,WAAAA,WAAAN,GAAAO,MAAAC,SAAA,CAAA,EAAA,CAAA,CAAAC,KAAA,SAAAP,GAAA,aACO,MAIMQ,EAAM,MACNC,EAAQ,QACRC,EAAK,KAILC,EAAO,OACPC,EAAO,OACPC,EAAQ,QAKRC,EAAQ,QAERC,EAAS,SACTC,EAAS,SAGTC,EAAW,WACXC,EAAQ,QAWRC,EAAU,CACtBC,OAAQ,CACPC,IAAK,CACJC,KAAM,CAAC,MAAO,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,SAC7EC,MAAO,CAAC,IAAK,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,QAE/DC,MAAO,CACNF,KAAM,CAAC,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QACtEC,MAAO,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,QAGzDE,SAAU,CACTJ,IAAK,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QAClEG,MAAO,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,MAAO,QAAS,WAKzDE,EAAgB,CAC5B,EACA,KACA,QACA,WACA,cACA,gBACA,mBACA,oBACA,qBAGYC,EAAiB,CAC7B,EACA,IACA,IACA,IACA,KACA,KACA,KACA,KACA,MAIYC,EAAaC,KAAKC,IAAI,MACtBC,EAAcF,KAAKC,IAAI,KC3D9BE,EAAmB,CACxBtB,CAACA,GAAK,CAACuB,WAAW,EAAMC,KAAM,IAAMC,eAAgB1B,GACpDD,CAACA,GAAM,CAACyB,WAAW,EAAOC,KAAM,KAAMC,eAAgB3B,GACtDC,CAACA,GAAQ,CAACwB,WAAW,EAAOC,KAAM,KAAMC,eAAgB1B,IAyElD,SAAS2B,EAAyBC,EAAKC,EAAGL,EAAWX,EAAMY,GAEjE,IAAIK,EAASF,GADHJ,EAAYN,EAAeW,GAAKZ,EAAcY,IAYxD,OATIhB,IACHiB,GAAU,EAENA,GAAUL,GAAQI,EAAI,IACzBC,GAAUL,EACVI,MAIK,CAACC,SAAQD,IACjB,CCtDO,SAAShC,EAAUkC,GAAKlB,KAC9BA,GAAO,EAAKmB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EFjCoB,IEiCNC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASnC,EAAMS,SACfA,GAAW,EAAK2B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBpC,EAAKqC,UACtBA,EAAY,GACT,IACH,IAAIjB,EAAIe,EACPhB,EAAMmB,OAAOhB,GACbD,EAAS,GACTkB,EAAM,EACNC,EFlDmB,GEqDpB,MAAMzB,UAACA,EAASC,KAAEA,EAAIC,eAAEA,GDjDlB,SAA+Be,EAAUR,GAE/C,OAAIV,EAAiBkB,GACblB,EAAiBkB,GAIZ,IAATR,EACI,CAACT,WAAW,EAAOC,KAAM,KAAMC,eAAgB3B,GAIhD,CAACyB,WAAW,EAAMC,KAAM,IAAMC,eAAgB1B,EACtD,CCoC2CkD,CAAqBT,EAAUR,GAEnEkB,GAAoB,IAAbnC,EACZoC,EAAMxB,EAAM,EACZyB,EAAejC,KAAKyB,GAErB,GAAmB,iBAARd,GAAoBuB,MAAMvB,GACpC,MAAM,IAAIwB,UFxFkB,kBE2F7B,GFzEuB,mBEyEZF,EACV,MAAM,IAAIE,UF3FiB,2BEoG5B,GALIH,IACHxB,GAAOA,GAII,IAARA,EACH,OD3CK,SAA0BkB,EAAWpB,EAAgBb,EAAM2B,EAASW,EAAMR,EAAWD,EAAQH,GACnG,MAAMT,EAAS,GACfA,EAAO,GAAKgB,EAAY,GAAI,GAAIU,YAAYV,GAAa,EACzD,MAAMG,EAAInB,EAAO,GAAKpB,EAAQC,OAAOe,GAAgBb,EAAOX,EAAOE,GAAO,GAE1E,OAAIsC,IAAWlC,EACP,GAIJgC,EAAQV,EAAO,MAClBA,EAAO,GAAKU,EAAQV,EAAO,KAIxBqB,IACHrB,EAAO,GAAKa,EAAU,IAAMjC,EAAQM,SAASU,GAAgB,IAAMb,EDlElD,MCkE+DV,IAI1EuC,IAAWrC,EAAQyB,EAASY,IAAWpC,EAAS,CACtDmD,MAAO3B,EAAO,GACdnB,OAAQmB,EAAO,GACfc,SAAU,EACVc,KAAMT,GACHnB,EAAO6B,KAAKpB,GACjB,CCiBSqB,CAAgBd,EAAWpB,EAAgBb,EAAM2B,EAASW,EAAMR,EAAWD,EAAQH,GAmB3F,KAfU,IAANV,GAAYyB,MAAMzB,MACrBA,EAAIL,EAAYJ,KAAKyC,MAAMzC,KAAKC,IAAIO,GAAON,GAAeF,KAAKyC,MAAMzC,KAAKC,IAAIO,GAAOT,GACjFU,EAAI,IACPA,EAAI,IAKFA,EAAI,IACHiB,EAAY,IACfA,GAAa,EAAIjB,GAElBA,EAAI,GAGDa,IAAWlC,EACd,OAAOqB,EAIR,MAAOC,OAAQgC,EAAajC,EAAGkC,GAAiBpC,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,GACjGuB,EAAMc,EACNjC,EAAIkC,EAGJ,MAAMC,EAAInC,EAAI,GAAKK,EAAQ,EAAId,KAAK6C,IAAI,GAAI/B,GAAS,EASrD,GARAJ,EAAO,GAAW,IAANkC,EAAUX,EAAaL,GAAOK,EAAaL,EAAMgB,GAAKA,EAE9DlC,EAAO,KAAOL,GAAQI,EAAI,QAAKe,IAClCd,EAAO,GAAK,EACZD,KAIGiB,EAAY,EAAG,CAClB,MAAMoB,EDhBD,SAAiCT,EAAOX,EAAWjB,EAAGD,EAAKJ,EAAWX,EAAMY,EAAM4B,EAAcnB,GACtG,IAAIJ,EAAS2B,EAAMD,YAAYV,GAG/B,GAAIhB,EAAOqC,SDtGK,MCsGUtC,EAAI,EAAG,CAChCA,IACA,MAAOC,OAAQgC,GAAenC,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,GACzEuC,EAAI9B,EAAQ,EAAId,KAAK6C,IAAI,GAAI/B,GAAS,EAC5CJ,GAAgB,IAANkC,EAAUX,EAAaS,GAAeT,EAAaS,EAAcE,GAAKA,GAAGR,YAAYV,EAChG,CAEA,MAAO,CAACW,MAAO3B,EAAQD,IACxB,CCI0BuC,CAAuBtC,EAAO,GAAIgB,EAAWjB,EAAGD,EAAKJ,EAAWX,EAAMY,EAAM4B,EAAcnB,GAClHJ,EAAO,GAAKoC,EAAgBT,MAC5B5B,EAAIqC,EAAgBrC,CACrB,CAGA,MAAMwC,EAAc3D,EAAQC,OAAOe,GAAgBb,EAAOX,EAAOE,GAqBjE,OApBA6C,EAAInB,EAAO,GAAMN,GAAmB,IAANK,EAAYhB,EFvIpB,OACC,KEsIgDwD,EAAYxC,GAG/EuB,IACHtB,EAAO,IAAMA,EAAO,IAIjBU,EAAQV,EAAO,MAClBA,EAAO,GAAKU,EAAQV,EAAO,KAI5BA,EAAO,GDZD,SAAgC2B,EAAOtB,EAAQE,EAAeC,EAAWN,EAAKE,GACpF,IAAIJ,EAAS2B,EAYb,IATe,IAAXtB,EACHL,EAASA,EAAOwC,iBACNnC,EAAOoC,OAAS,EAC1BzC,EAASA,EAAOwC,eAAenC,EAAQE,GAC7BC,EAAUiC,OAAS,IAC7BzC,EAASA,EAAO0C,WAAWC,QDjIP,ICiIuBnC,IAIxCN,GAAOE,EAAQ,EAAG,CACrB,MAAMwC,EAAY5C,EAAO0C,WACnBG,EAAIrC,IAAeoC,EAAUE,MAAM,UAAY,IAAIC,ODvIrC,ICwIdC,EAAMJ,EAAUK,MAAMJ,GACtBK,EAAIF,EAAI,ID1IK,GC2IbG,EAAID,EAAET,OACNW,EAAIhD,EAAQ+C,EAElBnD,EAAS,GAAGgD,EAAI,KAAKH,IAAIK,EAAEG,OAAOF,EAAIC,ED1IpB,MC2InB,CAEA,OAAOpD,CACR,CCbasD,CAAsBtD,EAAO,GAAIK,EAAQE,EAAeC,EAAWN,EAAKE,GAEhFiB,IACHrB,EAAO,GAAKa,EAAUd,IAAMnB,EAAQM,SAASU,GAAgBG,IAAMhB,EF3JlD,ME2J+DV,IAAuB,IAAd2B,EAAO,GFxI7E,GAEJ,ME0IZY,IAAWrC,EACPyB,EAGJY,IAAWpC,EACP,CACNmD,MAAO3B,EAAO,GACdnB,OAAQmB,EAAO,GACfc,SAAUf,EACV6B,KAAMT,GFlJY,MEsJbV,EAAmB,GAAGT,EAAO,MAAMA,EAAO,KAAOA,EAAO6B,KAAKpB,EACrE,CAgEAhD,EAAAM,SAAAA,EAAAN,EAAA8F,QApCO,UAAkBxE,KACxBA,GAAO,EAAKmB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EF3LoB,IE2LNC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASnC,EAAMS,SACfA,GAAW,EAAK2B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBpC,EAAKqC,UACtBA,EAAY,GACT,IACH,OAAOf,GAAOlC,EAASkC,EAAK,CAC3BlB,OACAmB,MACAC,OACAC,QACAC,SACAE,gBACAC,YACAC,SACAC,UACAC,WACAC,SACA1B,WACA2B,YACAC,WACAC,iBACAC,aAEF,CAAA"} \ No newline at end of file +{"version":3,"file":"filesize.umd.min.js","sources":["../src/constants.js","../src/helpers.js","../src/filesize.js"],"sourcesContent":["// Error Messages\nexport const INVALID_NUMBER = \"Invalid number\";\nexport const INVALID_ROUND = \"Invalid rounding method\";\n\n// Standard Types\nexport const IEC = \"iec\";\nexport const JEDEC = \"jedec\";\nexport const SI = \"si\";\n\n// Unit Types\nexport const BIT = \"bit\";\nexport const BITS = \"bits\";\nexport const BYTE = \"byte\";\nexport const BYTES = \"bytes\";\nexport const SI_KBIT = \"kbit\";\nexport const SI_KBYTE = \"kB\";\n\n// Output Format Types\nexport const ARRAY = \"array\";\nexport const FUNCTION = \"function\";\nexport const OBJECT = \"object\";\nexport const STRING = \"string\";\n\n// Processing Constants\nexport const EXPONENT = \"exponent\";\nexport const ROUND = \"round\";\n\n// Special Characters and Values\nexport const E = \"e\";\nexport const EMPTY = \"\";\nexport const PERIOD = \".\";\nexport const S = \"s\";\nexport const SPACE = \" \";\nexport const ZERO = \"0\";\n\n// Data Structures\nexport const STRINGS = {\n\tsymbol: {\n\t\tiec: {\n\t\t\tbits: [\"bit\", \"Kibit\", \"Mibit\", \"Gibit\", \"Tibit\", \"Pibit\", \"Eibit\", \"Zibit\", \"Yibit\"],\n\t\t\tbytes: [\"B\", \"KiB\", \"MiB\", \"GiB\", \"TiB\", \"PiB\", \"EiB\", \"ZiB\", \"YiB\"],\n\t\t},\n\t\tjedec: {\n\t\t\tbits: [\"bit\", \"Kbit\", \"Mbit\", \"Gbit\", \"Tbit\", \"Pbit\", \"Ebit\", \"Zbit\", \"Ybit\"],\n\t\t\tbytes: [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"],\n\t\t},\n\t},\n\tfullform: {\n\t\tiec: [\"\", \"kibi\", \"mebi\", \"gibi\", \"tebi\", \"pebi\", \"exbi\", \"zebi\", \"yobi\"],\n\t\tjedec: [\"\", \"kilo\", \"mega\", \"giga\", \"tera\", \"peta\", \"exa\", \"zetta\", \"yotta\"],\n\t},\n};\n\n// Pre-computed lookup tables for performance optimization\nexport const BINARY_POWERS = [\n\t1, // 2^0\n\t1024, // 2^10\n\t1048576, // 2^20\n\t1073741824, // 2^30\n\t1099511627776, // 2^40\n\t1125899906842624, // 2^50\n\t1152921504606846976, // 2^60\n\t1180591620717411303424, // 2^70\n\t1208925819614629174706176, // 2^80\n];\n\nexport const DECIMAL_POWERS = [\n\t1, // 10^0\n\t1000, // 10^3\n\t1000000, // 10^6\n\t1000000000, // 10^9\n\t1000000000000, // 10^12\n\t1000000000000000, // 10^15\n\t1000000000000000000, // 10^18\n\t1000000000000000000000, // 10^21\n\t1000000000000000000000000, // 10^24\n];\n\n// Pre-computed log values for faster exponent calculation\nexport const LOG_2_1024 = Math.log(1024);\nexport const LOG_10_1000 = Math.log(1000);\n","import {\n\tARRAY,\n\tBINARY_POWERS,\n\tBIT,\n\tBYTE,\n\tDECIMAL_POWERS,\n\tE,\n\tEMPTY,\n\tEXPONENT,\n\tIEC,\n\tJEDEC,\n\tOBJECT,\n\tPERIOD,\n\tSI,\n\tSTRINGS,\n\tZERO,\n} from \"./constants.js\";\n\n// Cached configuration lookup for better performance\nconst STANDARD_CONFIGS = {\n\t[SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC },\n\t[IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC },\n\t[JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC },\n};\n\n/**\n * Optimized base configuration lookup\n * @param {string} standard - Standard type\n * @param {number} base - Base number\n * @returns {Object} Configuration object\n */\nexport function getBaseConfiguration(standard, base) {\n\t// Use cached lookup table for better performance\n\tif (STANDARD_CONFIGS[standard]) {\n\t\treturn STANDARD_CONFIGS[standard];\n\t}\n\n\t// Base override\n\tif (base === 2) {\n\t\treturn { isDecimal: false, ceil: 1024, actualStandard: IEC };\n\t}\n\n\t// Default\n\treturn { isDecimal: true, ceil: 1000, actualStandard: JEDEC };\n}\n\n/**\n * Optimized zero value handling\n * @param {number} precision - Precision value\n * @param {string} actualStandard - Standard to use\n * @param {boolean} bits - Whether to use bits\n * @param {Object} symbols - Custom symbols\n * @param {boolean} full - Whether to use full form\n * @param {Array} fullforms - Custom full forms\n * @param {string} output - Output format\n * @param {string} spacer - Spacer character\n * @param {string} [symbol] - Symbol to use (defaults based on bits/standard)\n * @returns {string|Array|Object|number} Formatted result\n */\nexport function handleZeroValue(\n\tprecision,\n\tactualStandard,\n\tbits,\n\tsymbols,\n\tfull,\n\tfullforms,\n\toutput,\n\tspacer,\n\tsymbol,\n) {\n\tconst value = precision > 0 ? (0).toPrecision(precision) : 0;\n\n\tif (output === EXPONENT) {\n\t\treturn 0;\n\t}\n\n\t// Set default symbol if not provided\n\tif (!symbol) {\n\t\tsymbol = bits\n\t\t\t? STRINGS.symbol[actualStandard].bits[0]\n\t\t\t: STRINGS.symbol[actualStandard].bytes[0];\n\t}\n\n\t// Apply symbol customization\n\tif (symbols[symbol]) {\n\t\tsymbol = symbols[symbol];\n\t}\n\n\t// Apply full form\n\tif (full) {\n\t\tsymbol = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE);\n\t}\n\n\t// Return in requested format\n\tif (output === ARRAY) {\n\t\treturn [value, symbol];\n\t}\n\n\tif (output === OBJECT) {\n\t\treturn { value, symbol, exponent: 0, unit: symbol };\n\t}\n\n\treturn value + spacer + symbol;\n}\n\n/**\n * Optimized value calculation with bits handling\n * @param {number} num - Input number\n * @param {number} e - Exponent\n * @param {boolean} isDecimal - Whether to use decimal powers\n * @param {boolean} bits - Whether to calculate bits\n * @param {number} ceil - Ceiling value for auto-increment\n * @param {boolean} autoExponent - Whether exponent is auto (-1 or NaN)\n * @returns {Object} Object with result and e properties\n */\nexport function calculateOptimizedValue(num, e, isDecimal, bits, ceil, autoExponent = true) {\n\tconst d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e];\n\tlet result = num / d;\n\n\tif (bits) {\n\t\tresult *= 8;\n\t\t// Handle auto-increment for bits (only when exponent is auto)\n\t\tif (autoExponent && result >= ceil && e < 8) {\n\t\t\tresult /= ceil;\n\t\t\te++;\n\t\t}\n\t}\n\n\treturn { result, e };\n}\n\n/**\n * Optimized precision handling with scientific notation correction\n * @param {number} value - Current value\n * @param {number} precision - Precision to apply\n * @param {number} e - Current exponent\n * @param {number} num - Original number\n * @param {boolean} isDecimal - Whether using decimal base\n * @param {boolean} bits - Whether calculating bits\n * @param {number} ceil - Ceiling value\n * @param {Function} roundingFunc - Rounding function\n * @param {number} round - Round value\n * @param {number} exponent - Forced exponent (-1 for auto)\n * @returns {Object} Object with value and e properties\n */\nexport function applyPrecisionHandling(\n\tvalue,\n\tprecision,\n\te,\n\tnum,\n\tisDecimal,\n\tbits,\n\tceil,\n\troundingFunc,\n\tround,\n\texponent,\n) {\n\tlet result = value.toPrecision(precision);\n\n\tconst autoExponent = exponent === -1 || isNaN(exponent);\n\n\t// Handle scientific notation by recalculating with incremented exponent\n\tif (result.includes(E) && e < 8 && autoExponent) {\n\t\te++;\n\t\tconst { result: valueResult } = calculateOptimizedValue(num, e, isDecimal, bits, ceil);\n\t\tconst p = round > 0 ? Math.pow(10, round) : 1;\n\t\tresult = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(\n\t\t\tprecision,\n\t\t);\n\t}\n\n\treturn { value: result, e };\n}\n\n/**\n * Optimized number formatting with locale, separator, and padding\n * @param {number|string} value - Value to format\n * @param {string|boolean} locale - Locale setting\n * @param {Object} localeOptions - Locale options\n * @param {string} separator - Custom separator\n * @param {boolean} pad - Whether to pad\n * @param {number} round - Round value\n * @returns {string|number} Formatted value\n */\nexport function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) {\n\tlet result = value;\n\n\t// Apply locale formatting\n\tif (locale === true) {\n\t\tresult = result.toLocaleString();\n\t} else if (locale.length > 0) {\n\t\tresult = result.toLocaleString(locale, localeOptions);\n\t} else if (separator.length > 0) {\n\t\tresult = result.toString().replace(PERIOD, separator);\n\t}\n\n\t// Apply padding\n\tif (pad && round > 0) {\n\t\tconst resultStr = result.toString();\n\t\tconst x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD;\n\t\tconst tmp = resultStr.split(x);\n\t\tconst s = tmp[1] || EMPTY;\n\n\t\tconst l = s.length;\n\t\tconst n = round - l;\n\n\t\tresult = `${tmp[0]}${x}${s.padEnd(l + n, ZERO)}`;\n\t}\n\n\treturn result;\n}\n","import {\n\tARRAY,\n\tBIT,\n\tBITS,\n\tBYTE,\n\tBYTES,\n\tEMPTY,\n\tEXPONENT,\n\tFUNCTION,\n\tINVALID_NUMBER,\n\tINVALID_ROUND,\n\tLOG_10_1000,\n\tLOG_2_1024,\n\tOBJECT,\n\tROUND,\n\tS,\n\tSI_KBIT,\n\tSI_KBYTE,\n\tSPACE,\n\tSTRING,\n\tSTRINGS,\n} from \"./constants.js\";\nimport {\n\tapplyNumberFormatting,\n\tapplyPrecisionHandling,\n\tcalculateOptimizedValue,\n\tgetBaseConfiguration,\n\thandleZeroValue,\n} from \"./helpers.js\";\n\n/**\n * Converts a file size in bytes to a human-readable string with appropriate units\n * @param {number|string|bigint} arg - The file size in bytes to convert\n * @param {Object} [options={}] - Configuration options for formatting\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {string|Array|Object|number} Formatted file size based on output option\n * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid\n * @example\n * filesize(1024) // \"1.02 kB\"\n * filesize(1024, {bits: true}) // \"8.19 kbit\"\n * filesize(1024, {output: \"object\"}) // {value: 1.02, symbol: \"kB\", exponent: 1, unit: \"kB\"}\n */\nexport function filesize(\n\targ,\n\t{\n\t\tbits = false,\n\t\tpad = false,\n\t\tbase = -1,\n\t\tround = 2,\n\t\tlocale = EMPTY,\n\t\tlocaleOptions = {},\n\t\tseparator = EMPTY,\n\t\tspacer = SPACE,\n\t\tsymbols = {},\n\t\tstandard = EMPTY,\n\t\toutput = STRING,\n\t\tfullform = false,\n\t\tfullforms = [],\n\t\texponent = -1,\n\t\troundingMethod = ROUND,\n\t\tprecision = 0,\n\t} = {},\n) {\n\tlet e = exponent,\n\t\tnum = Number(arg),\n\t\tresult = [],\n\t\tval = 0,\n\t\tu = EMPTY;\n\n\t// Optimized base & standard configuration lookup\n\tconst { isDecimal, ceil, actualStandard } = getBaseConfiguration(standard, base);\n\n\tconst full = fullform === true,\n\t\tneg = num < 0,\n\t\troundingFunc = Math[roundingMethod];\n\n\tif (typeof arg !== \"bigint\" && isNaN(arg)) {\n\t\tthrow new TypeError(INVALID_NUMBER);\n\t}\n\n\tif (typeof roundingFunc !== FUNCTION) {\n\t\tthrow new TypeError(INVALID_ROUND);\n\t}\n\n\t// Flipping a negative number to determine the size\n\tif (neg) {\n\t\tnum = -num;\n\t}\n\n\t// Fast path for zero\n\tif (num === 0) {\n\t\treturn handleZeroValue(\n\t\t\tprecision,\n\t\t\tactualStandard,\n\t\t\tbits,\n\t\t\tsymbols,\n\t\t\tfull,\n\t\t\tfullforms,\n\t\t\toutput,\n\t\t\tspacer,\n\t\t);\n\t}\n\n\t// Optimized exponent calculation using pre-computed log values\n\tif (e === -1 || isNaN(e)) {\n\t\te = isDecimal\n\t\t\t? Math.floor(Math.log(num) / LOG_10_1000)\n\t\t\t: Math.floor(Math.log(num) / LOG_2_1024);\n\t\tif (e < 0) {\n\t\t\te = 0;\n\t\t}\n\t}\n\n\t// Exceeding supported length, time to reduce & multiply\n\tif (e > 8) {\n\t\tif (precision > 0) {\n\t\t\tprecision += 8 - e;\n\t\t}\n\t\te = 8;\n\t}\n\n\tconst autoExponent = exponent === -1 || isNaN(exponent);\n\n\tif (output === EXPONENT) {\n\t\treturn e;\n\t}\n\n\t// Calculate value with optimized lookup and bits handling\n\tconst { result: valueResult, e: valueExponent } = calculateOptimizedValue(\n\t\tnum,\n\t\te,\n\t\tisDecimal,\n\t\tbits,\n\t\tceil,\n\t\tautoExponent,\n\t);\n\tval = valueResult;\n\te = valueExponent;\n\n\t// Optimize rounding calculation\n\tconst p = e > 0 && round > 0 ? Math.pow(10, round) : 1;\n\tresult[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p;\n\n\tif (result[0] === ceil && e < 8 && autoExponent) {\n\t\tresult[0] = 1;\n\t\te++;\n\t}\n\n\t// Apply precision handling\n\tif (precision > 0) {\n\t\tconst precisionResult = applyPrecisionHandling(\n\t\t\tresult[0],\n\t\t\tprecision,\n\t\t\te,\n\t\t\tnum,\n\t\t\tisDecimal,\n\t\t\tbits,\n\t\t\tceil,\n\t\t\troundingFunc,\n\t\t\tround,\n\t\t\texponent,\n\t\t);\n\t\tresult[0] = precisionResult.value;\n\t\te = precisionResult.e;\n\t}\n\n\t// Cache symbol lookup\n\tconst symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES];\n\tu = result[1] = isDecimal && e === 1 ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e];\n\n\t// Decorating a 'diff'\n\tif (neg) {\n\t\tresult[0] = -result[0];\n\t}\n\n\t// Applying custom symbol\n\tif (symbols[result[1]]) {\n\t\tresult[1] = symbols[result[1]];\n\t}\n\n\t// Apply locale, separator, and padding formatting\n\tresult[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round);\n\n\tif (full) {\n\t\tresult[1] =\n\t\t\tfullforms[e] ||\n\t\t\tSTRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S);\n\t}\n\n\t// Optimized return logic\n\tif (output === ARRAY) {\n\t\treturn result;\n\t}\n\n\tif (output === OBJECT) {\n\t\treturn {\n\t\t\tvalue: result[0],\n\t\t\tsymbol: result[1],\n\t\t\texponent: e,\n\t\t\tunit: u,\n\t\t};\n\t}\n\n\treturn spacer === SPACE ? `${result[0]} ${result[1]}` : result.join(spacer);\n}\n\n/**\n * Creates a partially applied version of filesize with preset options\n * @param {Object} [options={}] - Configuration options (same as filesize)\n * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes\n * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter\n * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto)\n * @param {number} [options.round=2] - Number of decimal places to round to\n * @param {string|boolean} [options.locale=\"\"] - Locale for number formatting, true for system locale\n * @param {Object} [options.localeOptions={}] - Additional options for locale formatting\n * @param {string} [options.separator=\"\"] - Custom decimal separator\n * @param {string} [options.spacer=\" \"] - String to separate value and unit\n * @param {Object} [options.symbols={}] - Custom unit symbols\n * @param {string} [options.standard=\"\"] - Unit standard to use (SI, IEC, JEDEC)\n * @param {string} [options.output=\"string\"] - Output format: \"string\", \"array\", \"object\", or \"exponent\"\n * @param {boolean} [options.fullform=false] - If true, uses full unit names instead of abbreviations\n * @param {Array} [options.fullforms=[]] - Custom full unit names\n * @param {number} [options.exponent=-1] - Force specific exponent (-1 for auto)\n * @param {string} [options.roundingMethod=\"round\"] - Math rounding method to use\n * @param {number} [options.precision=0] - Number of significant digits (0 for auto)\n * @returns {Function} A function that takes a file size and returns formatted output\n * @example\n * const formatBytes = partial({round: 1, standard: \"iec\"});\n * formatBytes(1024) // \"1 KiB\"\n * formatBytes(2048) // \"2 KiB\"\n * formatBytes(1536) // \"1.5 KiB\"\n */\nexport function partial({\n\tbits = false,\n\tpad = false,\n\tbase = -1,\n\tround = 2,\n\tlocale = EMPTY,\n\tlocaleOptions = {},\n\tseparator = EMPTY,\n\tspacer = SPACE,\n\tsymbols = {},\n\tstandard = EMPTY,\n\toutput = STRING,\n\tfullform = false,\n\tfullforms = [],\n\texponent = -1,\n\troundingMethod = ROUND,\n\tprecision = 0,\n} = {}) {\n\treturn (arg) =>\n\t\tfilesize(arg, {\n\t\t\tbits,\n\t\t\tpad,\n\t\t\tbase,\n\t\t\tround,\n\t\t\tlocale,\n\t\t\tlocaleOptions,\n\t\t\tseparator,\n\t\t\tspacer,\n\t\t\tsymbols,\n\t\t\tstandard,\n\t\t\toutput,\n\t\t\tfullform,\n\t\t\tfullforms,\n\t\t\texponent,\n\t\t\troundingMethod,\n\t\t\tprecision,\n\t\t});\n}\n"],"names":["g","f","exports","module","define","amd","globalThis","self","filesize","this","IEC","JEDEC","SI","BYTE","ARRAY","OBJECT","STRING","EXPONENT","ROUND","STRINGS","symbol","iec","bits","bytes","jedec","fullform","BINARY_POWERS","DECIMAL_POWERS","LOG_2_1024","Math","log","LOG_10_1000","STANDARD_CONFIGS","isDecimal","ceil","actualStandard","calculateOptimizedValue","num","e","autoExponent","result","arg","pad","base","round","locale","EMPTY","localeOptions","separator","spacer","symbols","standard","output","fullforms","exponent","roundingMethod","precision","Number","val","u","getBaseConfiguration","full","neg","roundingFunc","isNaN","TypeError","value","toPrecision","unit","handleZeroValue","floor","valueResult","valueExponent","p","pow","precisionResult","includes","applyPrecisionHandling","symbolTable","toLocaleString","length","toString","replace","resultStr","x","slice","match","pop","tmp","split","s","l","n","padEnd","applyNumberFormatting","join","partial"],"mappings":";;;;CAAA,SAAAA,EAAAC,GAAA,iBAAAC,SAAA,oBAAAC,OAAAF,EAAAC,SAAA,mBAAAE,QAAAA,OAAAC,IAAAD,OAAA,CAAA,WAAAH,GAAAA,GAAAD,EAAA,oBAAAM,WAAAA,WAAAN,GAAAO,MAAAC,SAAA,CAAA,EAAA,CAAA,CAAAC,KAAA,SAAAP,GAAA,aACO,MAIMQ,EAAM,MACNC,EAAQ,QACRC,EAAK,KAKLC,EAAO,OAMPC,EAAQ,QAERC,EAAS,SACTC,EAAS,SAGTC,EAAW,WACXC,EAAQ,QAWRC,EAAU,CACtBC,OAAQ,CACPC,IAAK,CACJC,KAAM,CAAC,MAAO,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,QAAS,SAC7EC,MAAO,CAAC,IAAK,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,MAAO,QAE/DC,MAAO,CACNF,KAAM,CAAC,MAAO,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QACtEC,MAAO,CAAC,IAAK,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,KAAM,QAGzDE,SAAU,CACTJ,IAAK,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,QAClEG,MAAO,CAAC,GAAI,OAAQ,OAAQ,OAAQ,OAAQ,OAAQ,MAAO,QAAS,WAKzDE,EAAgB,CAC5B,EACA,KACA,QACA,WACA,cACA,gBACA,mBACA,oBACA,qBAGYC,EAAiB,CAC7B,EACA,IACA,IACA,IACA,KACA,KACA,KACA,KACA,MAIYC,EAAaC,KAAKC,IAAI,MACtBC,EAAcF,KAAKC,IAAI,KC7D9BE,EAAmB,CACxBpB,CAACA,GAAK,CAAEqB,WAAW,EAAMC,KAAM,IAAMC,eAAgBxB,GACrDD,CAACA,GAAM,CAAEuB,WAAW,EAAOC,KAAM,KAAMC,eAAgBzB,GACvDC,CAACA,GAAQ,CAAEsB,WAAW,EAAOC,KAAM,KAAMC,eAAgBxB,IA6FnD,SAASyB,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,EAAMK,GAAe,GAErF,IAAIC,EAASH,GADHJ,EAAYN,EAAeW,GAAKZ,EAAcY,IAYxD,OATIhB,IACHkB,GAAU,EAEND,GAAgBC,GAAUN,GAAQI,EAAI,IACzCE,GAAUN,EACVI,MAIK,CAAEE,SAAQF,IAClB,CCxEO,SAAS9B,EACfiC,GACAnB,KACCA,GAAO,EAAKoB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EFnCmB,IEmCLC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASpC,EAAMS,SACfA,GAAW,EAAK4B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBrC,EAAKsC,UACtBA,EAAY,GACT,CAAA,GAEJ,IAAIlB,EAAIgB,EACPjB,EAAMoB,OAAOhB,GACbD,EAAS,GACTkB,EAAM,EACNC,EFrDmB,GEwDpB,MAAM1B,UAAEA,EAASC,KAAEA,EAAIC,eAAEA,GDtDnB,SAA8BgB,EAAUR,GAE9C,OAAIX,EAAiBmB,GACbnB,EAAiBmB,GAIZ,IAATR,EACI,CAAEV,WAAW,EAAOC,KAAM,KAAMC,eAAgBzB,GAIjD,CAAEuB,WAAW,EAAMC,KAAM,IAAMC,eAAgBxB,EACvD,CCyC6CiD,CAAqBT,EAAUR,GAErEkB,GAAoB,IAAbpC,EACZqC,EAAMzB,EAAM,EACZ0B,EAAelC,KAAK0B,GAErB,GAAmB,iBAARd,GAAoBuB,MAAMvB,GACpC,MAAM,IAAIwB,UF3FkB,kBE8F7B,GF5EuB,mBE4EZF,EACV,MAAM,IAAIE,UF9FiB,2BEuG5B,GALIH,IACHzB,GAAOA,GAII,IAARA,EACH,OD/CK,SACNmB,EACArB,EACAb,EACA4B,EACAW,EACAR,EACAD,EACAH,EACA7B,GAEA,MAAM8C,EAAQV,EAAY,GAAI,GAAIW,YAAYX,GAAa,EAE3D,OAAIJ,IAAWnC,EACP,GAIHG,IACJA,EAASE,EACNH,EAAQC,OAAOe,GAAgBb,KAAK,GACpCH,EAAQC,OAAOe,GAAgBZ,MAAM,IAIrC2B,EAAQ9B,KACXA,EAAS8B,EAAQ9B,IAIdyC,IACHzC,EAASiC,EAAU,IAAMlC,EAAQM,SAASU,GAAgB,IAAMb,EDhF/C,MCgF4DT,IAI1EuC,IAAWtC,EACP,CAACoD,EAAO9C,GAGZgC,IAAWrC,EACP,CAAEmD,QAAO9C,SAAQkC,SAAU,EAAGc,KAAMhD,GAGrC8C,EAAQjB,EAAS7B,EACzB,CCGSiD,CACNb,EACArB,EACAb,EACA4B,EACAW,EACAR,EACAD,EACAH,KAKQ,IAANX,GAAY0B,MAAM1B,MACrBA,EAAIL,EACDJ,KAAKyC,MAAMzC,KAAKC,IAAIO,GAAON,GAC3BF,KAAKyC,MAAMzC,KAAKC,IAAIO,GAAOT,GAC1BU,EAAI,IACPA,EAAI,IAKFA,EAAI,IACHkB,EAAY,IACfA,GAAa,EAAIlB,GAElBA,EAAI,GAGL,MAAMC,OAAee,GAAmBU,MAAMV,GAE9C,GAAIF,IAAWnC,EACd,OAAOqB,EAIR,MAAQE,OAAQ+B,EAAajC,EAAGkC,GAAkBpC,EACjDC,EACAC,EACAL,EACAX,EACAY,EACAK,GAEDmB,EAAMa,EACNjC,EAAIkC,EAGJ,MAAMC,EAAInC,EAAI,GAAKM,EAAQ,EAAIf,KAAK6C,IAAI,GAAI9B,GAAS,EASrD,GARAJ,EAAO,GAAW,IAANiC,EAAUV,EAAaL,GAAOK,EAAaL,EAAMe,GAAKA,EAE9DjC,EAAO,KAAON,GAAQI,EAAI,GAAKC,IAClCC,EAAO,GAAK,EACZF,KAIGkB,EAAY,EAAG,CAClB,MAAMmB,EDpBD,SACNT,EACAV,EACAlB,EACAD,EACAJ,EACAX,EACAY,EACA6B,EACAnB,EACAU,GAEA,IAAId,EAAS0B,EAAMC,YAAYX,GAE/B,MAAMjB,OAAee,GAAmBU,MAAMV,GAG9C,GAAId,EAAOoC,SDtIK,MCsIUtC,EAAI,GAAKC,EAAc,CAChDD,IACA,MAAQE,OAAQ+B,GAAgBnC,EAAwBC,EAAKC,EAAGL,EAAWX,EAAMY,GAC3EuC,EAAI7B,EAAQ,EAAIf,KAAK6C,IAAI,GAAI9B,GAAS,EAC5CJ,GAAgB,IAANiC,EAAUV,EAAaQ,GAAeR,EAAaQ,EAAcE,GAAKA,GAAGN,YAClFX,EAEF,CAEA,MAAO,CAAEU,MAAO1B,EAAQF,IACzB,CCP0BuC,CACvBrC,EAAO,GACPgB,EACAlB,EACAD,EACAJ,EACAX,EACAY,EACA6B,EACAnB,EACAU,GAEDd,EAAO,GAAKmC,EAAgBT,MAC5B5B,EAAIqC,EAAgBrC,CACrB,CAGA,MAAMwC,EAAc3D,EAAQC,OAAOe,GAAgBb,EF3KhC,OAEC,SEgMpB,OAtBAqC,EAAInB,EAAO,GAAKP,GAAmB,IAANK,EAAWhB,EFzKlB,OACC,KEwK8CwD,EAAYxC,GAG7EwB,IACHtB,EAAO,IAAMA,EAAO,IAIjBU,EAAQV,EAAO,MAClBA,EAAO,GAAKU,EAAQV,EAAO,KAI5BA,EAAO,GDZD,SAA+B0B,EAAOrB,EAAQE,EAAeC,EAAWN,EAAKE,GACnF,IAAIJ,EAAS0B,EAYb,IATe,IAAXrB,EACHL,EAASA,EAAOuC,iBACNlC,EAAOmC,OAAS,EAC1BxC,EAASA,EAAOuC,eAAelC,EAAQE,GAC7BC,EAAUgC,OAAS,IAC7BxC,EAASA,EAAOyC,WAAWC,QDnKP,ICmKuBlC,IAIxCN,GAAOE,EAAQ,EAAG,CACrB,MAAMuC,EAAY3C,EAAOyC,WACnBG,EAAIpC,IAAcmC,EAAUE,MAAM,GAAGC,MAAM,UAAY,IAAIC,ODzK7C,IC0KdC,EAAML,EAAUM,MAAML,GACtBM,EAAIF,EAAI,ID5KK,GC8KbG,EAAID,EAAEV,OACNY,EAAIhD,EAAQ+C,EAElBnD,EAAS,GAAGgD,EAAI,KAAKJ,IAAIM,EAAEG,OAAOF,EAAIC,ED7KpB,MC8KnB,CAEA,OAAOpD,CACR,CCdasD,CAAsBtD,EAAO,GAAIK,EAAQE,EAAeC,EAAWN,EAAKE,GAEhFiB,IACHrB,EAAO,GACNa,EAAUf,IACVnB,EAAQM,SAASU,GAAgBG,IAAMhB,EF/LvB,ME+LoCT,IAAuB,IAAd2B,EAAO,GF5KlD,GAEJ,ME8KZY,IAAWtC,EACP0B,EAGJY,IAAWrC,EACP,CACNmD,MAAO1B,EAAO,GACdpB,OAAQoB,EAAO,GACfc,SAAUhB,EACV8B,KAAMT,GFtLY,ME0LbV,EAAmB,GAAGT,EAAO,MAAMA,EAAO,KAAOA,EAAOuD,KAAK9C,EACrE,CAiEA/C,EAAAM,SAAAA,EAAAN,EAAA8F,QArCO,UAAiB1E,KACvBA,GAAO,EAAKoB,IACZA,GAAM,EAAKC,KACXA,GAAO,EAAEC,MACTA,EAAQ,EAACC,OACTA,EAASC,GAAKC,cACdA,EAAgB,CAAA,EAAEC,UAClBA,EAAYF,GAAKG,OACjBA,EF/NoB,IE+NNC,QACdA,EAAU,CAAA,EAAEC,SACZA,EAAWL,GAAKM,OAChBA,EAASpC,EAAMS,SACfA,GAAW,EAAK4B,UAChBA,EAAY,GAAEC,SACdA,GAAW,EAAEC,eACbA,EAAiBrC,EAAKsC,UACtBA,EAAY,GACT,IACH,OAAQf,GACPjC,EAASiC,EAAK,CACbnB,OACAoB,MACAC,OACAC,QACAC,SACAE,gBACAC,YACAC,SACAC,UACAC,WACAC,SACA3B,WACA4B,YACAC,WACAC,iBACAC,aAEH,CAAA"} diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..9d02447 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,421 @@ +# API Reference + +## Functions + +### filesize(arg, options) + +Converts a file size in bytes to a human-readable string with appropriate units. + +**Parameters:** + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| `arg` | `number\|string\|bigint` | *(required)* | The file size in bytes to convert | +| `options` | `Object` | `{}` | Configuration options for formatting | + +**Options:** + +| Option | Type | Default | Description | +|--------|------|---------|-------------| +| `bits` | `boolean` | `false` | If true, calculates bits instead of bytes | +| `pad` | `boolean` | `false` | If true, pads decimal places to match round parameter | +| `base` | `number` | `-1` | Number base (2 for binary, 10 for decimal, -1 for auto) | +| `round` | `number` | `2` | Number of decimal places to round to | +| `locale` | `string\|boolean` | `""` | Locale for number formatting, true for system locale | +| `localeOptions` | `Object` | `{}` | Additional options for locale formatting | +| `separator` | `string` | `""` | Custom decimal separator | +| `spacer` | `string` | `" "` | String to separate value and unit | +| `symbols` | `Object` | `{}` | Custom unit symbols | +| `standard` | `string` | `""` | Unit standard to use (`si`, `iec`, `jedec`) | +| `output` | `string` | `"string"` | Output format (`string`, `array`, `object`, `exponent`) | +| `fullform` | `boolean` | `false` | If true, uses full unit names instead of abbreviations | +| `fullforms` | `Array` | `[]` | Custom full unit names | +| `exponent` | `number` | `-1` | Force specific exponent (-1 for auto) | +| `roundingMethod` | `string` | `"round"` | Math rounding method to use (`round`, `floor`, `ceil`) | +| `precision` | `number` | `0` | Number of significant digits (0 for auto) | + +**Returns:** `string\|Array\|Object\|number` - Formatted file size based on output option + +**Throws:** `TypeError` - When arg is not a valid number or roundingMethod is invalid + +**Examples:** + +```javascript +// Basic usage +filesize(1024) // "1.02 kB" +filesize(265318) // "265.32 kB" + +// Bits instead of bytes +filesize(1024, {bits: true}) // "8.19 kbit" + +// Object output +filesize(1024, {output: "object"}) +// {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"} + +// Binary standard (IEC) +filesize(1024, {base: 2, standard: "iec"}) // "1 KiB" + +// Full form names +filesize(1024, {fullform: true}) // "1.02 kilobytes" + +// Custom formatting +filesize(265318, {separator: ",", round: 0}) // "265 kB" + +// Locale formatting +filesize(265318, {locale: "de"}) // "265,32 kB" + +// BigInt support +filesize(BigInt(1024)) // "1.02 kB" + +// Negative numbers +filesize(-1024) // "-1.02 kB" + +// Exponent output +filesize(1536, {output: "exponent"}) // 1 + +// Array output +filesize(1536, {output: "array"}) // [1.54, "kB"] +``` + +--- + +### partial(options) + +Creates a partially applied version of filesize with preset options. + +**Parameters:** + +| Name | Type | Default | Description | +|------|------|---------|-------------| +| `options` | `Object` | `{}` | Configuration options (same as filesize) | + +**Returns:** `Function` - A function that takes a file size and returns formatted output + +**Examples:** + +```javascript +import {partial} from "filesize"; + +// Create specialized formatters +const formatBinary = partial({base: 2, standard: "iec"}); +const formatBits = partial({bits: true}); +const formatPrecise = partial({round: 3, pad: true}); +const formatGerman = partial({locale: "de"}); + +// Use throughout application +formatBinary(1024) // "1 KiB" +formatBinary(1048576) // "1 MiB" + +formatBits(1024) // "8.19 kbit" + +formatPrecise(1536) // "1.536 kB" + +formatGerman(265318) // "265,32 kB" + +// Method chaining +const sizes = [1024, 2048, 4096]; +sizes.map(formatBinary) // ["1 KiB", "2 KiB", "4 KiB"] +``` + +--- + +## Output Formats + +### String Output (default) + +Returns a formatted string with value and unit separated by a space (or custom spacer). + +```javascript +filesize(1536) // "1.54 kB" +filesize(265318, {separator: ","}) // "265,32 kB" +filesize(265318, {spacer: ""}) // "265.32kB" +``` + +### Array Output + +Returns an array with `[value, symbol]`. + +```javascript +filesize(1536, {output: "array"}) // [1.54, "kB"] +filesize(1024, {output: "array", base: 2}) // [1, "KiB"] +``` + +### Object Output + +Returns an object with `value`, `symbol`, `exponent`, and `unit` properties. + +```javascript +filesize(1536, {output: "object"}) +// {value: 1.54, symbol: "kB", exponent: 1, unit: "kB"} +``` + +### Exponent Output + +Returns the exponent as a number (0 = bytes, 1 = kB/KiB, 2 = MB/MiB, etc.). + +```javascript +filesize(1536, {output: "exponent"}) // 1 +filesize(1048576, {output: "exponent", base: 2}) // 2 +``` + +--- + +## Unit Standards + +### SI (International System of Units) - Default + +Uses base 10 (powers of 1000) with SI symbols. + +```javascript +filesize(1000) // "1 kB" +filesize(1000000) // "1 MB" +filesize(1000000000) // "1 GB" +``` + +### IEC (International Electrotechnical Commission) + +Uses base 1024 (powers of 1024) with binary prefixes (KiB, MiB, GiB). + +```javascript +filesize(1024, {base: 2, standard: "iec"}) // "1 KiB" +filesize(1048576, {base: 2, standard: "iec"}) // "1 MiB" +filesize(1073741824, {base: 2, standard: "iec"}) // "1 GiB" +``` + +### JEDEC + +Uses base 1024 (powers of 1024) with traditional symbols (KB, MB, GB). + +```javascript +filesize(1024, {standard: "jedec"}) // "1 KB" +filesize(1048576, {standard: "jedec"}) // "1 MB" +filesize(265318, {standard: "jedec"}) // "259.1 KB" +``` + +--- + +## Options Details + +### bits + +Calculate in bits instead of bytes. + +```javascript +filesize(1024, {bits: true}) // "8.19 kbit" +filesize(1024, {bits: true, base: 2}) // "8 Kibit" +``` + +### base + +Number base for calculations: +- `-1` (default): Auto-detect based on standard +- `2`: Binary (1024) - uses IEC symbols (KiB, MiB, GiB) +- `10`: Decimal (1000) + +```javascript +filesize(1024, {base: 2}) // "1 KiB" (binary, IEC symbols) +filesize(1024, {base: 10}) // "1.02 kB" (decimal, SI symbols) +``` + +### round + +Number of decimal places to round to. + +```javascript +filesize(1536, {round: 0}) // "2 kB" +filesize(1536, {round: 1}) // "1.5 kB" +filesize(1536, {round: 3}) // "1.536 kB" +``` + +### pad + +Pad decimal places with zeros to match the `round` parameter. + +```javascript +filesize(1536, {round: 3, pad: true}) // "1.536 kB" +filesize(1024, {round: 3, pad: true}) // "1.024 kB" +``` + +### separator + +Custom decimal separator character. + +```javascript +filesize(265318, {separator: ","}) // "265,32 kB" +filesize(265318, {separator: "."}) // "265.32 kB" +``` + +### spacer + +Character between the result and symbol. + +```javascript +filesize(265318, {spacer: ""}) // "265.32kB" +filesize(265318, {spacer: "-"}) // "265.32-kB" +``` + +### symbols + +Custom unit symbols for localization or branding. + +```javascript +filesize(1, {symbols: {B: "Б"}}) // "1 Б" +filesize(1024, {symbols: {kB: "KB"}}) // "1.02 KB" +``` + +### standard + +Unit standard to use: +- `""` (default): SI standard (base 10) +- `"si"`: SI standard (base 10) +- `"iec"`: IEC standard (base 1024, KiB/MiB/GiB) +- `"jedec"`: JEDEC standard (base 1024, KB/MB/GB) + +```javascript +filesize(1024, {standard: "si"}) // "1.02 kB" +filesize(1024, {standard: "iec"}) // "1 KiB" +filesize(1024, {standard: "jedec"}) // "1 KB" +``` + +### output + +Output format: +- `"string"` (default): Human-readable string +- `"array"`: `[value, symbol]` +- `"object"`: `{value, symbol, exponent, unit}` +- `"exponent"`: Number (exponent value) + +```javascript +filesize(1536, {output: "string"}) // "1.54 kB" +filesize(1536, {output: "array"}) // [1.54, "kB"] +filesize(1536, {output: "object"}) // {value: 1.54, symbol: "kB", exponent: 1, unit: "kB"} +filesize(1536, {output: "exponent"}) // 1 +``` + +### fullform + +Use full unit names instead of abbreviations. + +```javascript +filesize(1024, {fullform: true}) // "1.02 kilobytes" +filesize(1024, {base: 2, fullform: true}) // "1 kibibyte" +filesize(1024, {bits: true, fullform: true}) // "8.19 kilobits" +``` + +### fullforms + +Custom full unit names array (overrides default full names). + +```javascript +filesize(12, {fullform: true, fullforms: ["байт", "килобайт", "мегабайт"]}) +// "12 байт" +``` + +### exponent + +Force a specific exponent (-1 for auto). + +```javascript +filesize(1024, {exponent: 0}) // "1024 B" +filesize(1024, {exponent: 1}) // "1.02 kB" +filesize(1024, {exponent: 2}) // "0 MB" +``` + +### roundingMethod + +Math rounding method to use. + +```javascript +filesize(1536, {roundingMethod: "round"}) // "1.54 kB" +filesize(1536, {roundingMethod: "floor"}) // "1.53 kB" +filesize(1536, {roundingMethod: "ceil"}) // "1.54 kB" +``` + +### precision + +Number of significant digits (overrides round when specified). + +```javascript +filesize(1536, {precision: 3}) // "1.54 kB" +filesize(1234567, {precision: 2}) // "1.2 MB" +``` + +### locale + +Locale for number formatting: +- `""` (default): No locale formatting +- `true`: Use system locale +- `"en-US"`: Specific locale (BCP 47 language tag) + +```javascript +filesize(265318, {locale: "en-US"}) // "265.32 kB" +filesize(265318, {locale: "de"}) // "265,32 kB" +filesize(265318, {locale: true}) // Uses system locale +``` + +### localeOptions + +Additional options for `Intl.NumberFormat`. + +```javascript +filesize(265318, { + locale: "en-US", + localeOptions: { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + } +}) // "265.32 kB" +``` + +--- + +## Error Handling + +Invalid input throws `TypeError`: + +```javascript +try { + filesize("invalid"); +} catch (error) { + console.error(error.message); // "Invalid number" +} + +try { + filesize(NaN); +} catch (error) { + console.error(error.message); // "Invalid number" +} + +try { + filesize(1024, {roundingMethod: "invalid"}); +} catch (error) { + console.error(error.message); // "Invalid rounding method" +} +``` + +--- + +## Input Types + +### Number + +```javascript +filesize(1024) // "1.02 kB" +filesize(1536.5) // "1.54 kB" +filesize(-1024) // "-1.02 kB" +``` + +### String + +```javascript +filesize("1024") // "1.02 kB" +filesize("1536.5") // "1.54 kB" +filesize(" 1024 ") // "1.02 kB" +``` + +### BigInt + +```javascript +filesize(BigInt(1024)) // "1.02 kB" +filesize(BigInt("10000000000000000000")) // "10 EB" +``` diff --git a/docs/CODE_STYLE_GUIDE.md b/docs/CODE_STYLE_GUIDE.md index f25af77..ac312be 100644 --- a/docs/CODE_STYLE_GUIDE.md +++ b/docs/CODE_STYLE_GUIDE.md @@ -1,223 +1,139 @@ # Code Style Guide -## Table of Contents - -1. [Overview](#overview) -2. [General Principles](#general-principles) -3. [Code Formatting](#code-formatting) -4. [Naming Conventions](#naming-conventions) -5. [Function and Class Design](#function-and-class-design) -6. [Documentation Standards](#documentation-standards) -7. [Error Handling](#error-handling) -8. [Module Structure](#module-structure) -9. [Testing Standards](#testing-standards) -10. [Security Guidelines](#security-guidelines) -11. [Performance Considerations](#performance-considerations) -12. [Tools and Linting](#tools-and-linting) - ## Overview -This style guide establishes coding standards for the filesize.js project, following Node.js community best practices and ensuring code maintainability, readability, and security. +This style guide establishes coding standards for filesize.js, following Node.js community best practices and the project's existing patterns. ## General Principles -### Core Development Principles - -- **DRY (Don't Repeat Yourself)**: Eliminate code duplication through abstraction and modularization +- **DRY (Don't Repeat Yourself)**: Eliminate code duplication - **KISS (Keep It Simple, Stupid)**: Favor simplicity over complexity -- **YAGNI (You Aren't Gonna Need It)**: Implement features only when necessary -- **SOLID Principles**: Follow Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion principles - -### Code Quality Standards - -- Write self-documenting code with clear intent -- Prioritize code readability over cleverness -- Use meaningful names that express intent -- Keep functions small and focused -- Write testable code with minimal dependencies +- **YAGNI (You Aren't Gonna Need It)**: Implement only when necessary +- **SOLID Principles**: Follow single responsibility, etc. ## Code Formatting -### Indentation and Spacing +### Indentation + +Use **tabs** for indentation: ```javascript -// Use tabs for indentation (project standard) -function exampleFunction(param1, param2) { - const result = param1 + param2; +export function filesize (arg, { + bits = false, + pad = false +} = {}) { + const result = []; return result; } - -// Space around operators -const sum = a + b; -const isValid = value > 0 && value < 100; - -// Space after commas in arrays and objects -const array = [1, 2, 3, 4]; -const obj = { key1: "value1", key2: "value2" }; ``` -### Line Length and Breaks +### Line Length - Maximum line length: 120 characters - Break long parameter lists across multiple lines -- Use consistent indentation for wrapped lines - -```javascript -// Good: Long parameter list -function processData({ - inputData, - outputFormat, - processingOptions, - validationRules -}) { - // Function implementation -} - -// Good: Long conditional -if (condition1 && - condition2 && - condition3) { - // Action -} -``` ### Trailing Commas -Use trailing commas in multi-line arrays and objects for cleaner diffs: +Use trailing commas in multi-line arrays/objects: ```javascript const config = { option1: "value1", option2: "value2", - option3: "value3", // Trailing comma }; ``` ## Naming Conventions -### Variables and Functions +### Functions and Variables -- Use **camelCase** for all variables and functions -- Use descriptive names that clearly indicate purpose -- Avoid abbreviations unless they are widely understood +Use **camelCase**: ```javascript // Good const userAccountBalance = getUserBalance(); const isValidEmail = validateEmailFormat(email); -// Bad -const usrAccBal = getUsrBal(); -const isValidEml = validateEmlFmt(eml); +// From filesize.js +export function handleZeroValue (precision, actualStandard, bits) { + // Implementation +} ``` ### Constants -- Use **UPPER_SNAKE_CASE** for all constants -- Group related constants together -- Use meaningful prefixes for constant categories +Use **UPPER_SNAKE_CASE**: ```javascript -// Good -const MAX_FILE_SIZE = 1024 * 1024; -const ERROR_INVALID_INPUT = "Invalid input provided"; -const HTTP_STATUS_OK = 200; - -// Export constants from dedicated module -export const STANDARDS = { - IEC: "iec", - JEDEC: "jedec", - SI: "si" -}; +// From constants.js +export const INVALID_NUMBER = "Invalid number"; +export const IEC = "iec"; +export const BINARY_POWERS = [1, 1024, 1048576]; ``` ### Files and Directories -- Use **kebab-case** for file and directory names -- Use descriptive names that indicate file purpose -- Group related files in logical directories +Use **kebab-case** for file/directory names. The project uses simple descriptive names: ``` src/ ├── constants.js ├── filesize.js -├── utils/ -│ ├── validation-helpers.js -│ └── formatting-utils.js +└── helpers.js + tests/ -├── unit/ -│ ├── filesize.test.js -│ └── constants.test.js -└── integration/ - └── full-workflow.test.js +└── unit/ + ├── filesize-helpers.test.js + └── filesize.test.js ``` -## Function and Class Design +## Function Design -### Function Structure +### Small, Focused Functions -- Keep functions small (ideally under 20 lines) -- Single responsibility per function -- Pure functions when possible (no side effects) -- Use default parameters for optional arguments +Keep functions small with single responsibility: ```javascript /** - * Calculates the file size with specified formatting options - * @param {number} bytes - The size in bytes - * @param {Object} [options={}] - Formatting options - * @param {string} [options.standard="jedec"] - Unit standard to use - * @param {number} [options.precision=2] - Decimal places - * @returns {string} Formatted file size string + * Optimized base configuration lookup + * @param {string} standard - Standard type + * @param {number} base - Base number + * @returns {Object} Configuration object */ -function formatFileSize(bytes, { - standard = "jedec", - precision = 2 -} = {}) { - // Implementation +export function getBaseConfiguration (standard, base) { + if (STANDARD_CONFIGS[standard]) { + return STANDARD_CONFIGS[standard]; + } + + if (base === 2) { + return {isDecimal: false, ceil: 1024, actualStandard: IEC}; + } + + return {isDecimal: true, ceil: 1000, actualStandard: JEDEC}; } ``` -### Parameter Handling +### Default Parameters -- Use destructuring for options objects -- Provide sensible defaults -- Validate critical parameters +Use default parameters for optional arguments: ```javascript -// Good: Clear parameter destructuring with defaults -function processFile({ - inputPath, - outputPath = "./output", - format = "json", - compress = false -} = {}) { - if (!inputPath) { - throw new TypeError("inputPath is required"); - } - // Process file +export function partial (options = {}) { + return arg => filesize(arg, options); } ``` -### Return Values +### Early Returns -- Be consistent with return types -- Return early for error conditions -- Use descriptive return objects when returning multiple values +Return early for error conditions: ```javascript -// Good: Consistent return pattern -function parseFileSize(input) { - if (typeof input !== "string") { - return { success: false, error: "Input must be a string" }; - } - - const parsed = parseSize(input); - return { - success: true, - value: parsed.value, - unit: parsed.unit - }; +if (typeof arg !== "bigint" && isNaN(arg)) { + throw new TypeError(INVALID_NUMBER); +} + +if (typeof roundingFunc !== FUNCTION) { + throw new TypeError(INVALID_ROUND); } ``` @@ -225,386 +141,217 @@ function parseFileSize(input) { ### JSDoc Requirements -All public functions and classes must have comprehensive JSDoc comments: +All exported functions must have JSDoc: ```javascript /** - * Converts bytes to human-readable format with specified options - * @param {number|bigint} input - Number of bytes to convert + * Converts a file size in bytes to a human-readable string + * @param {number|string|bigint} arg - The file size in bytes * @param {Object} [options={}] - Configuration options - * @param {boolean} [options.binary=false] - Use binary (1024) vs decimal (1000) calculation - * @param {number} [options.precision=2] - Number of decimal places - * @param {string} [options.standard="jedec"] - Unit standard (jedec, iec, si) - * @param {string} [options.locale=""] - Locale for number formatting - * @returns {string|Object|Array} Formatted file size - * @throws {TypeError} When input is not a valid number - * @throws {RangeError} When precision is negative + * @param {boolean} [options.bits=false] - Calculate bits instead of bytes + * @param {number} [options.round=2] - Decimal places to round to + * @returns {string|Array|Object|number} Formatted file size + * @throws {TypeError} When arg is not a valid number * @example - * // Basic usage - * formatBytes(1024) // "1 KB" - * - * // With options - * formatBytes(1024, { binary: true, precision: 1 }) // "1.0 KiB" - * - * // Object output - * formatBytes(1024, { output: "object" }) - * // { value: 1, unit: "KB", symbol: "KB" } + * filesize(1024) // "1.02 kB" */ -function formatBytes(input, options = {}) { +export function filesize (arg, options = {}) { // Implementation } ``` -### Inline Comments +### Comment Style -- Use comments sparingly for complex logic -- Explain "why" not "what" -- Keep comments up-to-date with code changes +Use `//` for inline comments. Explain "why" not "what": ```javascript -// Calculate exponent based on logarithm -// This handles edge cases where Math.log returns unexpected values -const exponent = Math.max(0, Math.floor(Math.log(bytes) / Math.log(base))); -``` - -### README Documentation +// Fast path for zero +if (num === 0) { + return handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer); +} -- Include clear installation instructions -- Provide comprehensive API documentation -- Include practical examples -- Document all configuration options +// Optimized exponent calculation using pre-computed log values +if (e === -1 || isNaN(e)) { + e = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024); +} +``` ## Error Handling -### Error Types and Messages +### Specific Error Types -- Use specific error types for different error conditions -- Provide clear, actionable error messages -- Include context in error messages +Use specific error types with clear messages: ```javascript -// Good: Specific error handling -function validateInput(value) { - if (typeof value !== "number" && typeof value !== "bigint") { - throw new TypeError( - `Expected number or bigint, received ${typeof value}` - ); - } - - if (value < 0) { - throw new RangeError( - `Value must be non-negative, received ${value}` - ); - } +if (typeof arg !== "bigint" && isNaN(arg)) { + throw new TypeError(INVALID_NUMBER); +} + +if (typeof roundingFunc !== FUNCTION) { + throw new TypeError(INVALID_ROUND); } ``` ### Error Constants -Define error messages as constants to ensure consistency: +Define error messages as constants: ```javascript // constants.js -export const ERRORS = { - INVALID_NUMBER: "Invalid number: expected number or bigint", - INVALID_PRECISION: "Invalid precision: must be non-negative integer", - INVALID_STANDARD: "Invalid standard: must be 'jedec', 'iec', or 'si'", - INVALID_ROUNDING: "Invalid rounding method: method does not exist on Math object" -}; -``` - -### Graceful Degradation - -- Provide fallback behavior when possible -- Log warnings for recoverable errors -- Fail fast for critical errors - -```javascript -function formatWithLocale(value, locale, options) { - try { - return value.toLocaleString(locale, options); - } catch (error) { - // Fallback to default formatting - console.warn(`Locale formatting failed: ${error.message}`); - return value.toString(); - } -} +export const INVALID_NUMBER = "Invalid number"; +export const INVALID_ROUND = "Invalid rounding method"; ``` ## Module Structure -### ES6 Module Standards +### ES6 Modules -- Use ES6 import/export syntax -- Prefer named exports over default exports for utilities -- Use default exports for main functionality +Use ES6 import/export syntax: ```javascript -// constants.js - Named exports for related constants -export const BYTE_SIZES = { - KILOBYTE: 1024, - MEGABYTE: 1024 * 1024, - GIGABYTE: 1024 * 1024 * 1024 -}; +// Import grouped by source, alphabetized +import { + ARRAY, + BIT, + BITS, + BYTE, + BYTES, + EMPTY, + EXPONENT, + FUNCTION, + INVALID_NUMBER, + INVALID_ROUND, + LOG_10_1000, + LOG_2_1024, + OBJECT, + ROUND, + S, + SI_KBIT, + SI_KBYTE, + SPACE, + STRING, + STRINGS, +} from "./constants.js"; +import { + applyNumberFormatting, + applyPrecisionHandling, + calculateOptimizedValue, + getBaseConfiguration, + handleZeroValue +} from "./helpers.js"; +``` -export const STANDARDS = { - IEC: "iec", - JEDEC: "jedec" -}; +### Named Exports -// filesize.js - Default export for main function -import { BYTE_SIZES, STANDARDS } from "./constants.js"; +Prefer named exports for utilities: -export default function filesize(bytes, options) { +```javascript +export function filesize (arg, options = {}) { // Implementation } -// Also provide named export -export { filesize }; -``` - -### File Organization - -- Separate concerns into different modules -- Keep related functionality together -- Use barrel exports for clean public APIs - -```javascript -// index.js - Barrel export -export { default as filesize, filesize } from "./filesize.js"; -export { partial } from "./partial.js"; -export * from "./constants.js"; +export function partial (options = {}) { + return arg => filesize(arg, options); +} ``` ## Testing Standards ### Test Structure -- Use descriptive test names that explain behavior -- Group related tests with `describe` blocks -- Use `beforeEach`/`afterEach` for setup and cleanup +Use `node:test` with `node:assert`: ```javascript import assert from "node:assert"; -import { filesize } from "../src/filesize.js"; +import { describe, it } from "node:test"; +import { filesize } from "../../src/filesize.js"; describe("filesize()", () => { - describe("basic functionality", () => { - it("should convert bytes to KB correctly", () => { - const result = filesize(1024); - assert.strictEqual(result, "1 KB"); - }); - - it("should handle zero bytes", () => { - const result = filesize(0); - assert.strictEqual(result, "0 B"); - }); - }); - - describe("error handling", () => { - it("should throw TypeError for invalid input", () => { - assert.throws( - () => filesize("invalid"), - { name: "TypeError", message: /Invalid number/ } - ); - }); + it("should convert bytes to human readable format", () => { + const result = filesize(1024); + assert.strictEqual(result, "1.02 kB"); }); }); ``` -### Test Types - -#### Unit Tests (`tests/unit/`) -- Test individual functions in isolation -- Mock external dependencies -- Focus on function behavior and edge cases -- Use node:assert for assertions - -#### Integration Tests (`tests/integration/`) -- Test complete workflows -- Test module interactions -- Use real dependencies when appropriate -- Validate end-to-end functionality +### Coverage Requirements -### Test Coverage - -- Aim for 90%+ code coverage -- Cover all public API methods -- Test error conditions and edge cases -- Use c8 for coverage reporting - -```javascript -// Example comprehensive test -describe("filesize with options", () => { - const testCases = [ - { input: 1024, options: { binary: true }, expected: "1 KiB" }, - { input: 1000, options: { binary: false }, expected: "1 KB" }, - { input: 1536, options: { precision: 1 }, expected: "1.5 KB" } - ]; - - testCases.forEach(({ input, options, expected }) => { - it(`should return "${expected}" for ${input} bytes with options ${JSON.stringify(options)}`, () => { - assert.strictEqual(filesize(input, options), expected); - }); - }); -}); -``` +- **100%** statement, branch, function, and line coverage +- No uncovered lines allowed ## Security Guidelines ### Input Validation -Following OWASP security guidelines: - -- Validate all inputs at entry points -- Sanitize user-provided data -- Use type checking for security-critical operations +Validate all inputs at entry points: ```javascript -function secureFilesize(input, options = {}) { - // Input validation - if (typeof input !== "number" && typeof input !== "bigint") { - throw new TypeError("Input must be a number or bigint"); - } - - // Validate numeric ranges - if (typeof input === "number" && !Number.isFinite(input)) { - throw new RangeError("Input must be a finite number"); - } - - // Validate options object - if (options !== null && typeof options !== "object") { - throw new TypeError("Options must be an object"); - } - - return formatFilesize(input, options); +if (typeof arg !== "bigint" && isNaN(arg)) { + throw new TypeError(INVALID_NUMBER); } ``` -### Dependency Security - -- Regularly audit dependencies with `npm audit` -- Use specific version numbers in package.json -- Review security advisories for dependencies -- Minimize dependency count - -### Data Sanitization +### No External Dependencies -- Escape output for display contexts -- Validate object properties before use -- Use allow-lists for string values when possible - -```javascript -const ALLOWED_STANDARDS = ["jedec", "iec", "si"]; - -function validateStandard(standard) { - if (!ALLOWED_STANDARDS.includes(standard)) { - throw new Error(`Invalid standard: ${standard}`); - } - return standard; -} -``` +The project has zero external dependencies - use only native JavaScript APIs. ## Performance Considerations -### Optimization Guidelines +### Pre-computed Lookup Tables -- Profile before optimizing -- Prefer readable code over premature optimization -- Cache expensive calculations when appropriate -- Use appropriate data structures for the task +Use lookup tables instead of runtime calculations: ```javascript -// Good: Efficient object lookup instead of array iteration -const UNIT_MULTIPLIERS = { - B: 1, - KB: 1024, - MB: 1024 * 1024, - GB: 1024 * 1024 * 1024 -}; - -function getMultiplier(unit) { - return UNIT_MULTIPLIERS[unit] || 1; -} +// Pre-computed lookup tables for performance optimization +export const BINARY_POWERS = [ + 1, // 2^0 + 1024, // 2^10 + 1048576, // 2^20 + // ... +]; + +// Pre-computed log values for faster exponent calculation +export const LOG_2_1024 = Math.log(1024); +export const LOG_10_1000 = Math.log(1000); ``` -### Memory Management +### Fast Paths -- Avoid memory leaks with proper cleanup -- Use `const` and `let` appropriately -- Minimize object creation in hot paths +Optimize common cases: ```javascript -// Good: Reuse objects when possible -const formatOptions = { - minimumFractionDigits: 0, - maximumFractionDigits: 2 -}; - -function formatNumber(value, precision = 2) { - formatOptions.maximumFractionDigits = precision; - return value.toLocaleString("en-US", formatOptions); +// Fast path for zero +if (num === 0) { + return handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer); } ``` -## Tools and Linting - -### ESLint Configuration +## Tools -The project uses ESLint for code quality enforcement. Key rules include: +### Linting -- Consistent indentation (tabs) -- Semicolon usage -- Quote style consistency -- Unused variable detection -- Function complexity limits +Run before committing: -### Development Workflow - -1. **Linting**: Run `npm run lint` before committing -2. **Auto-fixing**: Use `npm run fix` for automatic fixes -3. **Testing**: Run `npm test` for full test suite -4. **Coverage**: Review coverage reports with c8 - -### Pre-commit Hooks - -The project uses Husky for pre-commit hooks: - -- Lint all staged files -- Run tests -- Validate commit message format - -### Recommended VS Code Extensions - -- ESLint -- Prettier (if configured) -- JavaScript (ES6) code snippets -- Path Intellisense -- GitLens - -## Code Review Guidelines +```bash +npm run lint # Check code style +npm run lint:fix # Auto-fix issues +``` -### Review Checklist +### Testing -- [ ] Code follows established patterns -- [ ] Functions have appropriate JSDoc documentation -- [ ] Error handling is comprehensive -- [ ] Tests cover new functionality -- [ ] No security vulnerabilities introduced -- [ ] Performance implications considered -- [ ] Breaking changes documented +```bash +npm test # Full test suite (lint + node:test) +npm run test:watch # Live test watching +``` -### Review Process +### Building -1. **Self-review**: Author reviews their own changes first -2. **Peer review**: At least one other developer reviews -3. **Testing**: All tests pass in CI/CD -4. **Documentation**: Updates made to relevant docs +```bash +npm run build # Build all distributions +npm run build:watch # Watch mode +npm run build:analyze # Bundle size analysis +``` --- -## Conclusion - -This style guide serves as the foundation for consistent, maintainable, and secure code in the filesize.js project. Regular updates to this guide should reflect evolving best practices and project needs. - -For questions or suggestions regarding this style guide, please open an issue in the project repository. \ No newline at end of file +This style guide reflects the actual patterns used in the filesize.js codebase. diff --git a/docs/TECHNICAL_DOCUMENTATION.md b/docs/TECHNICAL_DOCUMENTATION.md index 963856a..0ce5a76 100644 --- a/docs/TECHNICAL_DOCUMENTATION.md +++ b/docs/TECHNICAL_DOCUMENTATION.md @@ -207,7 +207,7 @@ When precision is specified ($p > 0$), the value is adjusted to show $p$ signifi 3. Re-applies rounding and precision formatting 4. This ensures output remains in standard decimal notation -The precision parameter takes precedence over round when both are specified. +The precision parameter is applied after rounding, providing significant digit control on top of the rounded value. ### Overflow Handling @@ -445,7 +445,7 @@ Creates a partially applied function with preset options. | `separator` | string | `""` | Custom decimal separator | | `spacer` | string | `" "` | Value-unit separator | | `symbols` | Object | `{}` | Custom unit symbols | -| `standard` | string | `""` | Unit standard (SI, IEC, JEDEC) | +| `standard` | string | `""` | Unit standard (si, iec, jedec) | | `output` | string | `"string"` | Output format | | `fullform` | boolean | `false` | Use full unit names | | `fullforms` | Array | `[]` | Custom full unit names | @@ -475,14 +475,14 @@ graph TD ```javascript // String output (default) -filesize(1536) // "1.5 KB" +filesize(1536) // "1.54 kB" // Array output -filesize(1536, { output: "array" }) // [1.5, "KB"] +filesize(1536, { output: "array" }) // [1.54, "kB"] // Object output filesize(1536, { output: "object" }) -// { value: 1.5, symbol: "KB", exponent: 1, unit: "KB" } +// { value: 1.54, symbol: "kB", exponent: 1, unit: "kB" } // Exponent output filesize(1536, { output: "exponent" }) // 1 @@ -496,26 +496,30 @@ filesize(1536, { output: "exponent" }) // 1 import { filesize } from 'filesize'; // Simple conversion -filesize(1024); // "1 KB" -filesize(1536); // "1.5 KB" -filesize(1073741824); // "1 GB" +filesize(1024); // "1.02 kB" +filesize(1536); // "1.54 kB" +filesize(1073741824); // "1.07 GB" ``` ### Advanced Configuration ```javascript // IEC binary standard -filesize(1024, { standard: "IEC" }); // "1 KiB" +filesize(1024, { standard: "iec" }); // "1 KiB" + +// JEDEC binary format (KB, MB, GB with binary calculation) +filesize(1024, { standard: "jedec" }); // "1 KB" +filesize(265318, { standard: "jedec" }); // "259.1 KB" // High precision -filesize(1536, { round: 3 }); // "1.500 KB" +filesize(1536, { round: 3 }); // "1.536 kB" // Bits instead of bytes -filesize(1024, { bits: true }); // "8 Kb" +filesize(1024, { bits: true }); // "8.19 kbit" // Object output for programmatic use filesize(1536, { output: "object" }); -// { value: 1.5, symbol: "KB", exponent: 1, unit: "KB" } +// { value: 1.54, symbol: "kB", exponent: 1, unit: "kB" } ``` ### Functional Programming Pattern @@ -524,11 +528,11 @@ filesize(1536, { output: "object" }); import { partial } from 'filesize'; // Create specialized formatters -const formatBinary = partial({ standard: "IEC", round: 1 }); +const formatBinary = partial({ standard: "iec", round: 1 }); const formatPrecise = partial({ round: 4, pad: true }); -formatBinary(1024); // "1.0 KiB" -formatPrecise(1536); // "1.5000 KB" +formatBinary(1024); // "1 KiB" +formatPrecise(1536); // "1.5360 kB" ``` ## Modern Application Examples (2025) @@ -541,7 +545,7 @@ import { filesize, partial } from 'filesize'; import { useLocale } from '@/hooks/useLocale'; const formatStorage = partial({ - standard: "IEC", + standard: "iec", round: 1, output: "object" }); @@ -551,13 +555,13 @@ function StorageWidget({ usage, quota }) { const usageFormatted = filesize(usage, { locale, - standard: "IEC", + standard: "iec", localeOptions: { notation: "compact" } }); const quotaFormatted = filesize(quota, { locale, - standard: "IEC" + standard: "iec" }); const percentage = (usage / quota) * 100; @@ -636,13 +640,13 @@ class DataUsageTracker { this.locale = locale; this.formatData = partial({ bits: true, - standard: "IEC", + standard: "iec", locale: this.locale, round: 1 }); this.formatBytes = partial({ - standard: "IEC", + standard: "iec", locale: this.locale, round: 2 }); @@ -707,7 +711,7 @@ class CacheManager { return { bytes: totalSize, formatted: filesize(totalSize, { - standard: "IEC", + standard: "iec", round: 1 }) }; @@ -754,7 +758,7 @@ export function SystemMetrics() { const formatMetric = (value: number, options = {}) => filesize(value, { locale, - standard: "IEC", + standard: "iec", round: 1, ...options }); @@ -839,13 +843,13 @@ graph TB ```javascript // Multi-language file size formatting const localizedFormatters = { - 'en-US': partial({ locale: 'en-US', standard: "JEDEC" }), - 'en-GB': partial({ locale: 'en-GB', standard: "IEC" }), - 'de-DE': partial({ locale: 'de-DE', standard: "IEC", separator: ',' }), - 'fr-FR': partial({ locale: 'fr-FR', standard: "SI" }), - 'ja-JP': partial({ locale: 'ja-JP', standard: "IEC" }), - 'zh-CN': partial({ locale: 'zh-CN', standard: "IEC" }), - 'ar-SA': partial({ locale: 'ar-SA', standard: "IEC" }) + 'en-US': partial({ locale: 'en-US', standard: "jedec" }), + 'en-GB': partial({ locale: 'en-GB', standard: "iec" }), + 'de-DE': partial({ locale: 'de-DE', standard: "iec", separator: ',' }), + 'fr-FR': partial({ locale: 'fr-FR', standard: "si" }), + 'ja-JP': partial({ locale: 'ja-JP', standard: "iec" }), + 'zh-CN': partial({ locale: 'zh-CN', standard: "iec" }), + 'ar-SA': partial({ locale: 'ar-SA', standard: "iec" }) }; // Usage in internationalized app @@ -855,9 +859,9 @@ function formatFileSize(bytes, userLocale = 'en-US') { } // Examples -formatFileSize(1536, 'en-US'); // "1.5 KB" -formatFileSize(1536, 'de-DE'); // "1,5 KB" (German decimal separator) -formatFileSize(1536, 'fr-FR'); // "1,5 ko" (French locale) +formatFileSize(1536, 'en-US'); // "1.54 kB" +formatFileSize(1536, 'de-DE'); // "1,5 KiB" (German decimal separator) +formatFileSize(1536, 'fr-FR'); // "1,54 kB" (French locale) ``` ### Advanced Locale Configuration @@ -867,7 +871,7 @@ formatFileSize(1536, 'fr-FR'); // "1,5 ko" (French locale) const regionConfigs = { europe: { locale: 'en-GB', - standard: 'IEC', + standard: 'iec', localeOptions: { minimumFractionDigits: 1, maximumFractionDigits: 2 @@ -876,7 +880,7 @@ const regionConfigs = { asia: { locale: 'ja-JP', - standard: 'IEC', + standard: 'iec', localeOptions: { notation: 'compact', compactDisplay: 'short' @@ -885,7 +889,7 @@ const regionConfigs = { americas: { locale: 'en-US', - standard: 'JEDEC', + standard: 'jedec', localeOptions: { style: 'decimal' } @@ -905,9 +909,9 @@ class LocalizedFileSize { formatWithContext(bytes, context = 'storage') { const contextOptions = { - storage: { standard: 'IEC' }, - network: { bits: true, standard: 'SI' }, - memory: { standard: 'IEC', round: 0 } + storage: { standard: 'iec' }, + network: { bits: true, standard: 'si' }, + memory: { standard: 'iec', round: 0 } }; return filesize(bytes, { @@ -966,7 +970,7 @@ graph TD ```javascript // 1. Formatter Reuse const commonFormatter = partial({ - standard: "IEC", + standard: "iec", round: 1, locale: "en-US" }); @@ -1023,6 +1027,33 @@ self.onmessage = function(e) { }; ``` +## Security + +filesize.js is designed with security best practices and is safe to use in production environments. + +### ✅ Secure Patterns + +The library implements the following security measures: + +1. **No `eval` or `Function` constructor** - No code injection risk +2. **No prototype pollution** - All object accesses use safe patterns: + - `symbols[result[1]]` only **reads** from user input, never writes + - `deepClone` uses `structuredClone`/`JSON.parse` which don't pollute prototypes + - No `__proto__`, `constructor`, or `Object.prototype` manipulation +3. **No command injection** - No shell execution +4. **No path traversal** - No file system operations +5. **No XSS** - Returns plain strings, no HTML/JS generation +6. **No SSRF** - No network requests +7. **No ReDoS** - Regex `/[.,]/g` is simple, no catastrophic backtracking +8. **Input validation** - Validates number input and rounding method + +### Security Considerations + +- The `symbols` option allows user-controlled objects. While currently safe (read-only access), if you ever add write operations to user-provided objects, use `Object.create(null)` or `Object.hasOwn()` checks to prevent prototype pollution. +- The `partial()` function uses destructuring to extract and freeze primitive option values at creation time. This approach is simpler than deep cloning while maintaining immutability for all option types. Mutations to the original options object after calling `partial()` will not affect the created formatter. + +**The library is secure as-is and requires no additional configuration.** + ## Integration Patterns ### Framework-Specific Integrations @@ -1056,7 +1087,7 @@ export function useFilesize(options = {}) { // Usage function FileList({ files }) { const { format } = useFilesize({ - standard: 'IEC', + standard: 'iec', locale: navigator.language }); @@ -1133,7 +1164,7 @@ export class FilesizeService { } formatWithLocale(bytes: number | bigint, locale: string): string { - return this.format(bytes, { locale, standard: 'IEC' }); + return this.format(bytes, { locale, standard: 'iec' }); } } ``` @@ -1152,7 +1183,7 @@ function filesizeMiddleware(options = {}) { } // Usage -app.use(filesizeMiddleware({ standard: 'IEC', locale: 'en-US' })); +app.use(filesizeMiddleware({ standard: 'iec', locale: 'en-US' })); app.get('/api/files', (req, res) => { const files = getFiles().map(file => ({ diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 755b755..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,20 +0,0 @@ -import globals from "globals"; -import pluginJs from "@eslint/js"; - -export default [ - { - languageOptions: { - globals: { - ...globals.browser, ...globals.node, ...globals.amd, - it: true, - describe: true, - BigInt: true, - beforeEach: true - } - }, - rules: { - "no-unused-vars": ["error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }] - } - }, - pluginJs.configs.recommended, -]; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b306e02..2cf2c71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,264 +11,15 @@ "devDependencies": { "@rollup/plugin-terser": "^1.0.0", "auto-changelog": "^2.5.0", - "c8": "^11.0.0", - "eslint": "^9.36.0", "husky": "^9.1.7", - "mocha": "^11.7.2", + "oxfmt": "^0.42.0", + "oxlint": "^1.57.0", "rollup": "^4.52.0" }, "engines": { "node": ">= 10.8.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", - "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", - "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", @@ -319,54 +70,10 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/plugin-terser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", - "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "serialize-javascript": "^7.0.3", - "smob": "^1.0.0", - "terser": "^5.17.4" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "rollup": "^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", - "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", - "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "node_modules/@oxfmt/binding-android-arm-eabi": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm-eabi/-/binding-android-arm-eabi-0.42.0.tgz", + "integrity": "sha512-dsqPTYsozeokRjlrt/b4E7Pj0z3eS3Eg74TWQuuKbjY4VttBmA88rB7d50Xrd+TZ986qdXCNeZRPEzZHAe+jow==", "cpu": [ "arm" ], @@ -375,12 +82,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", - "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "node_modules/@oxfmt/binding-android-arm64": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-android-arm64/-/binding-android-arm64-0.42.0.tgz", + "integrity": "sha512-t+aAjHxcr5eOBphFHdg1ouQU9qmZZoRxnX7UOJSaTwSoKsb6TYezNKO0YbWytGXCECObRqNcUxPoPr0KaraAIg==", "cpu": [ "arm64" ], @@ -389,12 +99,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", - "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "node_modules/@oxfmt/binding-darwin-arm64": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-arm64/-/binding-darwin-arm64-0.42.0.tgz", + "integrity": "sha512-ulpSEYMKg61C5bRMZinFHrKJYRoKGVbvMEXA5zM1puX3O9T6Q4XXDbft20yrDijpYWeuG59z3Nabt+npeTsM1A==", "cpu": [ "arm64" ], @@ -403,12 +116,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", - "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "node_modules/@oxfmt/binding-darwin-x64": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-darwin-x64/-/binding-darwin-x64-0.42.0.tgz", + "integrity": "sha512-ttxLKhQYPdFiM8I/Ri37cvqChE4Xa562nNOsZFcv1CKTVLeEozXjKuYClNvxkXmNlcF55nzM80P+CQkdFBu+uQ==", "cpu": [ "x64" ], @@ -417,26 +133,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", - "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", - "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "node_modules/@oxfmt/binding-freebsd-x64": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-freebsd-x64/-/binding-freebsd-x64-0.42.0.tgz", + "integrity": "sha512-Og7QS3yI3tdIKYZ58SXik0rADxIk2jmd+/YvuHRyKULWpG4V2fR5V4hvKm624Mc0cQET35waPXiCQWvjQEjwYQ==", "cpu": [ "x64" ], @@ -445,12 +150,15 @@ "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", - "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "node_modules/@oxfmt/binding-linux-arm-gnueabihf": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.42.0.tgz", + "integrity": "sha512-jwLOw/3CW4H6Vxcry4/buQHk7zm9Ne2YsidzTL1kpiMe4qqrRCwev3dkyWe2YkFmP+iZCQ7zku4KwjcLRoh8ew==", "cpu": [ "arm" ], @@ -459,12 +167,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", - "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "node_modules/@oxfmt/binding-linux-arm-musleabihf": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.42.0.tgz", + "integrity": "sha512-XwXu2vkMtiq2h7tfvN+WA/9/5/1IoGAVCFPiiQUvcAuG3efR97KNcRGM8BetmbYouFotQ2bDal3yyjUx6IPsTg==", "cpu": [ "arm" ], @@ -473,166 +184,226 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", - "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "node_modules/@oxfmt/binding-linux-arm64-gnu": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.42.0.tgz", + "integrity": "sha512-ea7s/XUJoT7ENAtUQDudFe3nkSM3e3Qpz4nJFRdzO2wbgXEcjnchKLEsV3+t4ev3r8nWxIYr9NRjPWtnyIFJVA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", - "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "node_modules/@oxfmt/binding-linux-arm64-musl": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.42.0.tgz", + "integrity": "sha512-+JA0YMlSdDqmacygGi2REp57c3fN+tzARD8nwsukx9pkCHK+6DkbAA9ojS4lNKsiBjIW8WWa0pBrBWhdZEqfuw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", - "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "node_modules/@oxfmt/binding-linux-ppc64-gnu": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.42.0.tgz", + "integrity": "sha512-VfnET0j4Y5mdfCzh5gBt0NK28lgn5DKx+8WgSMLYYeSooHhohdbzwAStLki9pNuGy51y4I7IoW8bqwAaCMiJQg==", "cpu": [ - "loong64" + "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", - "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "node_modules/@oxfmt/binding-linux-riscv64-gnu": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.42.0.tgz", + "integrity": "sha512-gVlCbmBkB0fxBWbhBj9rcxezPydsQHf4MFKeHoTSPicOQ+8oGeTQgQ8EeesSybWeiFPVRx3bgdt4IJnH6nOjAA==", "cpu": [ - "loong64" + "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", - "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "node_modules/@oxfmt/binding-linux-riscv64-musl": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.42.0.tgz", + "integrity": "sha512-zN5OfstL0avgt/IgvRu0zjQzVh/EPkcLzs33E9LMAzpqlLWiPWeMDZyMGFlSRGOdDjuNmlZBCgj0pFnK5u32TQ==", "cpu": [ - "ppc64" + "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", - "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "node_modules/@oxfmt/binding-linux-s390x-gnu": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.42.0.tgz", + "integrity": "sha512-9X6+H2L0qMc2sCAgO9HS03bkGLMKvOFjmEdchaFlany3vNZOjnVui//D8k/xZAtQv2vaCs1reD5KAgPoIU4msA==", "cpu": [ - "ppc64" + "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", - "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "node_modules/@oxfmt/binding-linux-x64-gnu": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.42.0.tgz", + "integrity": "sha512-BajxJ6KQvMMdpXGPWhBGyjb2Jvx4uec0w+wi6TJZ6Tv7+MzPwe0pO8g5h1U0jyFgoaF7mDl6yKPW3ykWcbUJRw==", "cpu": [ - "riscv64" + "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", - "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "node_modules/@oxfmt/binding-linux-x64-musl": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-linux-x64-musl/-/binding-linux-x64-musl-0.42.0.tgz", + "integrity": "sha512-0wV284I6vc5f0AqAhgAbHU2935B4bVpncPoe5n/WzVZY/KnHgqxC8iSFGeSyLWEgstFboIcWkOPck7tqbdHkzA==", "cpu": [ - "riscv64" + "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", - "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "node_modules/@oxfmt/binding-openharmony-arm64": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-openharmony-arm64/-/binding-openharmony-arm64-0.42.0.tgz", + "integrity": "sha512-p4BG6HpGnhfgHk1rzZfyR6zcWkE7iLrWxyehHfXUy4Qa5j3e0roglFOdP/Nj5cJJ58MA3isQ5dlfkW2nNEpolw==", "cpu": [ - "s390x" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", - "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "node_modules/@oxfmt/binding-win32-arm64-msvc": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.42.0.tgz", + "integrity": "sha512-mn//WV60A+IetORDxYieYGAoQso4KnVRRjORDewMcod4irlRe0OSC7YPhhwaexYNPQz/GCFk+v9iUcZ2W22yxQ==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", - "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "node_modules/@oxfmt/binding-win32-ia32-msvc": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.42.0.tgz", + "integrity": "sha512-3gWltUrvuz4LPJXWivoAxZ28Of2O4N7OGuM5/X3ubPXCEV8hmgECLZzjz7UYvSDUS3grfdccQwmjynm+51EFpw==", "cpu": [ - "x64" + "ia32" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", - "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "node_modules/@oxfmt/binding-win32-x64-msvc": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/@oxfmt/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.42.0.tgz", + "integrity": "sha512-Wg4TMAfQRL9J9AZevJ/ZNy3uyyDztDYQtGr4P8UyyzIhLhFrdSmz1J/9JT+rv0fiCDLaFOBQnj3f3K3+a5PzDQ==", "cpu": [ "x64" ], @@ -640,27 +411,33 @@ "license": "MIT", "optional": true, "os": [ - "openbsd" - ] + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", - "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.57.0.tgz", + "integrity": "sha512-C7EiyfAJG4B70496eV543nKiq5cH0o/xIh/ufbjQz3SIvHhlDDsyn+mRFh+aW8KskTyUpyH2LGWL8p2oN6bl1A==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "openharmony" - ] + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", - "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.57.0.tgz", + "integrity": "sha512-9i80AresjZ/FZf5xK8tKFbhQnijD4s1eOZw6/FHUwD59HEZbVLRc2C88ADYJfLZrF5XofWDiRX/Ja9KefCLy7w==", "cpu": [ "arm64" ], @@ -668,27 +445,33 @@ "license": "MIT", "optional": true, "os": [ - "win32" - ] + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", - "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.57.0.tgz", + "integrity": "sha512-0eUfhRz5L2yKa9I8k3qpyl37XK3oBS5BvrgdVIx599WZK63P8sMbg+0s4IuxmIiZuBK68Ek+Z+gcKgeYf0otsg==", "cpu": [ - "ia32" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" - ] + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", - "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.57.0.tgz", + "integrity": "sha512-UvrSuzBaYOue+QMAcuDITe0k/Vhj6KZGjfnI6x+NkxBTke/VoM7ZisaxgNY0LWuBkTnd1OmeQfEQdQ48fRjkQg==", "cpu": [ "x64" ], @@ -696,13 +479,16 @@ "license": "MIT", "optional": true, "os": [ - "win32" - ] + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", - "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.57.0.tgz", + "integrity": "sha512-wtQq0dCoiw4bUwlsNVDJJ3pxJA218fOezpgtLKrbQqUtQJcM9yP8z+I9fu14aHg0uyAxIY+99toL6uBa2r7nxA==", "cpu": [ "x64" ], @@ -710,2286 +496,1107 @@ "license": "MIT", "optional": true, "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, + "freebsd" + ], "engines": { - "node": ">=0.4.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.57.0.tgz", + "integrity": "sha512-qxFWl2BBBFcT4djKa+OtMdnLgoHEJXpqjyGwz8OhW35ImoCwR5qtAGqApNYce5260FQqoAHW8S8eZTjiX67Tsg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.57.0.tgz", + "integrity": "sha512-SQoIsBU7J0bDW15/f0/RvxHfY3Y0+eB/caKBQtNFbuerTiA6JCYx9P1MrrFTwY2dTm/lMgTSgskvCEYk2AtG/Q==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.57.0.tgz", + "integrity": "sha512-jqxYd1W6WMeozsCmqe9Rzbu3SRrGTyGDAipRlRggetyYbUksJqJKvUNTQtZR/KFoJPb+grnSm5SHhdWrywv3RQ==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.57.0.tgz", + "integrity": "sha512-i66WyEPVEvq9bxRUCJ/MP5EBfnTDN3nhwEdFZFTO5MmLLvzngfWEG3NSdXQzTT3vk5B9i6C2XSIYBh+aG6uqyg==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/auto-changelog": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.5.0.tgz", - "integrity": "sha512-UTnLjT7I9U2U/xkCUH5buDlp8C7g0SGChfib+iDrJkamcj5kaMqNKHNfbKJw1kthJUq8sUo3i3q2S6FzO/l/wA==", + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.57.0.tgz", + "integrity": "sha512-oMZDCwz4NobclZU3pH+V1/upVlJZiZvne4jQP+zhJwt+lmio4XXr4qG47CehvrW1Lx2YZiIHuxM2D4YpkG3KVA==", + "cpu": [ + "ppc64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "commander": "^7.2.0", - "handlebars": "^4.7.7", - "import-cwd": "^3.0.0", - "node-fetch": "^2.6.1", - "parse-github-url": "^1.0.3", - "semver": "^7.3.5" - }, - "bin": { - "auto-changelog": "src/index.js" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=8.3" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.57.0.tgz", + "integrity": "sha512-uoBnjJ3MMEBbfnWC1jSFr7/nSCkcQYa72NYoNtLl1imshDnWSolYCjzb8LVCwYCCfLJXD+0gBLD7fyC14c0+0g==", + "cpu": [ + "riscv64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "license": "ISC" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/c8": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-11.0.0.tgz", - "integrity": "sha512-e/uRViGHSVIJv7zsaDKM7VRn2390TgHXqUSvYwPHBQaU6L7E9L0n9JbdkwdYPvshDT0KymBmmlwSpms3yBaMNg==", - "dev": true, - "license": "ISC", - "dependencies": { - "@bcoe/v8-coverage": "^1.0.1", - "@istanbuljs/schema": "^0.1.3", - "find-up": "^5.0.0", - "foreground-child": "^3.1.1", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.1.6", - "test-exclude": "^8.0.0", - "v8-to-istanbul": "^9.0.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1" - }, - "bin": { - "c8": "bin/c8.js" - }, - "engines": { - "node": "20 || >=22" - }, - "peerDependencies": { - "monocart-coverage-reports": "^2" - }, - "peerDependenciesMeta": { - "monocart-coverage-reports": { - "optional": true - } - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.57.0.tgz", + "integrity": "sha512-BdrwD7haPZ8a9KrZhKJRSj6jwCor+Z8tHFZ3PT89Y3Jq5v3LfMfEePeAmD0LOTWpiTmzSzdmyw9ijneapiVHKQ==", + "cpu": [ + "riscv64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.57.0.tgz", + "integrity": "sha512-BNs+7ZNsRstVg2tpNxAXfMX/Iv5oZh204dVyb8Z37+/gCh+yZqNTlg6YwCLIMPSk5wLWIGOaQjT0GUOahKYImw==", + "cpu": [ + "s390x" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.57.0.tgz", + "integrity": "sha512-AghS18w+XcENcAX0+BQGLiqjpqpaxKJa4cWWP0OWNLacs27vHBxu7TYkv9LUSGe5w8lOJHeMxcYfZNOAPqw2bg==", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.57.0.tgz", + "integrity": "sha512-E/FV3GB8phu/Rpkhz5T96hAiJlGzn91qX5yj5gU754P5cmVGXY1Jw/VSjDSlZBCY3VHjsVLdzgdkJaomEmcNOg==", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.57.0.tgz", + "integrity": "sha512-xvZ2yZt0nUVfU14iuGv3V25jpr9pov5N0Wr28RXnHFxHCRxNDMtYPHV61gGLhN9IlXM96gI4pyYpLSJC5ClLCQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=12" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.57.0.tgz", + "integrity": "sha512-Z4D8Pd0AyHBKeazhdIXeUUy5sIS3Mo0veOlzlDECg6PhRRKgEsBJCCV1n+keUZtQ04OP+i7+itS3kOykUyNhDg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.57.0.tgz", + "integrity": "sha512-StOZ9nFMVKvevicbQfql6Pouu9pgbeQnu60Fvhz2S6yfMaii+wnueLnqQ5I1JPgNF0Syew4voBlAaHD13wH6tw==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.57.0.tgz", + "integrity": "sha512-6PuxhYgth8TuW0+ABPOIkGdBYw+qYGxgIdXPHSVpiCDm+hqTTWCmC739St1Xni0DJBt8HnSHTG67i1y6gr8qrA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/@rollup/plugin-terser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", + "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "serialize-javascript": "^7.0.3", + "smob": "^1.0.0", + "terser": "^5.17.4" }, "engines": { - "node": ">=10" + "node": ">=20.0.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@rollup/plugin-terser/node_modules/serialize-javascript": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.4.tgz", + "integrity": "sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==", "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=7.0.0" + "node": ">=20.0.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "android" + ] }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">= 10" - } + "optional": true, + "os": [ + "android" + ] }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } + "optional": true, + "os": [ + "freebsd" + ] }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.39.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", - "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.1", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.39.2", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", - "dev": true, - "license": "ISC" - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/husky": { - "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", - "dev": true, - "license": "MIT", - "bin": { - "husky": "bin.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", - "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", - "dev": true, - "license": "MIT", - "dependencies": { - "import-from": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", - "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-from/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mocha": { - "version": "11.7.5", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", - "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", - "dev": true, - "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^7.0.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^9.2.0", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-github-url": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.3.tgz", - "integrity": "sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==", - "dev": true, - "license": "MIT", - "bin": { - "parse-github-url": "cli.js" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=6" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "cpu": [ + "loong64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=4" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/rollup": { + "node_modules/@rollup/rollup-linux-riscv64-gnu": { "version": "4.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", - "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.0", - "@rollup/rollup-android-arm64": "4.60.0", - "@rollup/rollup-darwin-arm64": "4.60.0", - "@rollup/rollup-darwin-x64": "4.60.0", - "@rollup/rollup-freebsd-arm64": "4.60.0", - "@rollup/rollup-freebsd-x64": "4.60.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", - "@rollup/rollup-linux-arm-musleabihf": "4.60.0", - "@rollup/rollup-linux-arm64-gnu": "4.60.0", - "@rollup/rollup-linux-arm64-musl": "4.60.0", - "@rollup/rollup-linux-loong64-gnu": "4.60.0", - "@rollup/rollup-linux-loong64-musl": "4.60.0", - "@rollup/rollup-linux-ppc64-gnu": "4.60.0", - "@rollup/rollup-linux-ppc64-musl": "4.60.0", - "@rollup/rollup-linux-riscv64-gnu": "4.60.0", - "@rollup/rollup-linux-riscv64-musl": "4.60.0", - "@rollup/rollup-linux-s390x-gnu": "4.60.0", - "@rollup/rollup-linux-x64-gnu": "4.60.0", - "@rollup/rollup-linux-x64-musl": "4.60.0", - "@rollup/rollup-openbsd-x64": "4.60.0", - "@rollup/rollup-openharmony-arm64": "4.60.0", - "@rollup/rollup-win32-arm64-msvc": "4.60.0", - "@rollup/rollup-win32-ia32-msvc": "4.60.0", - "@rollup/rollup-win32-x64-gnu": "4.60.0", - "@rollup/rollup-win32-x64-msvc": "4.60.0", - "fsevents": "~2.3.2" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "cpu": [ + "riscv64" ], - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/smob": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", - "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", - "dev": true, - "license": "MIT" - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "openbsd" + ] }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" + "optional": true, + "os": [ + "openharmony" + ] }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "engines": { - "node": ">=8" - } + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=8" + "node": ">=0.4.0" } }, - "node_modules/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "node_modules/auto-changelog": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/auto-changelog/-/auto-changelog-2.5.0.tgz", + "integrity": "sha512-UTnLjT7I9U2U/xkCUH5buDlp8C7g0SGChfib+iDrJkamcj5kaMqNKHNfbKJw1kthJUq8sUo3i3q2S6FzO/l/wA==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" + "commander": "^7.2.0", + "handlebars": "^4.7.7", + "import-cwd": "^3.0.0", + "node-fetch": "^2.6.1", + "parse-github-url": "^1.0.3", + "semver": "^7.3.5" }, "bin": { - "terser": "bin/terser" + "auto-changelog": "src/index.js" }, "engines": { - "node": ">=10" + "node": ">=8.3" } }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT" }, - "node_modules/test-exclude": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-8.0.0.tgz", - "integrity": "sha512-ZOffsNrXYggvU1mDGHk54I96r26P8SyMjO5slMKSc7+IWmtB/MQKnEC2fP51imB3/pT6YK5cT5E8f+Dd9KdyOQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^13.0.6", - "minimatch": "^10.2.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/test-exclude/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true, "license": "MIT", "engines": { - "node": "18 || 20 || >=22" + "node": ">= 10" } }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/lru-cache": { - "version": "11.2.6", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", - "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", - "dev": true, - "license": "BlueOak-1.0.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": "20 || >=22" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dev": true, - "license": "BlueOak-1.0.0", + "license": "MIT", "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": "18 || 20 || >=22" + "node": ">=0.4.7" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, "bin": { - "uglifyjs": "bin/uglifyjs" + "husky": "bin.js" }, "engines": { - "node": ">=0.8.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/import-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-3.0.0.tgz", + "integrity": "sha512-4pnzH16plW+hgvRECbDWpQl3cqtvSofHWh44met7ESfZ8UZOWWddm8hEyDTqREJ9RbYHY8gi8DqmaelApoOGMg==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "punycode": "^2.1.0" + "import-from": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "node_modules/import-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz", + "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=10.12.0" + "node": ">=8" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { + "node_modules/import-from/node_modules/resolve-from": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, "license": "MIT" }, - "node_modules/workerpool": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz", - "integrity": "sha512-slxCaKbYjEdFT/o2rH9xS1hf4uRDch1w7Uo+apxhZ+sf/1d9e0ZVkn42kPNGP2dgjIx6YFvSevj0zHvbWe2jdw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=12" + "node": "4.x || >=6.0.0" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/oxfmt": { + "version": "0.42.0", + "resolved": "https://registry.npmjs.org/oxfmt/-/oxfmt-0.42.0.tgz", + "integrity": "sha512-QhejGErLSMReNuZ6vxgFHDyGoPbjTRNi6uGHjy0cvIjOQFqD6xmr/T+3L41ixR3NIgzcNiJ6ylQKpvShTgDfqg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "tinypool": "2.1.0" + }, + "bin": { + "oxfmt": "bin/oxfmt" }, "engines": { - "node": ">=10" + "node": "^20.19.0 || >=22.12.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxfmt/binding-android-arm-eabi": "0.42.0", + "@oxfmt/binding-android-arm64": "0.42.0", + "@oxfmt/binding-darwin-arm64": "0.42.0", + "@oxfmt/binding-darwin-x64": "0.42.0", + "@oxfmt/binding-freebsd-x64": "0.42.0", + "@oxfmt/binding-linux-arm-gnueabihf": "0.42.0", + "@oxfmt/binding-linux-arm-musleabihf": "0.42.0", + "@oxfmt/binding-linux-arm64-gnu": "0.42.0", + "@oxfmt/binding-linux-arm64-musl": "0.42.0", + "@oxfmt/binding-linux-ppc64-gnu": "0.42.0", + "@oxfmt/binding-linux-riscv64-gnu": "0.42.0", + "@oxfmt/binding-linux-riscv64-musl": "0.42.0", + "@oxfmt/binding-linux-s390x-gnu": "0.42.0", + "@oxfmt/binding-linux-x64-gnu": "0.42.0", + "@oxfmt/binding-linux-x64-musl": "0.42.0", + "@oxfmt/binding-openharmony-arm64": "0.42.0", + "@oxfmt/binding-win32-arm64-msvc": "0.42.0", + "@oxfmt/binding-win32-ia32-msvc": "0.42.0", + "@oxfmt/binding-win32-x64-msvc": "0.42.0" + } + }, + "node_modules/oxlint": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.57.0.tgz", + "integrity": "sha512-DGFsuBX5MFZX9yiDdtKjTrYPq45CZ8Fft6qCltJITYZxfwYjVdGf/6wycGYTACloauwIPxUnYhBVeZbHvleGhw==", "dev": true, "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, "engines": { - "node": ">=8" + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.57.0", + "@oxlint/binding-android-arm64": "1.57.0", + "@oxlint/binding-darwin-arm64": "1.57.0", + "@oxlint/binding-darwin-x64": "1.57.0", + "@oxlint/binding-freebsd-x64": "1.57.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.57.0", + "@oxlint/binding-linux-arm-musleabihf": "1.57.0", + "@oxlint/binding-linux-arm64-gnu": "1.57.0", + "@oxlint/binding-linux-arm64-musl": "1.57.0", + "@oxlint/binding-linux-ppc64-gnu": "1.57.0", + "@oxlint/binding-linux-riscv64-gnu": "1.57.0", + "@oxlint/binding-linux-riscv64-musl": "1.57.0", + "@oxlint/binding-linux-s390x-gnu": "1.57.0", + "@oxlint/binding-linux-x64-gnu": "1.57.0", + "@oxlint/binding-linux-x64-musl": "1.57.0", + "@oxlint/binding-openharmony-arm64": "1.57.0", + "@oxlint/binding-win32-arm64-msvc": "1.57.0", + "@oxlint/binding-win32-ia32-msvc": "1.57.0", + "@oxlint/binding-win32-x64-msvc": "1.57.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=0.15.0" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + } } }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/parse-github-url": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.3.tgz", + "integrity": "sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww==", "dev": true, "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "bin": { + "parse-github-url": "cli.js" }, "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/rollup": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", + "fsevents": "~2.3.2" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, "engines": { "node": ">=10" } }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "node_modules/smob": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", + "integrity": "sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==", "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "license": "ISC", + "license": "BSD-3-Clause", "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "license": "MIT", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/terser": { + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.14.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" }, "engines": { "node": ">=10" } }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-2.1.0.tgz", + "integrity": "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==", "dev": true, "license": "MIT", "engines": { - "node": ">=8" + "node": "^20.0.0 || >=22.0.0" } }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true, "license": "MIT" }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, "engines": { - "node": ">=8" + "node": ">=0.8.0" } }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" } } } diff --git a/package.json b/package.json index 87805ee..74d13dd 100644 --- a/package.json +++ b/package.json @@ -37,13 +37,11 @@ "build:analyze": "npm run rollup && npm run analyze:size", "analyze:size": "echo 'Bundle size analysis:' && du -h dist/* && echo 'Gzipped sizes:' && gzip -c dist/filesize.min.js | wc -c | awk '{print $1\" bytes (gzipped)\"}' && gzip -c dist/filesize.umd.min.js | wc -c | awk '{print $1\" bytes (umd gzipped)\"}'", "changelog": "auto-changelog -p", - "lint": "eslint *.js src/*.js tests/**/*.js", - "lint:fix": "eslint --fix *.js src/*.js tests/**/*.js", - "fix": "npm run lint:fix", - "mocha": "c8 mocha tests/**/*.js", + "lint": "npx oxlint *.js benchmarks src tests && npx oxfmt *.js benchmarks src tests/unit --check", + "fix": "npx oxlint --fix *.js benchmarks src tests && npx oxfmt *.js benchmarks src tests/unit --write", + "test": "npm run lint && node --test --experimental-test-coverage tests/**/*.js", + "test:watch": "node --test --watch tests/**/*.js", "rollup": "rollup --config", - "test": "npm run lint && npm run mocha", - "test:watch": "npm run mocha -- --watch", "prepack": "npm run build", "prepare": "husky", "dev": "npm run build:watch", @@ -57,10 +55,9 @@ "devDependencies": { "@rollup/plugin-terser": "^1.0.0", "auto-changelog": "^2.5.0", - "c8": "^11.0.0", - "eslint": "^9.36.0", "husky": "^9.1.7", - "mocha": "^11.7.2", + "oxfmt": "^0.42.0", + "oxlint": "^1.57.0", "rollup": "^4.52.0" }, "keywords": [ diff --git a/rollup.config.js b/rollup.config.js index 4466f56..833424c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -18,101 +18,120 @@ const bannerShort = `/*! @version ${pkg.version} */`; +// Plugin to ensure output files end with a newline +function ensureNewline() { + return { + name: "ensure-newline", + generateBundle(_, bundle) { + for (const fileName in bundle) { + const chunk = bundle[fileName]; + if (chunk.type === "asset") { + let source = chunk.source; + if (typeof source === "string" && !source.endsWith("\n")) { + chunk.source = source + "\n"; + } + } else if (chunk.type === "chunk" && chunk.code && !chunk.code.endsWith("\n")) { + chunk.code += "\n"; + } + } + }, + }; +} + // Optimized terser configuration for better compression const terserOptions = { compress: { passes: 2, drop_console: false, drop_debugger: true, - pure_funcs: ['console.log'], + pure_funcs: ["console.log"], unsafe_arrows: true, - unsafe_methods: true + unsafe_methods: true, }, mangle: { properties: { - regex: /^_/ - } + regex: /^_/, + }, }, format: { - comments: "some" - } + comments: "some", + }, }; const defaultOutBase = { - compact: true, - banner: bannerLong, + compact: true, + banner: bannerLong, name: pkg.name, generatedCode: { constBindings: true, arrowFunctions: true, - objectShorthand: true - } + objectShorthand: true, + }, }; const cjOutBase = { - ...defaultOutBase, - compact: false, - format: "cjs", + ...defaultOutBase, + compact: false, + format: "cjs", exports: "named", - interop: "compat" + interop: "compat", }; const esmOutBase = { - ...defaultOutBase, - format: "esm" + ...defaultOutBase, + format: "esm", }; const umdOutBase = { - ...defaultOutBase, - format: "umd" + ...defaultOutBase, + format: "umd", }; const minOutBase = { - banner: bannerShort, - name: pkg.name, - plugins: [terser(terserOptions)], + ...defaultOutBase, + banner: bannerShort, + plugins: [terser(terserOptions)], sourcemap: true, - generatedCode: { - constBindings: true, - arrowFunctions: true, - objectShorthand: true - } }; - export default [ { input: "./src/filesize.js", treeshake: { moduleSideEffects: false, propertyReadSideEffects: false, - unknownGlobalSideEffects: false + unknownGlobalSideEffects: false, }, output: [ { ...cjOutBase, - file: `dist/${pkg.name}.cjs` + file: `dist/${pkg.name}.cjs`, + plugins: [ensureNewline()], }, { ...esmOutBase, - file: `dist/${pkg.name}.js` + file: `dist/${pkg.name}.js`, + plugins: [ensureNewline()], }, { ...esmOutBase, ...minOutBase, - file: `dist/${pkg.name}.min.js` + file: `dist/${pkg.name}.min.js`, + plugins: [...minOutBase.plugins, ensureNewline()], }, { ...umdOutBase, file: `dist/${pkg.name}.umd.js`, - name: "filesize" + name: "filesize", + plugins: [ensureNewline()], }, { ...umdOutBase, ...minOutBase, file: `dist/${pkg.name}.umd.min.js`, - name: "filesize" - } - ] - } + name: "filesize", + plugins: [...minOutBase.plugins, ensureNewline()], + }, + ], + }, ]; diff --git a/src/constants.js b/src/constants.js index 4747e4d..4401f31 100644 --- a/src/constants.js +++ b/src/constants.js @@ -38,17 +38,17 @@ export const STRINGS = { symbol: { iec: { bits: ["bit", "Kibit", "Mibit", "Gibit", "Tibit", "Pibit", "Eibit", "Zibit", "Yibit"], - bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"] + bytes: ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"], }, jedec: { bits: ["bit", "Kbit", "Mbit", "Gbit", "Tbit", "Pbit", "Ebit", "Zbit", "Ybit"], - bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] - } + bytes: ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"], + }, }, fullform: { iec: ["", "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi"], - jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"] - } + jedec: ["", "kilo", "mega", "giga", "tera", "peta", "exa", "zetta", "yotta"], + }, }; // Pre-computed lookup tables for performance optimization @@ -61,7 +61,7 @@ export const BINARY_POWERS = [ 1125899906842624, // 2^50 1152921504606846976, // 2^60 1180591620717411303424, // 2^70 - 1208925819614629174706176 // 2^80 + 1208925819614629174706176, // 2^80 ]; export const DECIMAL_POWERS = [ @@ -73,7 +73,7 @@ export const DECIMAL_POWERS = [ 1000000000000000, // 10^15 1000000000000000000, // 10^18 1000000000000000000000, // 10^21 - 1000000000000000000000000 // 10^24 + 1000000000000000000000000, // 10^24 ]; // Pre-computed log values for faster exponent calculation diff --git a/src/filesize.js b/src/filesize.js index c7bdb1a..ae3645a 100644 --- a/src/filesize.js +++ b/src/filesize.js @@ -25,7 +25,7 @@ import { applyPrecisionHandling, calculateOptimizedValue, getBaseConfiguration, - handleZeroValue + handleZeroValue, } from "./helpers.js"; /** @@ -51,28 +51,31 @@ import { * @returns {string|Array|Object|number} Formatted file size based on output option * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid * @example - * filesize(1024) // "1 KB" - * filesize(1024, {bits: true}) // "8 Kb" - * filesize(1024, {output: "object"}) // {value: 1, symbol: "KB", exponent: 1, unit: "KB"} + * filesize(1024) // "1.02 kB" + * filesize(1024, {bits: true}) // "8.19 kbit" + * filesize(1024, {output: "object"}) // {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"} */ -export function filesize (arg, { - bits = false, - pad = false, - base = -1, - round = 2, - locale = EMPTY, - localeOptions = {}, - separator = EMPTY, - spacer = SPACE, - symbols = {}, - standard = EMPTY, - output = STRING, - fullform = false, - fullforms = [], - exponent = -1, - roundingMethod = ROUND, - precision = 0 -} = {}) { +export function filesize( + arg, + { + bits = false, + pad = false, + base = -1, + round = 2, + locale = EMPTY, + localeOptions = {}, + separator = EMPTY, + spacer = SPACE, + symbols = {}, + standard = EMPTY, + output = STRING, + fullform = false, + fullforms = [], + exponent = -1, + roundingMethod = ROUND, + precision = 0, + } = {}, +) { let e = exponent, num = Number(arg), result = [], @@ -80,7 +83,7 @@ export function filesize (arg, { u = EMPTY; // Optimized base & standard configuration lookup - const {isDecimal, ceil, actualStandard} = getBaseConfiguration(standard, base); + const { isDecimal, ceil, actualStandard } = getBaseConfiguration(standard, base); const full = fullform === true, neg = num < 0, @@ -101,12 +104,23 @@ export function filesize (arg, { // Fast path for zero if (num === 0) { - return handleZeroValue(precision, actualStandard, bits, symbols, full, fullforms, output, spacer); + return handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + ); } // Optimized exponent calculation using pre-computed log values if (e === -1 || isNaN(e)) { - e = isDecimal ? Math.floor(Math.log(num) / LOG_10_1000) : Math.floor(Math.log(num) / LOG_2_1024); + e = isDecimal + ? Math.floor(Math.log(num) / LOG_10_1000) + : Math.floor(Math.log(num) / LOG_2_1024); if (e < 0) { e = 0; } @@ -120,12 +134,21 @@ export function filesize (arg, { e = 8; } + const autoExponent = exponent === -1 || isNaN(exponent); + if (output === EXPONENT) { return e; } // Calculate value with optimized lookup and bits handling - const {result: valueResult, e: valueExponent} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult, e: valueExponent } = calculateOptimizedValue( + num, + e, + isDecimal, + bits, + ceil, + autoExponent, + ); val = valueResult; e = valueExponent; @@ -133,21 +156,32 @@ export function filesize (arg, { const p = e > 0 && round > 0 ? Math.pow(10, round) : 1; result[0] = p === 1 ? roundingFunc(val) : roundingFunc(val * p) / p; - if (result[0] === ceil && e < 8 && exponent === -1) { + if (result[0] === ceil && e < 8 && autoExponent) { result[0] = 1; e++; } // Apply precision handling if (precision > 0) { - const precisionResult = applyPrecisionHandling(result[0], precision, e, num, isDecimal, bits, ceil, roundingFunc, round); + const precisionResult = applyPrecisionHandling( + result[0], + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, + ); result[0] = precisionResult.value; e = precisionResult.e; } // Cache symbol lookup const symbolTable = STRINGS.symbol[actualStandard][bits ? BITS : BYTES]; - u = result[1] = (isDecimal && e === 1) ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; + u = result[1] = isDecimal && e === 1 ? (bits ? SI_KBIT : SI_KBYTE) : symbolTable[e]; // Decorating a 'diff' if (neg) { @@ -163,7 +197,9 @@ export function filesize (arg, { result[0] = applyNumberFormatting(result[0], locale, localeOptions, separator, pad, round); if (full) { - result[1] = fullforms[e] || STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); + result[1] = + fullforms[e] || + STRINGS.fullform[actualStandard][e] + (bits ? BIT : BYTE) + (result[0] === 1 ? EMPTY : S); } // Optimized return logic @@ -176,7 +212,7 @@ export function filesize (arg, { value: result[0], symbol: result[1], exponent: e, - unit: u + unit: u, }; } @@ -185,7 +221,7 @@ export function filesize (arg, { /** * Creates a partially applied version of filesize with preset options - * @param {Object} [options={}] - Default options to apply to the returned function + * @param {Object} [options={}] - Configuration options (same as filesize) * @param {boolean} [options.bits=false] - If true, calculates bits instead of bytes * @param {boolean} [options.pad=false] - If true, pads decimal places to match round parameter * @param {number} [options.base=-1] - Number base (2 for binary, 10 for decimal, -1 for auto) @@ -204,12 +240,12 @@ export function filesize (arg, { * @param {number} [options.precision=0] - Number of significant digits (0 for auto) * @returns {Function} A function that takes a file size and returns formatted output * @example - * const formatBytes = partial({round: 1, standard: "IEC"}); - * formatBytes(1024) // "1.0 KiB" - * formatBytes(2048) // "2.0 KiB" + * const formatBytes = partial({round: 1, standard: "iec"}); + * formatBytes(1024) // "1 KiB" + * formatBytes(2048) // "2 KiB" + * formatBytes(1536) // "1.5 KiB" */ -// Partial application for functional programming -export function partial ({ +export function partial({ bits = false, pad = false, base = -1, @@ -225,24 +261,25 @@ export function partial ({ fullforms = [], exponent = -1, roundingMethod = ROUND, - precision = 0 + precision = 0, } = {}) { - return arg => filesize(arg, { - bits, - pad, - base, - round, - locale, - localeOptions, - separator, - spacer, - symbols, - standard, - output, - fullform, - fullforms, - exponent, - roundingMethod, - precision - }); + return (arg) => + filesize(arg, { + bits, + pad, + base, + round, + locale, + localeOptions, + separator, + spacer, + symbols, + standard, + output, + fullform, + fullforms, + exponent, + roundingMethod, + precision, + }); } diff --git a/src/helpers.js b/src/helpers.js index cbbe029..9556a90 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -2,9 +2,7 @@ import { ARRAY, BINARY_POWERS, BIT, - BITS, BYTE, - BYTES, DECIMAL_POWERS, E, EMPTY, @@ -15,14 +13,14 @@ import { PERIOD, SI, STRINGS, - ZERO + ZERO, } from "./constants.js"; // Cached configuration lookup for better performance const STANDARD_CONFIGS = { - [SI]: {isDecimal: true, ceil: 1000, actualStandard: JEDEC}, - [IEC]: {isDecimal: false, ceil: 1024, actualStandard: IEC}, - [JEDEC]: {isDecimal: false, ceil: 1024, actualStandard: JEDEC} + [SI]: { isDecimal: true, ceil: 1000, actualStandard: JEDEC }, + [IEC]: { isDecimal: false, ceil: 1024, actualStandard: IEC }, + [JEDEC]: { isDecimal: false, ceil: 1024, actualStandard: JEDEC }, }; /** @@ -31,7 +29,7 @@ const STANDARD_CONFIGS = { * @param {number} base - Base number * @returns {Object} Configuration object */ -export function getBaseConfiguration (standard, base) { +export function getBaseConfiguration(standard, base) { // Use cached lookup table for better performance if (STANDARD_CONFIGS[standard]) { return STANDARD_CONFIGS[standard]; @@ -39,11 +37,11 @@ export function getBaseConfiguration (standard, base) { // Base override if (base === 2) { - return {isDecimal: false, ceil: 1024, actualStandard: IEC}; + return { isDecimal: false, ceil: 1024, actualStandard: IEC }; } // Default - return {isDecimal: true, ceil: 1000, actualStandard: JEDEC}; + return { isDecimal: true, ceil: 1000, actualStandard: JEDEC }; } /** @@ -56,34 +54,53 @@ export function getBaseConfiguration (standard, base) { * @param {Array} fullforms - Custom full forms * @param {string} output - Output format * @param {string} spacer - Spacer character + * @param {string} [symbol] - Symbol to use (defaults based on bits/standard) * @returns {string|Array|Object|number} Formatted result */ -export function handleZeroValue (precision, actualStandard, bits, symbols, full, fullforms, output, spacer) { - const result = []; - result[0] = precision > 0 ? (0).toPrecision(precision) : 0; - const u = result[1] = STRINGS.symbol[actualStandard][bits ? BITS : BYTES][0]; +export function handleZeroValue( + precision, + actualStandard, + bits, + symbols, + full, + fullforms, + output, + spacer, + symbol, +) { + const value = precision > 0 ? (0).toPrecision(precision) : 0; if (output === EXPONENT) { return 0; } + // Set default symbol if not provided + if (!symbol) { + symbol = bits + ? STRINGS.symbol[actualStandard].bits[0] + : STRINGS.symbol[actualStandard].bytes[0]; + } + // Apply symbol customization - if (symbols[result[1]]) { - result[1] = symbols[result[1]]; + if (symbols[symbol]) { + symbol = symbols[symbol]; } // Apply full form if (full) { - result[1] = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); + symbol = fullforms[0] || STRINGS.fullform[actualStandard][0] + (bits ? BIT : BYTE); } // Return in requested format - return output === ARRAY ? result : output === OBJECT ? { - value: result[0], - symbol: result[1], - exponent: 0, - unit: u - } : result.join(spacer); + if (output === ARRAY) { + return [value, symbol]; + } + + if (output === OBJECT) { + return { value, symbol, exponent: 0, unit: symbol }; + } + + return value + spacer + symbol; } /** @@ -93,22 +110,23 @@ export function handleZeroValue (precision, actualStandard, bits, symbols, full, * @param {boolean} isDecimal - Whether to use decimal powers * @param {boolean} bits - Whether to calculate bits * @param {number} ceil - Ceiling value for auto-increment - * @returns {Object} Object with val and e properties + * @param {boolean} autoExponent - Whether exponent is auto (-1 or NaN) + * @returns {Object} Object with result and e properties */ -export function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { +export function calculateOptimizedValue(num, e, isDecimal, bits, ceil, autoExponent = true) { const d = isDecimal ? DECIMAL_POWERS[e] : BINARY_POWERS[e]; let result = num / d; if (bits) { result *= 8; - // Handle auto-increment for bits - if (result >= ceil && e < 8) { + // Handle auto-increment for bits (only when exponent is auto) + if (autoExponent && result >= ceil && e < 8) { result /= ceil; e++; } } - return {result, e}; + return { result, e }; } /** @@ -122,20 +140,36 @@ export function calculateOptimizedValue (num, e, isDecimal, bits, ceil) { * @param {number} ceil - Ceiling value * @param {Function} roundingFunc - Rounding function * @param {number} round - Round value + * @param {number} exponent - Forced exponent (-1 for auto) * @returns {Object} Object with value and e properties */ -export function applyPrecisionHandling (value, precision, e, num, isDecimal, bits, ceil, roundingFunc, round) { +export function applyPrecisionHandling( + value, + precision, + e, + num, + isDecimal, + bits, + ceil, + roundingFunc, + round, + exponent, +) { let result = value.toPrecision(precision); + const autoExponent = exponent === -1 || isNaN(exponent); + // Handle scientific notation by recalculating with incremented exponent - if (result.includes(E) && e < 8) { + if (result.includes(E) && e < 8 && autoExponent) { e++; - const {result: valueResult} = calculateOptimizedValue(num, e, isDecimal, bits, ceil); + const { result: valueResult } = calculateOptimizedValue(num, e, isDecimal, bits, ceil); const p = round > 0 ? Math.pow(10, round) : 1; - result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision(precision); + result = (p === 1 ? roundingFunc(valueResult) : roundingFunc(valueResult * p) / p).toPrecision( + precision, + ); } - return {value: result, e}; + return { value: result, e }; } /** @@ -148,7 +182,7 @@ export function applyPrecisionHandling (value, precision, e, num, isDecimal, bit * @param {number} round - Round value * @returns {string|number} Formatted value */ -export function applyNumberFormatting (value, locale, localeOptions, separator, pad, round) { +export function applyNumberFormatting(value, locale, localeOptions, separator, pad, round) { let result = value; // Apply locale formatting @@ -163,9 +197,10 @@ export function applyNumberFormatting (value, locale, localeOptions, separator, // Apply padding if (pad && round > 0) { const resultStr = result.toString(); - const x = separator || ((resultStr.match(/(\D)/g) || []).pop() || PERIOD); + const x = separator || (resultStr.slice(1).match(/[.,]/g) || []).pop() || PERIOD; const tmp = resultStr.split(x); const s = tmp[1] || EMPTY; + const l = s.length; const n = round - l; diff --git a/tests/unit/filesize-helpers.test.js b/tests/unit/filesize-helpers.test.js index e3e31ff..4116f59 100644 --- a/tests/unit/filesize-helpers.test.js +++ b/tests/unit/filesize-helpers.test.js @@ -3,132 +3,133 @@ * Tests the individual helper functions for better test coverage */ -import assert from 'assert'; -import { +import assert from "node:assert"; +import { describe, it } from "node:test"; +import { getBaseConfiguration, handleZeroValue, calculateOptimizedValue, applyPrecisionHandling, - applyNumberFormatting -} from '../../src/helpers.js'; + applyNumberFormatting, +} from "../../src/helpers.js"; -describe('Helper Functions', () => { - describe('getBaseConfiguration', () => { - it('should return SI configuration', () => { - const config = getBaseConfiguration('si', -1); +describe("Helper Functions", () => { + describe("getBaseConfiguration", () => { + it("should return SI configuration", () => { + const config = getBaseConfiguration("si", -1); assert.deepStrictEqual(config, { isDecimal: true, ceil: 1000, - actualStandard: 'jedec' + actualStandard: "jedec", }); }); - it('should return IEC configuration', () => { - const config = getBaseConfiguration('iec', -1); + it("should return IEC configuration", () => { + const config = getBaseConfiguration("iec", -1); assert.deepStrictEqual(config, { isDecimal: false, ceil: 1024, - actualStandard: 'iec' + actualStandard: "iec", }); }); - it('should return JEDEC configuration', () => { - const config = getBaseConfiguration('jedec', -1); + it("should return JEDEC configuration", () => { + const config = getBaseConfiguration("jedec", -1); assert.deepStrictEqual(config, { isDecimal: false, ceil: 1024, - actualStandard: 'jedec' + actualStandard: "jedec", }); }); - it('should handle base=2 override', () => { - const config = getBaseConfiguration('', 2); + it("should handle base=2 override", () => { + const config = getBaseConfiguration("", 2); assert.deepStrictEqual(config, { isDecimal: false, ceil: 1024, - actualStandard: 'iec' + actualStandard: "iec", }); }); - it('should return default configuration', () => { - const config = getBaseConfiguration('', -1); + it("should return default configuration", () => { + const config = getBaseConfiguration("", -1); assert.deepStrictEqual(config, { isDecimal: true, ceil: 1000, - actualStandard: 'jedec' + actualStandard: "jedec", }); }); }); - describe('handleZeroValue', () => { - it('should handle zero with string output', () => { - const result = handleZeroValue(0, 'jedec', false, {}, false, [], 'string', ' '); - assert.strictEqual(result, '0 B'); + describe("handleZeroValue", () => { + it("should handle zero with string output", () => { + const result = handleZeroValue(0, "jedec", false, {}, false, [], "string", " "); + assert.strictEqual(result, "0 B"); }); - it('should handle zero with array output', () => { - const result = handleZeroValue(0, 'jedec', false, {}, false, [], 'array', ' '); - assert.deepStrictEqual(result, [0, 'B']); + it("should handle zero with array output", () => { + const result = handleZeroValue(0, "jedec", false, {}, false, [], "array", " "); + assert.deepStrictEqual(result, [0, "B"]); }); - it('should handle zero with object output', () => { - const result = handleZeroValue(0, 'jedec', false, {}, false, [], 'object', ' '); + it("should handle zero with object output", () => { + const result = handleZeroValue(0, "jedec", false, {}, false, [], "object", " "); assert.deepStrictEqual(result, { value: 0, - symbol: 'B', + symbol: "B", exponent: 0, - unit: 'B' + unit: "B", }); }); - it('should handle zero with exponent output', () => { - const result = handleZeroValue(0, 'jedec', false, {}, false, [], 'exponent', ' '); + it("should handle zero with exponent output", () => { + const result = handleZeroValue(0, "jedec", false, {}, false, [], "exponent", " "); assert.strictEqual(result, 0); }); - it('should handle zero with bits', () => { - const result = handleZeroValue(0, 'jedec', true, {}, false, [], 'string', ' '); - assert.strictEqual(result, '0 bit'); + it("should handle zero with bits", () => { + const result = handleZeroValue(0, "jedec", true, {}, false, [], "string", " "); + assert.strictEqual(result, "0 bit"); }); - it('should handle zero with custom symbols', () => { - const result = handleZeroValue(0, 'jedec', false, { 'B': 'Bytes' }, false, [], 'string', ' '); - assert.strictEqual(result, '0 Bytes'); + it("should handle zero with custom symbols", () => { + const result = handleZeroValue(0, "jedec", false, { B: "Bytes" }, false, [], "string", " "); + assert.strictEqual(result, "0 Bytes"); }); - it('should handle zero with fullform', () => { - const result = handleZeroValue(0, 'jedec', false, {}, true, [], 'string', ' '); - assert.strictEqual(result, '0 byte'); + it("should handle zero with fullform", () => { + const result = handleZeroValue(0, "jedec", false, {}, true, [], "string", " "); + assert.strictEqual(result, "0 byte"); }); - it('should handle zero with precision', () => { - const result = handleZeroValue(2, 'jedec', false, {}, false, [], 'string', ' '); - assert.strictEqual(result, '0.0 B'); + it("should handle zero with precision", () => { + const result = handleZeroValue(2, "jedec", false, {}, false, [], "string", " "); + assert.strictEqual(result, "0.0 B"); }); }); - describe('calculateOptimizedValue', () => { - it('should calculate value without bits', () => { + describe("calculateOptimizedValue", () => { + it("should calculate value without bits", () => { const result = calculateOptimizedValue(1024, 1, false, false, 1024); assert.deepStrictEqual(result, { result: 1, e: 1 }); }); - it('should calculate value with bits', () => { + it("should calculate value with bits", () => { const result = calculateOptimizedValue(1024, 1, false, true, 1024); assert.deepStrictEqual(result, { result: 8, e: 1 }); }); - it('should handle bits auto-increment', () => { + it("should handle bits auto-increment", () => { const result = calculateOptimizedValue(128, 0, false, true, 1024); assert.deepStrictEqual(result, { result: 1, e: 1 }); }); - it('should use decimal powers', () => { + it("should use decimal powers", () => { const result = calculateOptimizedValue(1000, 1, true, false, 1000); assert.deepStrictEqual(result, { result: 1, e: 1 }); }); - it('should not increment when e >= 8', () => { + it("should not increment when e >= 8", () => { // Use a proper YiB value for exponent 8 const yibValue = Math.pow(1024, 8); // 1 YiB in bytes const resultObj = calculateOptimizedValue(yibValue, 8, false, true, 1024); @@ -137,63 +138,109 @@ describe('Helper Functions', () => { }); }); - describe('applyPrecisionHandling', () => { - it('should apply precision without scientific notation', () => { - const result = applyPrecisionHandling(1.5, 2, 1, 1024, false, false, 1024, Math.round, 2); - assert.deepStrictEqual(result, { value: '1.5', e: 1 }); + describe("applyPrecisionHandling", () => { + it("should apply precision without scientific notation", () => { + const result = applyPrecisionHandling(1.5, 2, 1, 1024, false, false, 1024, Math.round, 2, -1); + assert.deepStrictEqual(result, { value: "1.5", e: 1 }); }); - it('should handle scientific notation with increment', () => { + it("should handle scientific notation with increment when exponent is -1", () => { // Test with a large number that would produce scientific notation - const result = applyPrecisionHandling(1000000000000, 1, 3, 1e15, true, false, 1000, Math.round, 2); - assert(typeof result.value === 'string'); - assert(typeof result.e === 'number'); - }); - - it('should not increment when e >= 8', () => { - const result = applyPrecisionHandling(1.5, 2, 8, 1024, false, false, 1024, Math.round, 2); - assert.deepStrictEqual(result, { value: '1.5', e: 8 }); + const result = applyPrecisionHandling( + 1000000000000, + 1, + 3, + 1e15, + true, + false, + 1000, + Math.round, + 2, + -1, + ); + assert(typeof result.value === "string"); + assert(typeof result.e === "number"); + }); + + it("should handle scientific notation with increment when exponent is NaN", () => { + // Test that NaN also triggers auto-increment like -1 + const result = applyPrecisionHandling( + 1000000000000, + 1, + 3, + 1e15, + true, + false, + 1000, + Math.round, + 2, + NaN, + ); + assert(typeof result.value === "string"); + assert(typeof result.e === "number"); + }); + + it("should not increment when exponent is forced", () => { + // Test that forced exponent prevents auto-increment + const result = applyPrecisionHandling( + 1000000000000, + 1, + 3, + 1e15, + true, + false, + 1000, + Math.round, + 2, + 5, + ); + assert.deepStrictEqual(result, { value: "1e+12", e: 3 }); + }); + + it("should not increment when e >= 8", () => { + const result = applyPrecisionHandling(1.5, 2, 8, 1024, false, false, 1024, Math.round, 2, -1); + assert.deepStrictEqual(result, { value: "1.5", e: 8 }); }); }); - describe('applyNumberFormatting', () => { - it('should format with system locale', () => { - const result = applyNumberFormatting(1.5, true, {}, '', false, 2); - assert(typeof result === 'string'); + describe("applyNumberFormatting", () => { + it("should format with system locale", () => { + const result = applyNumberFormatting(1.5, true, {}, "", false, 2); + assert(typeof result === "string"); }); - it('should format with specific locale', () => { - const result = applyNumberFormatting(1.5, 'en-US', {}, '', false, 2); - assert(typeof result === 'string'); + it("should format with specific locale", () => { + const result = applyNumberFormatting(1.5, "en-US", {}, "", false, 2); + assert(typeof result === "string"); }); - it('should apply custom separator', () => { - const result = applyNumberFormatting(1.5, '', {}, ',', false, 2); - assert.strictEqual(result, '1,5'); + it("should apply custom separator", () => { + const result = applyNumberFormatting(1.5, "", {}, ",", false, 2); + assert.strictEqual(result, "1,5"); }); - it('should apply padding', () => { - const result = applyNumberFormatting(1, '', {}, '', true, 2); - assert.strictEqual(result, '1.00'); + it("should apply padding", () => { + const result = applyNumberFormatting(1, "", {}, "", true, 2); + assert.strictEqual(result, "1.00"); }); - it('should apply padding with custom separator', () => { - const result = applyNumberFormatting(1.5, '', {}, ',', true, 3); - assert.strictEqual(result, '1,500'); + it("should apply padding with custom separator", () => { + const result = applyNumberFormatting(1.5, "", {}, ",", true, 3); + assert.strictEqual(result, "1,500"); }); - it('should handle no formatting', () => { - const result = applyNumberFormatting(1.5, '', {}, '', false, 2); + it("should handle no formatting", () => { + const result = applyNumberFormatting(1.5, "", {}, "", false, 2); assert.strictEqual(result, 1.5); }); - it('should handle padding with existing decimals', () => { - const result = applyNumberFormatting(1.5, '', {}, '', true, 3); - assert.strictEqual(result, '1.500'); + it("should handle padding with existing decimals", () => { + const result = applyNumberFormatting(1.5, "", {}, "", true, 3); + assert.strictEqual(result, "1.500"); }); - it('should handle padding with no round', () => { - const result = applyNumberFormatting(1.5, '', {}, '', true, 0); + it("should handle padding with no round", () => { + const result = applyNumberFormatting(1.5, "", {}, "", true, 0); assert.strictEqual(result, 1.5); }); }); diff --git a/tests/unit/filesize.test.js b/tests/unit/filesize.test.js index e518e55..6c6e44c 100644 --- a/tests/unit/filesize.test.js +++ b/tests/unit/filesize.test.js @@ -3,830 +3,913 @@ * Tests the main filesize function and partial function */ -import assert from 'assert'; -import { filesize, partial } from '../../src/filesize.js'; - -describe('filesize', () => { - describe('Basic functionality', () => { - it('should convert bytes to human readable format', () => { - assert.strictEqual(filesize(1000), '1 kB'); - assert.strictEqual(filesize(1000000), '1 MB'); - assert.strictEqual(filesize(1000000000), '1 GB'); - }); - - it('should handle zero bytes', () => { - assert.strictEqual(filesize(0), '0 B'); - }); - - it('should handle small numbers', () => { - assert.strictEqual(filesize(1), '1 B'); - assert.strictEqual(filesize(512), '512 B'); - }); - - it('should handle negative numbers', () => { - assert.strictEqual(filesize(-1000), '-1 kB'); - assert.strictEqual(filesize(-1000000), '-1 MB'); - }); - }); - - describe('Bits option', () => { - it('should convert to bits when bits option is true', () => { - assert.strictEqual(filesize(1000, { bits: true }), '8 kbit'); - assert.strictEqual(filesize(1000000, { bits: true }), '8 Mbit'); - }); - - it('should handle zero with bits option', () => { - assert.strictEqual(filesize(0, { bits: true }), '0 bit'); - }); - }); - - describe('Zero value edge cases', () => { - it('should handle zero with exponent output', () => { - assert.strictEqual(filesize(0, { output: 'exponent' }), 0); - }); - - it('should handle zero with custom symbols', () => { - assert.strictEqual(filesize(0, { symbols: { 'B': 'Bytes' } }), '0 Bytes'); - }); - - it('should handle zero with fullform', () => { - assert.strictEqual(filesize(0, { fullform: true }), '0 byte'); - }); - - it('should handle zero with fullform and bits', () => { - assert.strictEqual(filesize(0, { fullform: true, bits: true }), '0 bit'); - }); - - it('should handle zero with array output', () => { - const result = filesize(0, { output: 'array' }); - assert.deepStrictEqual(result, [0, 'B']); - }); - - it('should handle zero with object output', () => { - const result = filesize(0, { output: 'object' }); - assert.deepStrictEqual(result, { value: 0, symbol: 'B', exponent: 0, unit: 'B' }); - }); - }); - - describe('Standards', () => { - it('should use IEC standard', () => { - assert.strictEqual(filesize(1024, { standard: 'iec' }), '1 KiB'); - assert.strictEqual(filesize(1048576, { standard: 'iec' }), '1 MiB'); - }); - - it('should use JEDEC standard', () => { - assert.strictEqual(filesize(1024, { standard: 'jedec' }), '1 KB'); - assert.strictEqual(filesize(1048576, { standard: 'jedec' }), '1 MB'); - }); - - it('should use SI standard', () => { - assert.strictEqual(filesize(1000, { standard: 'si' }), '1 kB'); - assert.strictEqual(filesize(1000000, { standard: 'si' }), '1 MB'); - }); - }); - - describe('Output formats', () => { - it('should return string by default', () => { - const result = filesize(1000); - assert.strictEqual(typeof result, 'string'); - assert.strictEqual(result, '1 kB'); - }); - - it('should return array when output is "array"', () => { - const result = filesize(1000, { output: 'array' }); - assert(Array.isArray(result)); - assert.strictEqual(result[0], 1); - assert.strictEqual(result[1], 'kB'); - }); - - it('should return object when output is "object"', () => { - const result = filesize(1000, { output: 'object' }); - assert.strictEqual(typeof result, 'object'); - assert.strictEqual(result.value, 1); - assert.strictEqual(result.symbol, 'kB'); - assert.strictEqual(result.exponent, 1); - assert.strictEqual(result.unit, 'kB'); - }); - - it('should return exponent when output is "exponent"', () => { - const result = filesize(1000, { output: 'exponent' }); - assert.strictEqual(typeof result, 'number'); - assert.strictEqual(result, 1); - }); - }); - - describe('Rounding', () => { - it('should round to specified decimal places', () => { - assert.strictEqual(filesize(1536, { round: 1 }), '1.5 kB'); - assert.strictEqual(filesize(1536, { round: 0 }), '2 kB'); - assert.strictEqual(filesize(1536, { round: 3 }), '1.536 kB'); - }); - - it('should use different rounding methods', () => { - assert.strictEqual(filesize(1536, { roundingMethod: 'floor' }), '1.53 kB'); - assert.strictEqual(filesize(1536, { roundingMethod: 'ceil' }), '1.54 kB'); - assert.strictEqual(filesize(1536, { roundingMethod: 'round' }), '1.54 kB'); - }); - }); - - describe('Padding', () => { - it('should pad decimal places when pad is true', () => { - assert.strictEqual(filesize(1000, { pad: true, round: 2 }), '1.00 kB'); - assert.strictEqual(filesize(1536, { pad: true, round: 1 }), '1.5 kB'); - }); - }); - - describe('Custom separators and spacers', () => { - it('should use custom decimal separator', () => { - assert.strictEqual(filesize(1536, { separator: ',' }), '1,54 kB'); - }); - - it('should use custom spacer between value and unit', () => { - assert.strictEqual(filesize(1000, { spacer: '_' }), '1_kB'); - assert.strictEqual(filesize(1000, { spacer: '' }), '1kB'); - }); - }); - - describe('Custom symbols', () => { - it('should use custom symbols when provided', () => { - const customSymbols = { kB: 'kilobyte' }; - assert.strictEqual(filesize(1000, { symbols: customSymbols }), '1 kilobyte'); - }); - }); - - describe('Locale formatting', () => { - it('should format using system locale when locale is true', () => { - const result = filesize(1000, { locale: true }); - assert(typeof result === 'string'); - assert(result.includes('kB')); - }); - - it('should format using specific locale', () => { - const result = filesize(1536, { locale: 'en-US' }); - assert(typeof result === 'string'); - assert(result.includes('1.5')); - }); - }); - - describe('Full form names', () => { - it('should use full form names when fullform is true', () => { - assert.strictEqual(filesize(1000, { fullform: true }), '1 kilobyte'); - assert.strictEqual(filesize(2000, { fullform: true }), '2 kilobytes'); - assert.strictEqual(filesize(1, { fullform: true }), '1 byte'); - }); - - it('should use custom fullforms when provided', () => { - const customFullforms = ['', 'thousand-byte']; - assert.strictEqual(filesize(1000, { fullform: true, fullforms: customFullforms }), '1 thousand-byte'); - }); - }); - - describe('Base and exponent options', () => { - it('should use base 2 calculations', () => { - assert.strictEqual(filesize(1024, { base: 2 }), '1 KiB'); - }); - - it('should use base 10 calculations', () => { - assert.strictEqual(filesize(1000, { base: 10 }), '1 kB'); - }); - - it('should use forced exponent', () => { - assert.strictEqual(filesize(1024, { exponent: 0 }), '1024 B'); - assert.strictEqual(filesize(1024, { exponent: 2 }), '0 MB'); - }); - }); - - describe('Precision option', () => { - it('should use specified precision', () => { - assert.strictEqual(filesize(1536, { precision: 3 }), '1.54 kB'); - assert.strictEqual(filesize(1536000, { precision: 2 }), '1.5 MB'); - }); - - it('should remove scientific notation from precision results', () => { - // Test cases where toPrecision would normally produce scientific notation - // but our implementation removes it by splitting on "e" - - // Very large numbers that would produce scientific notation - assert.strictEqual(filesize(1234567890123, { precision: 2 }), '1.2 TB'); - assert.strictEqual(filesize(9876543210987, { precision: 3 }), '9.88 TB'); - - // Test with different precision values on large numbers - assert.strictEqual(filesize(1e15, { precision: 1 }), '1 PB'); - assert.strictEqual(filesize(1e15, { precision: 2 }), '1.0 PB'); - assert.strictEqual(filesize(1e15, { precision: 3 }), '1.00 PB'); - }); - - it('should handle precision with very small numbers', () => { - // Very small numbers (less than 1 byte) - assert.strictEqual(filesize(0.001, { precision: 1 }), '0 B'); - assert.strictEqual(filesize(0.1, { precision: 2 }), '0.0 B'); - assert.strictEqual(filesize(0.5, { precision: 1 }), '1 B'); - }); - - it('should handle precision with numbers that would produce e+ notation', () => { - // Numbers that produce e+ notation with toPrecision - const largeNum = 1234567890; - assert.strictEqual(filesize(largeNum, { precision: 2 }), '1.2 GB'); - assert.strictEqual(filesize(largeNum, { precision: 3 }), '1.23 GB'); - assert.strictEqual(filesize(largeNum, { precision: 1 }), '1 GB'); - }); - - it('should handle precision with numbers that would produce e- notation', () => { - // Very small decimal numbers that would produce e- notation - assert.strictEqual(filesize(0.0001, { precision: 1 }), '0 B'); - assert.strictEqual(filesize(0.00001, { precision: 2 }), '0.0 B'); - }); - - it('should work with precision and different standards', () => { - const largeNum = 1234567890; - assert.strictEqual(filesize(largeNum, { precision: 2, standard: 'iec' }), '1.1 GiB'); - assert.strictEqual(filesize(largeNum, { precision: 2, standard: 'jedec' }), '1.1 GB'); - assert.strictEqual(filesize(largeNum, { precision: 2, standard: 'si' }), '1.2 GB'); - }); - - it('should work with precision and bits option', () => { - const largeNum = 1234567890; - assert.strictEqual(filesize(largeNum, { precision: 2, bits: true }), '9.9 Gbit'); - assert.strictEqual(filesize(largeNum, { precision: 3, bits: true }), '9.88 Gbit'); - }); - - it('should work with precision and array output', () => { - const result = filesize(1234567890, { precision: 2, output: 'array' }); - assert(Array.isArray(result)); - assert.strictEqual(result[0], '1.2'); - assert.strictEqual(result[1], 'GB'); - }); - - it('should work with precision and object output', () => { - const result = filesize(1234567890, { precision: 2, output: 'object' }); - assert.strictEqual(typeof result, 'object'); - assert.strictEqual(result.value, '1.2'); - assert.strictEqual(result.symbol, 'GB'); - }); - - it('should handle precision with extremely large numbers', () => { - // Test with numbers that exceed normal exponent range - const extremeNumber = Math.pow(1024, 15); - const result = filesize(extremeNumber, { precision: 2 }); - assert(typeof result === 'string'); - // Note: For extremely large numbers that exceed JavaScript's normal number range, - // the number itself may be in scientific notation, which is expected behavior - assert(result.includes('YB') || result.includes('YiB')); - }); - - it('should handle precision edge cases', () => { - // Test precision with zero - assert.strictEqual(filesize(0, { precision: 3 }), '0.00 B'); - - // Test precision with exactly 1 byte - assert.strictEqual(filesize(1, { precision: 2 }), '1.0 B'); - - // Test precision with negative numbers - assert.strictEqual(filesize(-1234567890, { precision: 2 }), '-1.2 GB'); - }); - - it('should ensure no scientific notation in any precision result', () => { - // Test a range of numbers that would normally produce scientific notation from toPrecision - // but should have it removed by our implementation - const testNumbers = [ - 1e3, 1e6, 1e9, 1e12, - 1234567890123456, - Math.pow(10, 10) - ]; - - testNumbers.forEach(num => { - [1, 2, 3, 4].forEach(precision => { - const result = filesize(num, { precision }); - // Result should not contain scientific notation markers when using toPrecision - // Note: We exclude extremely large numbers that naturally contain 'e' in JavaScript - if (!num.toString().includes('e')) { - assert(!result.includes('e'), `Result "${result}" contains 'e' for number ${num} with precision ${precision}`); - assert(!result.includes('E'), `Result "${result}" contains 'E' for number ${num} with precision ${precision}`); - } - }); - }); - }); - - it('should handle precision with base 2 when scientific notation is produced (regular number)', () => { - // Test case that triggers base === 2 path in line 168 precision handling - // We need a number that will produce scientific notation with toPrecision - // and uses base 2 (IEC standard) - const testNumber = 999999999999999; // Large number that triggers scientific notation - const result = filesize(testNumber, { precision: 1, standard: 'iec' }); - assert(typeof result === 'string'); - assert(!result.includes('e')); - assert(!result.includes('E')); - // Should use IEC units (base 2) - assert(result.includes('TiB') || result.includes('PiB')); - }); - - it('should handle precision with base 2 when scientific notation is produced (BigInt)', () => { - // Test case that triggers base === 2 path in line 168 precision handling using BigInt - // This ensures both number types are tested for base 2 path - const testNumber = BigInt('999999999999999'); // Large BigInt that triggers scientific notation - const result = filesize(testNumber, { precision: 1, standard: 'iec' }); - assert(typeof result === 'string'); - assert(!result.includes('e')); - assert(!result.includes('E')); - // Should use IEC units (base 2) - assert(result.includes('TiB') || result.includes('PiB')); - }); - - it('should handle precision with base 10 when scientific notation is produced (regular number)', () => { - // Test case that triggers base === 10 path in line 168 precision handling - // We need a number that will produce scientific notation with toPrecision - // and uses base 10 (SI/JEDEC standard) - const testNumber = 999999999999999; // Large number that triggers scientific notation - const result = filesize(testNumber, { precision: 1, standard: 'jedec' }); - assert(typeof result === 'string'); - assert(!result.includes('e')); - assert(!result.includes('E')); - // Should use JEDEC units (base 10 logic) - assert(result.includes('TB') || result.includes('PB')); - }); - - it('should handle precision with base 10 when scientific notation is produced (BigInt)', () => { - // Test case that triggers base === 10 path in line 168 precision handling using BigInt - // This ensures both number types are tested for base 10 path - const testNumber = BigInt('999999999999999'); // Large BigInt that triggers scientific notation - const result = filesize(testNumber, { precision: 1, standard: 'jedec' }); - assert(typeof result === 'string'); - assert(!result.includes('e')); - assert(!result.includes('E')); - // Should use JEDEC units (base 10 logic) - assert(result.includes('TB') || result.includes('PB')); - }); - - it('should handle precision with YiB-level numbers where e >= 8 (regular number)', () => { - // Test case that covers line 166 when e >= 8 (YiB level) using regular number - // This triggers result[0].includes(E) but e < 8 is false, so increment logic is skipped - const yibNumber = Math.pow(1024, 8); // 1 YiB in bytes using regular number - const result = filesize(yibNumber, { precision: 1, standard: 'iec' }); - assert(typeof result === 'string'); - // For extremely large regular numbers, some scientific notation may be expected - assert(result.includes('YiB')); - }); - - it('should handle precision with YiB-level numbers where e >= 8 (BigInt)', () => { - // Test case that covers line 166 when e >= 8 (YiB level) using BigInt - // This triggers result[0].includes(E) but e < 8 is false, so increment logic is skipped - // YiB is at exponent 8, so we need a number large enough to reach this level - const yibNumber = BigInt(1024) ** BigInt(8); // 1 YiB in bytes using BigInt - const result = filesize(yibNumber, { precision: 1, standard: 'iec' }); - assert(typeof result === 'string'); - assert(!result.includes('e')); - assert(!result.includes('E')); - assert(result.includes('YiB')); - }); - - it('should handle precision with extremely large numbers that exceed YiB (regular number)', () => { - // Test case for numbers that are larger than YiB using regular number - // This ensures we test the e >= 8 condition thoroughly with regular numbers - const extremeNumber = Math.pow(1024, 8) * 1000; // 1000 YiB using regular number - const result = filesize(extremeNumber, { precision: 2, standard: 'iec' }); - assert(typeof result === 'string'); - // For extremely large regular numbers, some scientific notation may be expected - assert(result.includes('YiB')); - }); - - it('should handle precision with extremely large numbers that exceed YiB (BigInt)', () => { - // Test case for numbers that are larger than YiB using BigInt - // This ensures we test the e >= 8 condition thoroughly with BigInt - const extremeNumber = BigInt(1024) ** BigInt(8) * BigInt(1000); // 1000 YiB using BigInt - const result = filesize(extremeNumber, { precision: 2, standard: 'iec' }); - assert(typeof result === 'string'); - // For extremely large BigInt numbers, the result should still use YiB units - // Some scientific notation in the final result may be acceptable for such large numbers - assert(result.includes('YiB')); - }); - - it('should handle precision with specific test case for GiB values', () => { - // Test specific precision case: filesize(1058223158, {base: 2, standard: 'iec', precision: 3}) - // This should output '0.990 GiB' - assert.strictEqual(filesize(1058223158, {base: 2, standard: 'iec', precision: 3}), '0.990 GiB'); - }); - }); - - describe('Edge cases', () => { - it('should handle very large numbers', () => { - const largeNumber = Math.pow(1024, 8); - const result = filesize(largeNumber); - assert(typeof result === 'string'); - assert(result.includes('YB') || result.includes('YiB')); - }); - - it('should handle BigInt input', () => { - const result = filesize(BigInt(1024), { standard: 'jedec' }); - assert.strictEqual(result, '1 KB'); - }); - - it('should handle numbers exceeding maximum exponent', () => { - const hugeNumber = Math.pow(1024, 10); - const result = filesize(hugeNumber); - assert(typeof result === 'string'); - }); - - it('should handle numbers exceeding lookup table size (decimal)', () => { - // Test number requiring Math.pow fallback for decimal powers - const hugeNumber = Math.pow(1000, 15); // Exceeds DECIMAL_POWERS array - const result = filesize(hugeNumber, { base: 10 }); - assert.ok(result.includes('YB')); - }); - - it('should handle numbers exceeding lookup table size (binary)', () => { - // Test number requiring Math.pow fallback for binary powers - const hugeNumber = Math.pow(2, 100); // Exceeds BINARY_POWERS array - const result = filesize(hugeNumber, { base: 2 }); - assert.ok(result.includes('YiB')); - }); - - it('should handle precision with numbers exceeding lookup table', () => { - const hugeNumber = Math.pow(1000, 12); - const result = filesize(hugeNumber, { precision: 2, base: 10 }); - assert.ok(result.includes('YB')); - }); - - it('should handle precision with round=0 and scientific notation', () => { - // Test case where p === 1 in the precision recalculation branch - const result = filesize(1000000000000000000, { precision: 1, round: 0 }); - assert.ok(typeof result === 'string'); - }); - - it('should cover p===1 branch in precision recalculation', () => { - // Try to force the p===1 branch in the precision recalculation - // This needs a very specific edge case where scientific notation is produced - // and after increment e=0 or round=0 makes p=1 - const result = filesize(0.0001, { precision: 20, round: 0 }); - assert.ok(typeof result === 'string'); - }); - - it('should trigger precision recalculation with bytes and round=0', () => { - // Another attempt: very small number that might produce scientific notation in bytes - const result = filesize(0.000000000000001, { precision: 30, round: 0 }); - assert.ok(typeof result === 'string'); - }); - - it('should cover p===1 branch with input=1 and extreme precision', () => { - // When input=1, e=0, so p=1. High precision might trigger scientific notation - // and the recalculation with p===1 branch - const result = filesize(1, { precision: 50 }); - assert.ok(typeof result === 'string'); - }); - - it('should attempt to trigger the elusive p===1 branch in recalculation', () => { - // Numbers that would produce scientific notation with precision=1 - // When e=0 (bytes), p=1, and toPrecision(1) might produce "1e+1" etc. - const result = filesize(10, { precision: 1, round: 0 }); - assert.ok(typeof result === 'string'); - }); - - it('should handle extremely large numbers with precision adjustment', () => { - const extremeNumber = Math.pow(1024, 15); // Much larger than supported exponent - const result = filesize(extremeNumber, { precision: 3 }); - assert(typeof result === 'string'); - assert(result.includes('YB') || result.includes('YiB')); - }); - - it('should handle bit conversion auto-increment near boundary', () => { - // Test edge case where bits conversion causes auto-increment to next unit - const result = filesize(127.5, { bits: true }); // Should auto-increment - assert(typeof result === 'string'); - }); - - it('should handle result equal to ceil boundary for auto-increment', () => { - // Test case where result equals ceil and triggers increment - const result = filesize(999.5, { round: 0 }); // Should round to 1000 and increment - assert(typeof result === 'string'); - }); - - it('should handle padding with custom separator edge cases', () => { - // Test padding when separator is found via regex - const result = filesize(1536, { pad: true, round: 2, locale: 'de-DE' }); - assert(typeof result === 'string'); - }); - - it('should handle NaN exponent edge case', () => { - const result = filesize(1000, { exponent: NaN }); - assert.strictEqual(result, '1 kB'); - }); - }); - - describe('Error handling', () => { - it('should throw TypeError for invalid number input', () => { - assert.throws(() => filesize('invalid'), TypeError); - assert.throws(() => filesize(NaN), TypeError); - assert.throws(() => filesize({}), TypeError); - }); - - it('should throw TypeError for invalid rounding method', () => { - assert.throws(() => filesize(1024, { roundingMethod: 'invalid' }), TypeError); - assert.throws(() => filesize(1024, { roundingMethod: 'nonexistent' }), TypeError); - }); - }); - - describe('Input type handling', () => { - describe('Number input', () => { - describe('filesize() with number input', () => { - it('should handle integer numbers', () => { - assert.strictEqual(filesize(1024), '1.02 kB'); - assert.strictEqual(filesize(0), '0 B'); - assert.strictEqual(filesize(-1024), '-1.02 kB'); - }); - - it('should handle floating point numbers', () => { - assert.strictEqual(filesize(1536.5), '1.54 kB'); - assert.strictEqual(filesize(0.5), '1 B'); - }); - - it('should handle very large numbers', () => { - const largeNumber = Number.MAX_SAFE_INTEGER; - const result = filesize(largeNumber); - assert(typeof result === 'string'); - }); - }); - - describe('partial() with number input', () => { - it('should handle integer numbers', () => { - const formatIEC = partial({ standard: 'iec' }); - assert.strictEqual(formatIEC(1024), '1 KiB'); - assert.strictEqual(formatIEC(0), '0 B'); - assert.strictEqual(formatIEC(-1024), '-1 KiB'); - }); - - it('should handle floating point numbers', () => { - const formatRounded = partial({ round: 1 }); - assert.strictEqual(formatRounded(1536.7), '1.5 kB'); - assert.strictEqual(formatRounded(0.5), '1 B'); - }); - - it('should handle very large numbers', () => { - const formatBits = partial({ bits: true }); - const largeNumber = Number.MAX_SAFE_INTEGER; - const result = formatBits(largeNumber); - assert(typeof result === 'string'); - }); - }); - }); - - describe('String input', () => { - describe('filesize() with string input', () => { - it('should handle valid integer string', () => { - assert.strictEqual(filesize('1024'), '1.02 kB'); - assert.strictEqual(filesize('0'), '0 B'); - assert.strictEqual(filesize('-1024'), '-1.02 kB'); - }); - - it('should handle valid float string', () => { - assert.strictEqual(filesize('1536.5'), '1.54 kB'); - assert.strictEqual(filesize('0.5'), '1 B'); - }); - - it('should handle scientific notation string', () => { - assert.strictEqual(filesize('1e3'), '1 kB'); - assert.strictEqual(filesize('1.024e3'), '1.02 kB'); - }); - - it('should handle string with leading/trailing whitespace', () => { - assert.strictEqual(filesize(' 1024 '), '1.02 kB'); - assert.strictEqual(filesize('\t1024\n'), '1.02 kB'); - }); - - it('should work with string input and different options', () => { - assert.strictEqual(filesize('1024', { standard: 'iec' }), '1 KiB'); - assert.strictEqual(filesize('1000', { bits: true }), '8 kbit'); - assert.strictEqual(filesize('1024', { output: 'array' })[0], 1.02); - }); - }); - - describe('partial() with string input', () => { - it('should handle valid integer string', () => { - const formatJEDEC = partial({ standard: 'jedec' }); - assert.strictEqual(formatJEDEC('1024'), '1 KB'); - assert.strictEqual(formatJEDEC('0'), '0 B'); - assert.strictEqual(formatJEDEC('-1024'), '-1 KB'); - }); - - it('should handle valid float string', () => { - const formatPadded = partial({ pad: true, round: 2 }); - assert.strictEqual(formatPadded('1536.5'), '1.54 kB'); - assert.strictEqual(formatPadded('0.5'), '1.00 B'); - }); - - it('should handle scientific notation string', () => { - const formatBits = partial({ bits: true }); - assert.strictEqual(formatBits('1e3'), '8 kbit'); - assert.strictEqual(formatBits('1.024e3'), '8.19 kbit'); - }); - - it('should handle string with leading/trailing whitespace', () => { - const formatFullForm = partial({ fullform: true, standard: 'iec' }); - assert.strictEqual(formatFullForm(' 1024 '), '1 kibibyte'); - assert.strictEqual(formatFullForm('\t2048\n'), '2 kibibytes'); - }); - - it('should work with string input and complex options', () => { - const formatComplex = partial({ - standard: 'si', - round: 1, - spacer: '_', - output: 'array' - }); - const result = formatComplex('1000'); - assert(Array.isArray(result)); - assert.strictEqual(result[0], 1); - assert.strictEqual(result[1], 'kB'); - }); - }); - }); - - describe('BigInt input', () => { - describe('filesize() with BigInt input', () => { - it('should handle regular BigInt values', () => { - assert.strictEqual(filesize(BigInt(1024)), '1.02 kB'); - assert.strictEqual(filesize(BigInt(0)), '0 B'); - assert.strictEqual(filesize(BigInt(-1024)), '-1.02 kB'); - }); - - it('should handle large BigInt values', () => { - const largeBigInt = BigInt('9007199254740992'); // 2^53 - const result = filesize(largeBigInt); - assert(typeof result === 'string'); - assert(result.includes('PB') || result.includes('PiB')); - }); - - it('should handle very large BigInt values', () => { - const veryLargeBigInt = BigInt('1208925819614629174706176'); // 1024^8 - const result = filesize(veryLargeBigInt); - assert(typeof result === 'string'); - assert(result.includes('YB') || result.includes('YiB')); - }); - - it('should work with BigInt input and different options', () => { - assert.strictEqual(filesize(BigInt(1024), { standard: 'jedec' }), '1 KB'); - assert.strictEqual(filesize(BigInt(1000), { bits: true }), '8 kbit'); - - const objectResult = filesize(BigInt(1024), { output: 'object' }); - assert.strictEqual(objectResult.value, 1.02); - assert.strictEqual(objectResult.symbol, 'kB'); - }); - }); - - describe('partial() with BigInt input', () => { - it('should handle regular BigInt values', () => { - const formatCustomSymbols = partial({ symbols: { KiB: 'kibibyte' }, standard: 'iec' }); - assert.strictEqual(formatCustomSymbols(BigInt(1024)), '1 kibibyte'); - assert.strictEqual(formatCustomSymbols(BigInt(0)), '0 B'); - assert.strictEqual(formatCustomSymbols(BigInt(-1024)), '-1 kibibyte'); - }); - - it('should handle large BigInt values', () => { - const formatPrecision = partial({ precision: 3 }); - const largeBigInt = BigInt('9007199254740992'); // 2^53 - const result = formatPrecision(largeBigInt); - assert(typeof result === 'string'); - assert(result.includes('PB') || result.includes('PiB')); - }); - - it('should handle very large BigInt values', () => { - const formatExponent = partial({ exponent: -1 }); // Auto-select exponent - const veryLargeBigInt = BigInt('1208925819614629174706176'); // 1024^8 - const result = formatExponent(veryLargeBigInt); - assert(typeof result === 'string'); - assert(result.includes('YB') || result.includes('YiB')); - }); - - it('should work with BigInt input and different output formats', () => { - const formatObject = partial({ output: 'object', standard: 'iec' }); - const objectResult = formatObject(BigInt(1024)); - assert.strictEqual(typeof objectResult, 'object'); - assert.strictEqual(objectResult.value, 1); - assert.strictEqual(objectResult.symbol, 'KiB'); - - const formatExponentOnly = partial({ output: 'exponent' }); - const exponentResult = formatExponentOnly(BigInt(1024)); - assert.strictEqual(typeof exponentResult, 'number'); - assert.strictEqual(exponentResult, 1); - }); - - it('should work with BigInt input and bits option', () => { - const formatBits = partial({ bits: true, round: 1 }); - assert.strictEqual(formatBits(BigInt(1024)), '8.2 kbit'); - assert.strictEqual(formatBits(BigInt(1000)), '8 kbit'); - }); - - it('should work with BigInt input and locale formatting', () => { - const formatLocale = partial({ locale: 'en-US', round: 2 }); - const result = formatLocale(BigInt(1536)); - assert(typeof result === 'string'); - assert(result.includes('1.5') || result.includes('1.54')); - }); - }); - }); - }); +import assert from "node:assert"; +import { describe, it } from "node:test"; +import { filesize, partial } from "../../src/filesize.js"; + +describe("filesize", () => { + describe("Basic functionality", () => { + it("should convert bytes to human readable format", () => { + assert.strictEqual(filesize(1000), "1 kB"); + assert.strictEqual(filesize(1000000), "1 MB"); + assert.strictEqual(filesize(1000000000), "1 GB"); + }); + + it("should handle zero bytes", () => { + assert.strictEqual(filesize(0), "0 B"); + }); + + it("should handle small numbers", () => { + assert.strictEqual(filesize(1), "1 B"); + assert.strictEqual(filesize(512), "512 B"); + }); + + it("should handle negative numbers", () => { + assert.strictEqual(filesize(-1000), "-1 kB"); + assert.strictEqual(filesize(-1000000), "-1 MB"); + }); + }); + + describe("Bits option", () => { + it("should convert to bits when bits option is true", () => { + assert.strictEqual(filesize(1000, { bits: true }), "8 kbit"); + assert.strictEqual(filesize(1000000, { bits: true }), "8 Mbit"); + }); + + it("should handle zero with bits option", () => { + assert.strictEqual(filesize(0, { bits: true }), "0 bit"); + }); + + it("should respect forced exponent with bits", () => { + // Forced exponent should prevent auto-increment + assert.strictEqual(filesize(1024, { exponent: 0, bits: true }), "8192 bit"); + assert.strictEqual(filesize(1024, { exponent: 1, bits: true }), "8.19 kbit"); + }); + }); + + describe("Zero value edge cases", () => { + it("should handle zero with exponent output", () => { + assert.strictEqual(filesize(0, { output: "exponent" }), 0); + }); + + it("should handle zero with custom symbols", () => { + assert.strictEqual(filesize(0, { symbols: { B: "Bytes" } }), "0 Bytes"); + }); + + it("should handle zero with fullform", () => { + assert.strictEqual(filesize(0, { fullform: true }), "0 byte"); + }); + + it("should handle zero with fullform and bits", () => { + assert.strictEqual(filesize(0, { fullform: true, bits: true }), "0 bit"); + }); + + it("should handle zero with array output", () => { + const result = filesize(0, { output: "array" }); + assert.deepStrictEqual(result, [0, "B"]); + }); + + it("should handle zero with object output", () => { + const result = filesize(0, { output: "object" }); + assert.deepStrictEqual(result, { value: 0, symbol: "B", exponent: 0, unit: "B" }); + }); + }); + + describe("Standards", () => { + it("should use IEC standard", () => { + assert.strictEqual(filesize(1024, { standard: "iec" }), "1 KiB"); + assert.strictEqual(filesize(1048576, { standard: "iec" }), "1 MiB"); + }); + + it("should use JEDEC standard", () => { + assert.strictEqual(filesize(1024, { standard: "jedec" }), "1 KB"); + assert.strictEqual(filesize(1048576, { standard: "jedec" }), "1 MB"); + }); + + it("should use SI standard", () => { + assert.strictEqual(filesize(1000, { standard: "si" }), "1 kB"); + assert.strictEqual(filesize(1000000, { standard: "si" }), "1 MB"); + }); + }); + + describe("Output formats", () => { + it("should return string by default", () => { + const result = filesize(1000); + assert.strictEqual(typeof result, "string"); + assert.strictEqual(result, "1 kB"); + }); + + it('should return array when output is "array"', () => { + const result = filesize(1000, { output: "array" }); + assert(Array.isArray(result)); + assert.strictEqual(result[0], 1); + assert.strictEqual(result[1], "kB"); + }); + + it('should return object when output is "object"', () => { + const result = filesize(1000, { output: "object" }); + assert.strictEqual(typeof result, "object"); + assert.strictEqual(result.value, 1); + assert.strictEqual(result.symbol, "kB"); + assert.strictEqual(result.exponent, 1); + assert.strictEqual(result.unit, "kB"); + }); + + it('should return exponent when output is "exponent"', () => { + const result = filesize(1000, { output: "exponent" }); + assert.strictEqual(typeof result, "number"); + assert.strictEqual(result, 1); + }); + }); + + describe("Rounding", () => { + it("should round to specified decimal places", () => { + assert.strictEqual(filesize(1536, { round: 1 }), "1.5 kB"); + assert.strictEqual(filesize(1536, { round: 0 }), "2 kB"); + assert.strictEqual(filesize(1536, { round: 3 }), "1.536 kB"); + }); + + it("should use different rounding methods", () => { + assert.strictEqual(filesize(1536, { roundingMethod: "floor" }), "1.53 kB"); + assert.strictEqual(filesize(1536, { roundingMethod: "ceil" }), "1.54 kB"); + assert.strictEqual(filesize(1536, { roundingMethod: "round" }), "1.54 kB"); + }); + }); + + describe("Padding", () => { + it("should pad decimal places when pad is true", () => { + assert.strictEqual(filesize(1000, { pad: true, round: 2 }), "1.00 kB"); + assert.strictEqual(filesize(1536, { pad: true, round: 1 }), "1.5 kB"); + }); + + it("should pad decimal places for negative numbers", () => { + assert.strictEqual(filesize(-1000, { pad: true, round: 2 }), "-1.00 kB"); + assert.strictEqual(filesize(-1536, { pad: true, round: 2 }), "-1.54 kB"); + assert.strictEqual(filesize(-1024, { pad: true, round: 3 }), "-1.024 kB"); + }); + + it("should pad decimal places with locale formatting for negative numbers", () => { + assert.strictEqual(filesize(-1536, { locale: "de", pad: true, round: 2 }), "-1,54 kB"); + assert.strictEqual(filesize(-1000, { locale: "en-US", pad: true, round: 2 }), "-1.00 kB"); + }); + + it("should pad decimal places with locale formatting and grouping separators", () => { + // German locale: comma as decimal separator, dot as grouping separator + // Verify comma is correctly detected as decimal separator (not grouping separator) + assert.strictEqual(filesize(1234567, { locale: "de-DE", pad: true, round: 2 }), "1,23 MB"); + // Padding with German locale (3 decimal places) + assert.strictEqual(filesize(1536, { locale: "de-DE", pad: true, round: 3 }), "1,536 kB"); + // German locale with padding that adds decimal places + assert.strictEqual(filesize(1536, { locale: "de", pad: true, round: 4 }), "1,5360 kB"); + // German locale with grouping separator in integer part (1.234.567,89 kB) + assert.strictEqual( + filesize(1234567890, { locale: "de", exponent: 1, pad: true, round: 2 }), + "1.234.567,89 kB", + ); + // English locale with larger numbers + assert.strictEqual(filesize(1234567, { locale: "en-US", pad: true, round: 2 }), "1.23 MB"); + assert.strictEqual(filesize(1234567890, { locale: "en-US", pad: true, round: 2 }), "1.23 GB"); + }); + }); + + describe("Custom separators and spacers", () => { + it("should use custom decimal separator", () => { + assert.strictEqual(filesize(1536, { separator: "," }), "1,54 kB"); + }); + + it("should use custom spacer between value and unit", () => { + assert.strictEqual(filesize(1000, { spacer: "_" }), "1_kB"); + assert.strictEqual(filesize(1000, { spacer: "" }), "1kB"); + }); + }); + + describe("Custom symbols", () => { + it("should use custom symbols when provided", () => { + const customSymbols = { kB: "kilobyte" }; + assert.strictEqual(filesize(1000, { symbols: customSymbols }), "1 kilobyte"); + }); + }); + + describe("Locale formatting", () => { + it("should format using system locale when locale is true", () => { + const result = filesize(1000, { locale: true }); + assert(typeof result === "string"); + assert(result.includes("kB")); + }); + + it("should format using specific locale", () => { + const result = filesize(1536, { locale: "en-US" }); + assert(typeof result === "string"); + assert(result.includes("1.5")); + }); + }); + + describe("Full form names", () => { + it("should use full form names when fullform is true", () => { + assert.strictEqual(filesize(1000, { fullform: true }), "1 kilobyte"); + assert.strictEqual(filesize(2000, { fullform: true }), "2 kilobytes"); + assert.strictEqual(filesize(1, { fullform: true }), "1 byte"); + }); + + it("should use custom fullforms when provided", () => { + const customFullforms = ["", "thousand-byte"]; + assert.strictEqual( + filesize(1000, { fullform: true, fullforms: customFullforms }), + "1 thousand-byte", + ); + }); + }); + + describe("Base and exponent options", () => { + it("should use base 2 calculations", () => { + assert.strictEqual(filesize(1024, { base: 2 }), "1 KiB"); + }); + + it("should use base 10 calculations", () => { + assert.strictEqual(filesize(1000, { base: 10 }), "1 kB"); + }); + + it("should use forced exponent", () => { + assert.strictEqual(filesize(1024, { exponent: 0 }), "1024 B"); + assert.strictEqual(filesize(1024, { exponent: 2 }), "0 MB"); + }); + }); + + describe("Precision option", () => { + it("should use specified precision", () => { + assert.strictEqual(filesize(1536, { precision: 3 }), "1.54 kB"); + assert.strictEqual(filesize(1536000, { precision: 2 }), "1.5 MB"); + }); + + it("should remove scientific notation from precision results", () => { + // Test cases where toPrecision would normally produce scientific notation + // but our implementation removes it by splitting on "e" + + // Very large numbers that would produce scientific notation + assert.strictEqual(filesize(1234567890123, { precision: 2 }), "1.2 TB"); + assert.strictEqual(filesize(9876543210987, { precision: 3 }), "9.88 TB"); + + // Test with different precision values on large numbers + assert.strictEqual(filesize(1e15, { precision: 1 }), "1 PB"); + assert.strictEqual(filesize(1e15, { precision: 2 }), "1.0 PB"); + assert.strictEqual(filesize(1e15, { precision: 3 }), "1.00 PB"); + }); + + it("should handle precision with very small numbers", () => { + // Very small numbers (less than 1 byte) + assert.strictEqual(filesize(0.001, { precision: 1 }), "0 B"); + assert.strictEqual(filesize(0.1, { precision: 2 }), "0.0 B"); + assert.strictEqual(filesize(0.5, { precision: 1 }), "1 B"); + }); + + it("should handle precision with numbers that would produce e+ notation", () => { + // Numbers that produce e+ notation with toPrecision + const largeNum = 1234567890; + assert.strictEqual(filesize(largeNum, { precision: 2 }), "1.2 GB"); + assert.strictEqual(filesize(largeNum, { precision: 3 }), "1.23 GB"); + assert.strictEqual(filesize(largeNum, { precision: 1 }), "1 GB"); + }); + + it("should handle precision with numbers that would produce e- notation", () => { + // Very small decimal numbers that would produce e- notation + assert.strictEqual(filesize(0.0001, { precision: 1 }), "0 B"); + assert.strictEqual(filesize(0.00001, { precision: 2 }), "0.0 B"); + }); + + it("should work with precision and different standards", () => { + const largeNum = 1234567890; + assert.strictEqual(filesize(largeNum, { precision: 2, standard: "iec" }), "1.1 GiB"); + assert.strictEqual(filesize(largeNum, { precision: 2, standard: "jedec" }), "1.1 GB"); + assert.strictEqual(filesize(largeNum, { precision: 2, standard: "si" }), "1.2 GB"); + }); + + it("should work with precision and bits option", () => { + const largeNum = 1234567890; + assert.strictEqual(filesize(largeNum, { precision: 2, bits: true }), "9.9 Gbit"); + assert.strictEqual(filesize(largeNum, { precision: 3, bits: true }), "9.88 Gbit"); + }); + + it("should work with precision and array output", () => { + const result = filesize(1234567890, { precision: 2, output: "array" }); + assert(Array.isArray(result)); + assert.strictEqual(result[0], "1.2"); + assert.strictEqual(result[1], "GB"); + }); + + it("should work with precision and object output", () => { + const result = filesize(1234567890, { precision: 2, output: "object" }); + assert.strictEqual(typeof result, "object"); + assert.strictEqual(result.value, "1.2"); + assert.strictEqual(result.symbol, "GB"); + }); + + it("should handle precision with extremely large numbers", () => { + // Test with numbers that exceed normal exponent range + const extremeNumber = Math.pow(1024, 15); + const result = filesize(extremeNumber, { precision: 2 }); + assert(typeof result === "string"); + // Note: For extremely large numbers that exceed JavaScript's normal number range, + // the number itself may be in scientific notation, which is expected behavior + assert(result.includes("YB") || result.includes("YiB")); + }); + + it("should handle precision edge cases", () => { + // Test precision with zero + assert.strictEqual(filesize(0, { precision: 3 }), "0.00 B"); + + // Test precision with exactly 1 byte + assert.strictEqual(filesize(1, { precision: 2 }), "1.0 B"); + + // Test precision with negative numbers + assert.strictEqual(filesize(-1234567890, { precision: 2 }), "-1.2 GB"); + }); + + it("should ensure no scientific notation in any precision result", () => { + // Test a range of numbers that would normally produce scientific notation from toPrecision + // but should have it removed by our implementation + const testNumbers = [1e3, 1e6, 1e9, 1e12, 1234567890123456, Math.pow(10, 10)]; + + testNumbers.forEach((num) => { + [1, 2, 3, 4].forEach((precision) => { + const result = filesize(num, { precision }); + // Result should not contain scientific notation markers when using toPrecision + // Note: We exclude extremely large numbers that naturally contain 'e' in JavaScript + if (!num.toString().includes("e")) { + assert( + !result.includes("e"), + `Result "${result}" contains 'e' for number ${num} with precision ${precision}`, + ); + assert( + !result.includes("E"), + `Result "${result}" contains 'E' for number ${num} with precision ${precision}`, + ); + } + }); + }); + }); + + it("should handle precision with base 2 when scientific notation is produced (regular number)", () => { + // Test case that triggers base === 2 path in line 168 precision handling + // We need a number that will produce scientific notation with toPrecision + // and uses base 2 (IEC standard) + const testNumber = 999999999999999; // Large number that triggers scientific notation + const result = filesize(testNumber, { precision: 1, standard: "iec" }); + assert(typeof result === "string"); + assert(!result.includes("e")); + assert(!result.includes("E")); + // Should use IEC units (base 2) + assert(result.includes("TiB") || result.includes("PiB")); + }); + + it("should handle precision with base 2 when scientific notation is produced (BigInt)", () => { + // Test case that triggers base === 2 path in line 168 precision handling using BigInt + // This ensures both number types are tested for base 2 path + const testNumber = BigInt("999999999999999"); // Large BigInt that triggers scientific notation + const result = filesize(testNumber, { precision: 1, standard: "iec" }); + assert(typeof result === "string"); + assert(!result.includes("e")); + assert(!result.includes("E")); + // Should use IEC units (base 2) + assert(result.includes("TiB") || result.includes("PiB")); + }); + + it("should handle precision with base 10 when scientific notation is produced (regular number)", () => { + // Test case that triggers base === 10 path in line 168 precision handling + // We need a number that will produce scientific notation with toPrecision + // and uses base 10 (SI/JEDEC standard) + const testNumber = 999999999999999; // Large number that triggers scientific notation + const result = filesize(testNumber, { precision: 1, standard: "jedec" }); + assert(typeof result === "string"); + assert(!result.includes("e")); + assert(!result.includes("E")); + // Should use JEDEC units (base 10 logic) + assert(result.includes("TB") || result.includes("PB")); + }); + + it("should handle precision with base 10 when scientific notation is produced (BigInt)", () => { + // Test case that triggers base === 10 path in line 168 precision handling using BigInt + // This ensures both number types are tested for base 10 path + const testNumber = BigInt("999999999999999"); // Large BigInt that triggers scientific notation + const result = filesize(testNumber, { precision: 1, standard: "jedec" }); + assert(typeof result === "string"); + assert(!result.includes("e")); + assert(!result.includes("E")); + // Should use JEDEC units (base 10 logic) + assert(result.includes("TB") || result.includes("PB")); + }); + + it("should handle precision with YiB-level numbers where e >= 8 (regular number)", () => { + // Test case that covers line 166 when e >= 8 (YiB level) using regular number + // This triggers result[0].includes(E) but e < 8 is false, so increment logic is skipped + const yibNumber = Math.pow(1024, 8); // 1 YiB in bytes using regular number + const result = filesize(yibNumber, { precision: 1, standard: "iec" }); + assert(typeof result === "string"); + // For extremely large regular numbers, some scientific notation may be expected + assert(result.includes("YiB")); + }); + + it("should handle precision with YiB-level numbers where e >= 8 (BigInt)", () => { + // Test case that covers line 166 when e >= 8 (YiB level) using BigInt + // This triggers result[0].includes(E) but e < 8 is false, so increment logic is skipped + // YiB is at exponent 8, so we need a number large enough to reach this level + const yibNumber = BigInt(1024) ** BigInt(8); // 1 YiB in bytes using BigInt + const result = filesize(yibNumber, { precision: 1, standard: "iec" }); + assert(typeof result === "string"); + assert(!result.includes("e")); + assert(!result.includes("E")); + assert(result.includes("YiB")); + }); + + it("should handle precision with extremely large numbers that exceed YiB (regular number)", () => { + // Test case for numbers that are larger than YiB using regular number + // This ensures we test the e >= 8 condition thoroughly with regular numbers + const extremeNumber = Math.pow(1024, 8) * 1000; // 1000 YiB using regular number + const result = filesize(extremeNumber, { precision: 2, standard: "iec" }); + assert(typeof result === "string"); + // For extremely large regular numbers, some scientific notation may be expected + assert(result.includes("YiB")); + }); + + it("should handle precision with extremely large numbers that exceed YiB (BigInt)", () => { + // Test case for numbers that are larger than YiB using BigInt + // This ensures we test the e >= 8 condition thoroughly with BigInt + const extremeNumber = BigInt(1024) ** BigInt(8) * BigInt(1000); // 1000 YiB using BigInt + const result = filesize(extremeNumber, { precision: 2, standard: "iec" }); + assert(typeof result === "string"); + // For extremely large BigInt numbers, the result should still use YiB units + // Some scientific notation in the final result may be acceptable for such large numbers + assert(result.includes("YiB")); + }); + + it("should handle precision with specific test case for GiB values", () => { + // Test specific precision case: filesize(1058223158, {base: 2, standard: 'iec', precision: 3}) + // This should output '0.990 GiB' + assert.strictEqual( + filesize(1058223158, { base: 2, standard: "iec", precision: 3 }), + "0.990 GiB", + ); + }); + }); + + describe("Edge cases", () => { + it("should handle very large numbers", () => { + const largeNumber = Math.pow(1024, 8); + const result = filesize(largeNumber); + assert(typeof result === "string"); + assert(result.includes("YB") || result.includes("YiB")); + }); + + it("should handle BigInt input", () => { + const result = filesize(BigInt(1024), { standard: "jedec" }); + assert.strictEqual(result, "1 KB"); + }); + + it("should handle numbers exceeding maximum exponent", () => { + const hugeNumber = Math.pow(1024, 10); + const result = filesize(hugeNumber); + assert(typeof result === "string"); + }); + + it("should handle numbers exceeding lookup table size (decimal)", () => { + // Test number requiring Math.pow fallback for decimal powers + const hugeNumber = Math.pow(1000, 15); // Exceeds DECIMAL_POWERS array + const result = filesize(hugeNumber, { base: 10 }); + assert.ok(result.includes("YB")); + }); + + it("should handle numbers exceeding lookup table size (binary)", () => { + // Test number requiring Math.pow fallback for binary powers + const hugeNumber = Math.pow(2, 100); // Exceeds BINARY_POWERS array + const result = filesize(hugeNumber, { base: 2 }); + assert.ok(result.includes("YiB")); + }); + + it("should handle precision with numbers exceeding lookup table", () => { + const hugeNumber = Math.pow(1000, 12); + const result = filesize(hugeNumber, { precision: 2, base: 10 }); + assert.ok(result.includes("YB")); + }); + + it("should handle precision with round=0 and scientific notation", () => { + // Test case where p === 1 in the precision recalculation branch + const result = filesize(1000000000000000000, { precision: 1, round: 0 }); + assert.ok(typeof result === "string"); + }); + + it("should cover p===1 branch in precision recalculation", () => { + // Try to force the p===1 branch in the precision recalculation + // This needs a very specific edge case where scientific notation is produced + // and after increment e=0 or round=0 makes p=1 + const result = filesize(0.0001, { precision: 20, round: 0 }); + assert.ok(typeof result === "string"); + }); + + it("should trigger precision recalculation with bytes and round=0", () => { + // Another attempt: very small number that might produce scientific notation in bytes + const result = filesize(0.000000000000001, { precision: 30, round: 0 }); + assert.ok(typeof result === "string"); + }); + + it("should cover p===1 branch with input=1 and extreme precision", () => { + // When input=1, e=0, so p=1. High precision might trigger scientific notation + // and the recalculation with p===1 branch + const result = filesize(1, { precision: 50 }); + assert.ok(typeof result === "string"); + }); + + it("should attempt to trigger the elusive p===1 branch in recalculation", () => { + // Numbers that would produce scientific notation with precision=1 + // When e=0 (bytes), p=1, and toPrecision(1) might produce "1e+1" etc. + const result = filesize(10, { precision: 1, round: 0 }); + assert.ok(typeof result === "string"); + }); + + it("should handle extremely large numbers with precision adjustment", () => { + const extremeNumber = Math.pow(1024, 15); // Much larger than supported exponent + const result = filesize(extremeNumber, { precision: 3 }); + assert(typeof result === "string"); + assert(result.includes("YB") || result.includes("YiB")); + }); + + it("should handle bit conversion auto-increment near boundary", () => { + // Test edge case where bits conversion causes auto-increment to next unit + const result = filesize(127.5, { bits: true }); // Should auto-increment + assert(typeof result === "string"); + }); + + it("should handle result equal to ceil boundary for auto-increment", () => { + // Test case where result equals ceil and triggers increment + const result = filesize(999.5, { round: 0 }); // Should round to 1000 and increment + assert(typeof result === "string"); + }); + + it("should handle padding with custom separator edge cases", () => { + // Test padding when separator is found via regex + const result = filesize(1536, { pad: true, round: 2, locale: "de-DE" }); + assert(typeof result === "string"); + }); + + it("should handle NaN exponent edge case", () => { + const result = filesize(1000, { exponent: NaN }); + assert.strictEqual(result, "1 kB"); + }); + + it("should handle NaN exponent with precision", () => { + // NaN exponent should behave like auto (-1) and allow scientific notation normalization + const result = filesize(1e15, { exponent: NaN, precision: 2 }); + assert(typeof result === "string"); + assert(!result.includes("e"), "Should not contain scientific notation"); + }); + + it("should handle NaN exponent with auto-increment", () => { + // NaN exponent should allow auto-increment when result equals ceil (1024) + const result = filesize(1024, { exponent: NaN, standard: "iec" }); + assert.strictEqual(result, "1 KiB"); + }); + }); + + describe("Error handling", () => { + it("should throw TypeError for invalid number input", () => { + assert.throws(() => filesize("invalid"), TypeError); + assert.throws(() => filesize(NaN), TypeError); + assert.throws(() => filesize({}), TypeError); + }); + + it("should throw TypeError for invalid rounding method", () => { + assert.throws(() => filesize(1024, { roundingMethod: "invalid" }), TypeError); + assert.throws(() => filesize(1024, { roundingMethod: "nonexistent" }), TypeError); + }); + }); + + describe("Input type handling", () => { + describe("Number input", () => { + describe("filesize() with number input", () => { + it("should handle integer numbers", () => { + assert.strictEqual(filesize(1024), "1.02 kB"); + assert.strictEqual(filesize(0), "0 B"); + assert.strictEqual(filesize(-1024), "-1.02 kB"); + }); + + it("should handle floating point numbers", () => { + assert.strictEqual(filesize(1536.5), "1.54 kB"); + assert.strictEqual(filesize(0.5), "1 B"); + }); + + it("should handle very large numbers", () => { + const largeNumber = Number.MAX_SAFE_INTEGER; + const result = filesize(largeNumber); + assert(typeof result === "string"); + }); + }); + + describe("partial() with number input", () => { + it("should handle integer numbers", () => { + const formatIEC = partial({ standard: "iec" }); + assert.strictEqual(formatIEC(1024), "1 KiB"); + assert.strictEqual(formatIEC(0), "0 B"); + assert.strictEqual(formatIEC(-1024), "-1 KiB"); + }); + + it("should handle floating point numbers", () => { + const formatRounded = partial({ round: 1 }); + assert.strictEqual(formatRounded(1536.7), "1.5 kB"); + assert.strictEqual(formatRounded(0.5), "1 B"); + }); + + it("should handle very large numbers", () => { + const formatBits = partial({ bits: true }); + const largeNumber = Number.MAX_SAFE_INTEGER; + const result = formatBits(largeNumber); + assert(typeof result === "string"); + }); + }); + }); + + describe("String input", () => { + describe("filesize() with string input", () => { + it("should handle valid integer string", () => { + assert.strictEqual(filesize("1024"), "1.02 kB"); + assert.strictEqual(filesize("0"), "0 B"); + assert.strictEqual(filesize("-1024"), "-1.02 kB"); + }); + + it("should handle valid float string", () => { + assert.strictEqual(filesize("1536.5"), "1.54 kB"); + assert.strictEqual(filesize("0.5"), "1 B"); + }); + + it("should handle scientific notation string", () => { + assert.strictEqual(filesize("1e3"), "1 kB"); + assert.strictEqual(filesize("1.024e3"), "1.02 kB"); + }); + + it("should handle string with leading/trailing whitespace", () => { + assert.strictEqual(filesize(" 1024 "), "1.02 kB"); + assert.strictEqual(filesize("\t1024\n"), "1.02 kB"); + }); + + it("should work with string input and different options", () => { + assert.strictEqual(filesize("1024", { standard: "iec" }), "1 KiB"); + assert.strictEqual(filesize("1000", { bits: true }), "8 kbit"); + assert.strictEqual(filesize("1024", { output: "array" })[0], 1.02); + }); + }); + + describe("partial() with string input", () => { + it("should handle valid integer string", () => { + const formatJEDEC = partial({ standard: "jedec" }); + assert.strictEqual(formatJEDEC("1024"), "1 KB"); + assert.strictEqual(formatJEDEC("0"), "0 B"); + assert.strictEqual(formatJEDEC("-1024"), "-1 KB"); + }); + + it("should handle valid float string", () => { + const formatPadded = partial({ pad: true, round: 2 }); + assert.strictEqual(formatPadded("1536.5"), "1.54 kB"); + assert.strictEqual(formatPadded("0.5"), "1.00 B"); + }); + + it("should handle scientific notation string", () => { + const formatBits = partial({ bits: true }); + assert.strictEqual(formatBits("1e3"), "8 kbit"); + assert.strictEqual(formatBits("1.024e3"), "8.19 kbit"); + }); + + it("should handle string with leading/trailing whitespace", () => { + const formatFullForm = partial({ fullform: true, standard: "iec" }); + assert.strictEqual(formatFullForm(" 1024 "), "1 kibibyte"); + assert.strictEqual(formatFullForm("\t2048\n"), "2 kibibytes"); + }); + + it("should work with string input and complex options", () => { + const formatComplex = partial({ + standard: "si", + round: 1, + spacer: "_", + output: "array", + }); + const result = formatComplex("1000"); + assert(Array.isArray(result)); + assert.strictEqual(result[0], 1); + assert.strictEqual(result[1], "kB"); + }); + }); + }); + + describe("BigInt input", () => { + describe("filesize() with BigInt input", () => { + it("should handle regular BigInt values", () => { + assert.strictEqual(filesize(BigInt(1024)), "1.02 kB"); + assert.strictEqual(filesize(BigInt(0)), "0 B"); + assert.strictEqual(filesize(BigInt(-1024)), "-1.02 kB"); + }); + + it("should handle large BigInt values", () => { + const largeBigInt = BigInt("9007199254740992"); // 2^53 + const result = filesize(largeBigInt); + assert(typeof result === "string"); + assert(result.includes("PB") || result.includes("PiB")); + }); + + it("should handle very large BigInt values", () => { + const veryLargeBigInt = BigInt("1208925819614629174706176"); // 1024^8 + const result = filesize(veryLargeBigInt); + assert(typeof result === "string"); + assert(result.includes("YB") || result.includes("YiB")); + }); + + it("should work with BigInt input and different options", () => { + assert.strictEqual(filesize(BigInt(1024), { standard: "jedec" }), "1 KB"); + assert.strictEqual(filesize(BigInt(1000), { bits: true }), "8 kbit"); + + const objectResult = filesize(BigInt(1024), { output: "object" }); + assert.strictEqual(objectResult.value, 1.02); + assert.strictEqual(objectResult.symbol, "kB"); + }); + }); + + describe("partial() with BigInt input", () => { + it("should handle regular BigInt values", () => { + const formatCustomSymbols = partial({ symbols: { KiB: "kibibyte" }, standard: "iec" }); + assert.strictEqual(formatCustomSymbols(BigInt(1024)), "1 kibibyte"); + assert.strictEqual(formatCustomSymbols(BigInt(0)), "0 B"); + assert.strictEqual(formatCustomSymbols(BigInt(-1024)), "-1 kibibyte"); + }); + + it("should handle large BigInt values", () => { + const formatPrecision = partial({ precision: 3 }); + const largeBigInt = BigInt("9007199254740992"); // 2^53 + const result = formatPrecision(largeBigInt); + assert(typeof result === "string"); + assert(result.includes("PB") || result.includes("PiB")); + }); + + it("should handle very large BigInt values", () => { + const formatExponent = partial({ exponent: -1 }); // Auto-select exponent + const veryLargeBigInt = BigInt("1208925819614629174706176"); // 1024^8 + const result = formatExponent(veryLargeBigInt); + assert(typeof result === "string"); + assert(result.includes("YB") || result.includes("YiB")); + }); + + it("should work with BigInt input and different output formats", () => { + const formatObject = partial({ output: "object", standard: "iec" }); + const objectResult = formatObject(BigInt(1024)); + assert.strictEqual(typeof objectResult, "object"); + assert.strictEqual(objectResult.value, 1); + assert.strictEqual(objectResult.symbol, "KiB"); + + const formatExponentOnly = partial({ output: "exponent" }); + const exponentResult = formatExponentOnly(BigInt(1024)); + assert.strictEqual(typeof exponentResult, "number"); + assert.strictEqual(exponentResult, 1); + }); + + it("should work with BigInt input and bits option", () => { + const formatBits = partial({ bits: true, round: 1 }); + assert.strictEqual(formatBits(BigInt(1024)), "8.2 kbit"); + assert.strictEqual(formatBits(BigInt(1000)), "8 kbit"); + }); + + it("should work with BigInt input and locale formatting", () => { + const formatLocale = partial({ locale: "en-US", round: 2 }); + const result = formatLocale(BigInt(1536)); + assert(typeof result === "string"); + assert(result.includes("1.5") || result.includes("1.54")); + }); + }); + }); + }); }); -describe('partial', () => { - describe('Basic functionality', () => { - it('should create a function with preset options', () => { - const formatKiB = partial({ standard: 'iec' }); - assert.strictEqual(typeof formatKiB, 'function'); - assert.strictEqual(formatKiB(1024), '1 KiB'); - }); - - it('should preserve all options in partial application', () => { - const formatWithOptions = partial({ - round: 1, - standard: 'iec', - spacer: '_' - }); - assert.strictEqual(formatWithOptions(1536), '1.5_KiB'); - }); - }); - - describe('Options inheritance', () => { - it('should apply bits option from partial', () => { - const formatBits = partial({ bits: true }); - assert.strictEqual(formatBits(1000), '8 kbit'); - }); - - it('should apply pad option from partial', () => { - const formatPadded = partial({ pad: true, round: 2 }); - assert.strictEqual(formatPadded(1000), '1.00 kB'); - }); - - it('should apply output format from partial', () => { - const formatArray = partial({ output: 'array' }); - const result = formatArray(1000); - assert(Array.isArray(result)); - assert.strictEqual(result[0], 1); - assert.strictEqual(result[1], 'kB'); - }); - - it('should apply custom symbols from partial', () => { - const formatCustom = partial({ symbols: { kB: 'kilobyte' } }); - assert.strictEqual(formatCustom(1000), '1 kilobyte'); - }); - }); - - describe('Multiple partial functions', () => { - it('should create independent partial functions', () => { - const formatIEC = partial({ standard: 'iec' }); - const formatJEDEC = partial({ standard: 'jedec' }); - - assert.strictEqual(formatIEC(1024), '1 KiB'); - assert.strictEqual(formatJEDEC(1024), '1 KB'); - }); - - it('should handle complex option combinations', () => { - const formatComplex = partial({ - bits: true, - standard: 'iec', - round: 1, - fullform: true - }); - assert.strictEqual(formatComplex(1024), '8 kibibits'); - }); - }); - - describe('Precision with partial', () => { - it('should apply precision option from partial', () => { - const formatPrecision = partial({ precision: 2 }); - assert.strictEqual(formatPrecision(1234567890), '1.2 GB'); - assert.strictEqual(formatPrecision(9876543210), '9.9 GB'); - }); - - it('should remove scientific notation in partial functions', () => { - const formatWithPrecision = partial({ precision: 3 }); - const largeNum = 1234567890123; - const result = formatWithPrecision(largeNum); - assert(!result.includes('e')); - assert(!result.includes('E')); - assert.strictEqual(result, '1.23 TB'); - }); - - it('should work with precision and other options combined', () => { - const formatComplex = partial({ - precision: 2, - standard: 'iec', - bits: true - }); - const result = formatComplex(1234567890); - assert(!result.includes('e')); - assert(!result.includes('E')); - assert.strictEqual(result, '9.2 Gibit'); - }); - }); - - describe('Default behavior', () => { - it('should work with no options provided', () => { - const defaultFormat = partial(); - assert.strictEqual(typeof defaultFormat, 'function'); - assert.strictEqual(defaultFormat(1000), '1 kB'); - }); - - it('should work with empty options object', () => { - const emptyOptions = partial({}); - assert.strictEqual(emptyOptions(1000), '1 kB'); - }); - }); -}); \ No newline at end of file +describe("partial", () => { + describe("Basic functionality", () => { + it("should create a function with preset options", () => { + const formatKiB = partial({ standard: "iec" }); + assert.strictEqual(typeof formatKiB, "function"); + assert.strictEqual(formatKiB(1024), "1 KiB"); + }); + + it("should preserve all options in partial application", () => { + const formatWithOptions = partial({ + round: 1, + standard: "iec", + spacer: "_", + }); + assert.strictEqual(formatWithOptions(1536), "1.5_KiB"); + }); + }); + + describe("Options inheritance", () => { + it("should apply bits option from partial", () => { + const formatBits = partial({ bits: true }); + assert.strictEqual(formatBits(1000), "8 kbit"); + }); + + it("should apply pad option from partial", () => { + const formatPadded = partial({ pad: true, round: 2 }); + assert.strictEqual(formatPadded(1000), "1.00 kB"); + }); + + it("should apply output format from partial", () => { + const formatArray = partial({ output: "array" }); + const result = formatArray(1000); + assert(Array.isArray(result)); + assert.strictEqual(result[0], 1); + assert.strictEqual(result[1], "kB"); + }); + + it("should apply custom symbols from partial", () => { + const formatCustom = partial({ symbols: { kB: "kilobyte" } }); + assert.strictEqual(formatCustom(1000), "1 kilobyte"); + }); + }); + + describe("Multiple partial functions", () => { + it("should create independent partial functions", () => { + const formatIEC = partial({ standard: "iec" }); + const formatJEDEC = partial({ standard: "jedec" }); + + assert.strictEqual(formatIEC(1024), "1 KiB"); + assert.strictEqual(formatJEDEC(1024), "1 KB"); + }); + + it("should handle complex option combinations", () => { + const formatComplex = partial({ + bits: true, + standard: "iec", + round: 1, + fullform: true, + }); + assert.strictEqual(formatComplex(1024), "8 kibibits"); + }); + }); + + describe("Precision with partial", () => { + it("should apply precision option from partial", () => { + const formatPrecision = partial({ precision: 2 }); + assert.strictEqual(formatPrecision(1234567890), "1.2 GB"); + assert.strictEqual(formatPrecision(9876543210), "9.9 GB"); + }); + + it("should remove scientific notation in partial functions", () => { + const formatWithPrecision = partial({ precision: 3 }); + const largeNum = 1234567890123; + const result = formatWithPrecision(largeNum); + assert(!result.includes("e")); + assert(!result.includes("E")); + assert.strictEqual(result, "1.23 TB"); + }); + + it("should work with precision and other options combined", () => { + const formatComplex = partial({ + precision: 2, + standard: "iec", + bits: true, + }); + const result = formatComplex(1234567890); + assert(!result.includes("e")); + assert(!result.includes("E")); + assert.strictEqual(result, "9.2 Gibit"); + }); + }); + + describe("Default behavior", () => { + it("should work with no options provided", () => { + const defaultFormat = partial(); + assert.strictEqual(typeof defaultFormat, "function"); + assert.strictEqual(defaultFormat(1000), "1 kB"); + }); + + it("should work with empty options object", () => { + const emptyOptions = partial({}); + assert.strictEqual(emptyOptions(1000), "1 kB"); + }); + }); + + describe("Immutability", () => { + it("should not be affected by mutations to top-level primitive options", () => { + const opts = { standard: "iec", round: 1 }; + const format = partial(opts); + + // First call should work normally + assert.strictEqual(format(1536), "1.5 KiB"); + + // Mutate the original options object + opts.standard = "jedec"; + opts.round = 0; + + // The partial function should still use the destructured primitive values + assert.strictEqual(format(1536), "1.5 KiB"); + }); + + it("should not affect other partial functions when modifying one", () => { + const format1 = partial({ symbols: { kB: "first_kB" } }); + const format2 = partial({ symbols: { kB: "second_kB" } }); + + // Both should work independently + assert.strictEqual(format1(1000), "1 first_kB"); + assert.strictEqual(format2(1000), "1 second_kB"); + }); + }); +}); diff --git a/types/filesize.d.ts b/types/filesize.d.ts index 1e7a813..4bf61cb 100644 --- a/types/filesize.d.ts +++ b/types/filesize.d.ts @@ -71,9 +71,9 @@ export type FilesizeReturn = * @returns Formatted file size based on output option * @throws {TypeError} When arg is not a valid number or roundingMethod is invalid * @example - * filesize(1024) // "1 KB" - * filesize(1024, {bits: true}) // "8 Kb" - * filesize(1024, {output: "object"}) // {value: 1, symbol: "KB", exponent: 1, unit: "KB"} + * filesize(1024) // "1.02 kB" + * filesize(1024, {bits: true}) // "8.19 kbit" + * filesize(1024, {output: "object"}) // {value: 1.02, symbol: "kB", exponent: 1, unit: "kB"} */ export function filesize( arg: number | string | bigint, @@ -86,8 +86,9 @@ export function filesize( * @returns A function that takes a file size and returns formatted output * @example * const formatBytes = partial({round: 1, standard: "iec"}); - * formatBytes(1024) // "1.0 KiB" - * formatBytes(2048) // "2.0 KiB" + * formatBytes(1024) // "1 KiB" + * formatBytes(2048) // "2 KiB" + * formatBytes(1536) // "1.5 KiB" */ export function partial( options?: T diff --git a/types/helpers.d.ts b/types/helpers.d.ts index 3c10088..fdafbd5 100644 --- a/types/helpers.d.ts +++ b/types/helpers.d.ts @@ -48,6 +48,7 @@ export function getBaseConfiguration(standard: string, base: number): BaseConfig * @param fullforms - Custom full forms * @param output - Output format * @param spacer - Spacer character + * @param symbol - Symbol to use (optional, defaults based on bits/standard) * @returns Formatted result */ export function handleZeroValue( @@ -58,7 +59,8 @@ export function handleZeroValue( full: boolean, fullforms: string[], output: string, - spacer: string + spacer: string, + symbol?: string ): string | [number | string, string] | { value: number | string; symbol: string; exponent: number; unit: string } | number; /** @@ -68,6 +70,7 @@ export function handleZeroValue( * @param isDecimal - Whether to use decimal powers * @param bits - Whether to calculate bits * @param ceil - Ceiling value for auto-increment + * @param autoExponent - Whether exponent is auto (-1 or NaN) * @returns Object with result and e properties */ export function calculateOptimizedValue( @@ -75,7 +78,8 @@ export function calculateOptimizedValue( e: number, isDecimal: boolean, bits: boolean, - ceil: number + ceil: number, + autoExponent?: boolean ): OptimizedValueResult; /** @@ -89,6 +93,7 @@ export function calculateOptimizedValue( * @param ceil - Ceiling value * @param roundingFunc - Rounding function * @param round - Round value + * @param exponent - Forced exponent (-1 for auto) * @returns Object with value and e properties */ export function applyPrecisionHandling( @@ -100,7 +105,8 @@ export function applyPrecisionHandling( bits: boolean, ceil: number, roundingFunc: (x: number) => number, - round: number + round: number, + exponent: number ): PrecisionHandlingResult; /**