diff --git a/package-lock.json b/package-lock.json index e93ba67..f5a6802 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bytes-kit", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "bytes-kit", - "version": "1.2.0", + "version": "1.3.0", "license": "MIT", "devDependencies": { "tsup": "^8.0.2", diff --git a/package.json b/package.json index 8af66ac..42c3bea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bytes-kit", - "version": "1.2.0", + "version": "1.3.0", "description": "Utilities for formatting, parsing, and working with byte sizes.", "type": "module", "main": "./dist/index.cjs", @@ -8,9 +8,14 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "require": "./dist/index.cjs" + "import": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "require": { + "types": "./dist/index.d.cts", + "default": "./dist/index.cjs" + } } }, "files": [ @@ -57,6 +62,9 @@ "sideEffects": false, "author": "Cool Dev Master ", "license": "MIT", + "engines": { + "node": ">=18" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/cool-Dev-master" diff --git a/playground.ts b/playground.ts index 0f7b779..b531038 100644 --- a/playground.ts +++ b/playground.ts @@ -10,6 +10,7 @@ import bytes, { analyzeBytes, isValidByte, detectUnit, + convertBytes, } from './src/index.js'; console.log('=== Bytes Kit Playground ===\n'); @@ -70,7 +71,15 @@ console.log('sortBytes(arr): ', sortBytes(arr)); console.log('analyzeBytes(arr):', analyzeBytes(arr)); console.log(); -// 7. Error Handling Fallback Check +// 7. Conversion Operations +console.log('--- 6.5. Conversion Operations ---'); +console.log('convertBytes("10 MB", "KB"): ', convertBytes('10 MB', 'KB')); +console.log('convertBytes("1 GiB", "MiB"): ', convertBytes('1 GiB', 'MiB')); +console.log('convertBytes("1 GiB", "MB"): ', convertBytes('1 GiB', 'MB')); +console.log('convertBytes("1.5 MB", "KB", { format: true }):', convertBytes('1.5 MB', 'KB', { format: true })); +console.log(); + +// 8. Error Handling Fallback Check console.log('--- 7. Error Handling Fallback Check ---'); console.log('isValidByte("invalid"): ', isValidByte('invalid')); // never calls onError console.log('parseBytes("invalid"): ', parseBytes('invalid')); // calls onError, returns 0 diff --git a/src/formatter.test.ts b/src/formatter.test.ts index 824d989..a34d2e4 100644 --- a/src/formatter.test.ts +++ b/src/formatter.test.ts @@ -11,6 +11,7 @@ import { sortBytes, isValidByte, detectUnit, + convertBytes, } from './formatter.js'; import { defaultConfig, getConfig, resetConfig } from './config.js'; @@ -451,6 +452,47 @@ describe('bytes-kit New Utility APIs', () => { expect(sortBytes(['1 MB', 'invalid'])).toEqual(['invalid', '1 MB']); }); }); + + describe('convertBytes', () => { + it('converts byte value to target unit returning a number by default', () => { + expect(convertBytes('10 MB', 'KB')).toBe(10000); + expect(convertBytes('1 GB', 'MB')).toBe(1000); + expect(convertBytes(10000000, 'MB')).toBe(10); + expect(convertBytes('10.5 MB', 'B')).toBe(10500000); + }); + + it('handles binary conversions correctly', () => { + expect(convertBytes('1 GiB', 'MiB')).toBe(1024); + expect(convertBytes('1024 KiB', 'MiB')).toBe(1); + expect(convertBytes(1024 * 1024 * 1024, 'GiB')).toBe(1); + }); + + it('handles cross-base conversions (binary <-> decimal)', () => { + expect(convertBytes('1 GiB', 'MB')).toBe(1073.741824); + expect(convertBytes('1 MB', 'KiB')).toBe(1000000 / 1024); + }); + + it('returns formatted string when options.format is true', () => { + expect(convertBytes('10 MB', 'KB', { format: true })).toBe('10000 KB'); + expect(convertBytes('1 GiB', 'MiB', { format: true })).toBe('1024 MiB'); + expect(convertBytes('1.5 MB', 'KB', { format: true, space: false })).toBe('1500KB'); + expect(convertBytes(1024, 'KiB', { format: true, decimalPlaces: 1, fixedDecimals: true })).toBe('1.0 KiB'); + }); + + it('throws errors on invalid inputs or units by default', () => { + expect(() => convertBytes('invalid', 'MB')).toThrow(); + expect(() => convertBytes('10 MB', 'invalid' as any)).toThrow(); + }); + + it('respects global error handling (throwOnError: false)', () => { + defaultConfig({ throwOnError: false }); + expect(convertBytes('invalid', 'MB')).toBe(0); + expect(convertBytes('10 MB', 'invalid' as any)).toBe(0); + + expect(convertBytes('invalid', 'MB', { format: true })).toBe('0 MB'); + expect(convertBytes('10 MB', 'invalid' as any, { format: true })).toBe('0 B'); + }); + }); }); diff --git a/src/formatter.ts b/src/formatter.ts index d83dd39..de6f109 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -472,3 +472,44 @@ export function sortBytes( } } +/** + * Converts a byte value (number or string) to a target unit. + * + * @param input The input byte value (e.g. '10 MB', or raw number of bytes). + * @param targetFormat The unit to convert to (e.g. 'KB', 'MiB'). + * @param options Additional options. If `format` is true, returns a formatted string (e.g. "10000 KB"). + * You can also pass formatting options (like `decimalPlaces`, `fixedDecimals`, `space`, `locale`). + * @returns The converted value as a number, or as a formatted string if `options.format` is true. + */ +export function convertBytes( + input: number | string, + targetFormat: UnitType, + options?: FormatOptions & { format?: boolean }, +): number | string { + const cfg = mergeConfig(options); + try { + const bytes = toBytes(input); + const unitLower = targetFormat.toLowerCase(); + const unitConfig = UNIT_POWER_MAP[unitLower]; + if (!unitConfig) { + throw new Error(`Invalid target unit: "${targetFormat}"`); + } + + const convertedValue = bytes / Math.pow(unitConfig.base, unitConfig.exponent); + + if (options?.format) { + return _formatBytesCore(bytes, { ...cfg, unit: targetFormat }); + } + + return convertedValue; + } catch (err) { + const fallback = options?.format ? '0 B' : 0; + return handleError( + err instanceof Error ? err : new Error(String(err)), + fallback, + cfg, + ); + } +} + + diff --git a/src/index.ts b/src/index.ts index 231f0f2..67946d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,7 @@ export { analyzeBytes, isValidByte, detectUnit, + convertBytes, } from './formatter.js'; export { defaultConfig, getConfig, resetConfig } from './config.js'; @@ -34,6 +35,7 @@ import { analyzeBytes, isValidByte, detectUnit, + convertBytes, } from './formatter.js'; const bytes = { @@ -49,6 +51,7 @@ const bytes = { analyzeBytes, isValidByte, detectUnit, + convertBytes, defaultConfig, getConfig, resetConfig,