diff --git a/src/tests/bigint.test.ts b/src/tests/bigint.test.ts index 773e2dc..5f6d5d2 100644 --- a/src/tests/bigint.test.ts +++ b/src/tests/bigint.test.ts @@ -4,20 +4,42 @@ const hashids = new Hashids() const _BigInt = typeof BigInt === 'function' ? BigInt : undefined describe('BigInt environment', () => { - beforeAll(() => { - // @ts-expect-error wrong output - delete global.BigInt - }) + describe('BigInt on unsupported environment', () => { + beforeAll(() => { + // @ts-expect-error wrong output + delete global.BigInt + }) + + afterAll(() => { + if (_BigInt) global.BigInt = _BigInt + }) - afterAll(() => { - if (_BigInt) global.BigInt = _BigInt + it('throws decoding BigInt on unsupported environment', () => { + expect(() => + hashids.decode('N95VW0Lo06rQBvJDOE2BVvREP86AqvYN4O9g9p'), + ).toThrowErrorMatchingInlineSnapshot( + `"Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment"`, + ) + }) + + it('throws encoding a big numeric string on unsupported environment', () => { + expect(() => + hashids.encode('90071992547409910123456789'), + ).toThrowErrorMatchingInlineSnapshot( + `"Unable to encode the provided BigInt string without loss of information due to lack of support for BigInt type in the current environment"`, + ) + }) }) - it('throws decoding BigInt on unsupported environment', () => { - expect(() => - hashids.decode('N95VW0Lo06rQBvJDOE2BVvREP86AqvYN4O9g9p'), - ).toThrowErrorMatchingInlineSnapshot( - `"Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment"`, - ) + describe('BigInt on supported environment', () => { + it('decodes big numeric string on supported environment', () => { + const id = '90071992547409910123456789' + const encodedId = hashids.encode(id) + const [decodedId] = hashids.decode(encodedId) + + expect(encodedId).toBeTruthy() + expect(decodedId).toBeTruthy() + expect(id).toBe(decodedId?.toString()) + }) }) }) diff --git a/src/util.ts b/src/util.ts index e62d69f..51c2883 100644 --- a/src/util.ts +++ b/src/util.ts @@ -92,19 +92,33 @@ export const fromAlphabet = ( if (isSafeValue) { return value } - if (typeof BigInt === 'function') { - return BigInt(carry) * BigInt(alphabetChars.length) + BigInt(index) - } - // we do not have support for BigInt: - throw new Error( + + throwIfBigIntNotAvailable( `Unable to decode the provided string, due to lack of support for BigInt numbers in the current environment`, ) + + return BigInt(carry) * BigInt(alphabetChars.length) + BigInt(index) }, 0) const safeToParseNumberRegExp = /^\+?\d+$/ -export const safeParseInt10 = (str: string) => - safeToParseNumberRegExp.test(str) ? Number.parseInt(str, 10) : Number.NaN +export const safeParseInt10 = (str: string) => { + if (!safeToParseNumberRegExp.test(str)) { + return Number.NaN + } + + const int10 = Number.parseInt(str, 10) + + if (Number.isSafeInteger(int10)) { + return int10 + } + + throwIfBigIntNotAvailable( + 'Unable to encode the provided BigInt string without loss of information due to lack of support for BigInt type in the current environment', + ) + + return BigInt(str) +} export const splitAtIntervalAndMap = ( str: string, @@ -137,3 +151,11 @@ export const makeAtLeastSomeCharRegExp = (chars: string[]) => const escapeRegExp = (text: string) => text.replace(/[\s#$()*+,.?[\\\]^{|}-]/g, '\\$&') + +const throwIfBigIntNotAvailable = ( + errorMessage: string = 'BigInt is not available in this environment', +) => { + if (typeof BigInt !== 'function') { + throw new TypeError(errorMessage) + } +}