Skip to content

Commit

Permalink
Add stringifyJsonWithBigInts helper
Browse files Browse the repository at this point in the history
  • Loading branch information
lorisleiva committed Sep 10, 2024
1 parent 7e84995 commit 42cece0
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { stringifyJsonWithBigints } from '../stringify-json-with-bigints';

const MAX_SAFE_INTEGER = BigInt(Number.MAX_SAFE_INTEGER);
const MAX_SAFE_INTEGER_PLUS_ONE = BigInt(Number.MAX_SAFE_INTEGER) + 1n;

describe('stringifyJsonWithBigints', () => {
it.each`
input | expectedString
${0n} | ${'0'}
${-0n} | ${'0'}
${1n} | ${'1'}
${-1n} | ${'-1'}
${42n} | ${'42'}
${-42n} | ${'-42'}
${100000n} | ${'100000'}
${-100000n} | ${'-100000'}
${123n * 10n ** 32n} | ${'12300000000000000000000000000000000'}
${-123n * 10n ** 32n} | ${'-12300000000000000000000000000000000'}
${MAX_SAFE_INTEGER} | ${MAX_SAFE_INTEGER.toString()}
${MAX_SAFE_INTEGER_PLUS_ONE} | ${MAX_SAFE_INTEGER_PLUS_ONE.toString()}
`('strigifies bigint $input as a numerical value', ({ expectedString, input }) => {
expect(stringifyJsonWithBigints(input)).toBe(expectedString);
});
it('strigifies BigInts within nested structures', () => {
const input = {
alice: 42n,
bob: [3.14, BigInt(3e8), { baz: 1234567890123456789012345678901234567890n }],
};
expect(stringifyJsonWithBigints(input)).toBe(
'{"alice":42,"bob":[3.14,300000000,{"baz":1234567890123456789012345678901234567890}]}',
);
});
it.each`
input | expectedString
${0.5} | ${'0.5'}
${-0.5} | ${'-0.5'}
${3.14159265} | ${'3.14159265'}
${-3.14159265} | ${'-3.14159265'}
${1e-5} | ${'0.00001'}
${-1e-5} | ${'-0.00001'}
${1e-32} | ${'1e-32'}
${-1189e-32} | ${'-1.189e-29'}
`('strigifies number $input as a numerical value', ({ expectedString, input }) => {
expect(stringifyJsonWithBigints(input)).toBe(expectedString);
});
it.each([
null,
false,
true,
[],
[null, true, false],
{},
{ foo: 'bar' },
'',
'Hello World',
'42 apples',
'base64',
'"base64',
'""base64',
'\\base64',
'\\"base64',
'\\""base64',
'\\\\base64',
'\\\\"base64',
'\\\\""base64',
'He said: "I will eat 3 bananas"',
{ message_100: 'Hello to the 1st World' },
{ message_200: 'Hello to the "2nd World"' },
{ data: ['', 'base64'] },
])('does not alter the value of %s', input => {
expect(stringifyJsonWithBigints(input)).toBe(JSON.stringify(input));
});
});
22 changes: 22 additions & 0 deletions packages/rpc-transport-http/src/stringify-json-with-bigints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Transforms a value into a JSON string, whilst rendering bigints as large unsafe integers.
*/
export function stringifyJsonWithBigints(value: unknown, space?: number | string): string {
return unwrapBigIntValueObject(
JSON.stringify(value, (_, v) => (typeof v === 'bigint' ? wrapBigIntValueObject(v) : v), space),
);
}

type BigIntValueObject = {
// `$` implies 'this is a value object'.
// `n` implies 'interpret the value as a bigint'.
$n: string;
};

function wrapBigIntValueObject(value: bigint): BigIntValueObject {
return { $n: `${value}` };
}

function unwrapBigIntValueObject(value: string): string {
return value.replace(/\{\s*"\$n"\s*:\s*"(-?\d+)"\s*\}/g, '$1');
}

0 comments on commit 42cece0

Please sign in to comment.