Skip to content

Commit 02a697c

Browse files
committed
feat: 🎸 Throws when number exceeds safe integer range
1 parent fbbcbad commit 02a697c

4 files changed

Lines changed: 30 additions & 18 deletions

File tree

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "json-web3",
3-
"version": "1.3.1",
3+
"version": "1.3.2",
44
"description": "BigInt-safe JSON serialization and deserialization for Web3 use cases.",
55
"keywords": [
66
"json",
@@ -36,9 +36,10 @@
3636
],
3737
"scripts": {
3838
"build": "tsup",
39-
"test": "vitest run --coverage",
4039
"lint": "prettier . --write",
4140
"prepublishOnly": "pnpm run build",
41+
"publishOnly": "pnpm run build",
42+
"test": "vitest run --coverage",
4243
"test:watch": "vitest"
4344
},
4445
"devDependencies": {

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
SET_TAG,
1818
toSerializable,
1919
TYPEDARRAY_TAG,
20-
undefined,
20+
udf,
2121
URL_TAG,
2222
} from './utils'
2323

@@ -45,7 +45,7 @@ const applyReplacer = (holder: any, key: string, value: any, replacer: Replacer)
4545
if (isArrayBufferPayload(holder)) return value
4646
if (isRegExpPayload(holder)) return value
4747
if (isArray(holder)) return value
48-
return replacer.includes(key) ? value : undefined
48+
return replacer.includes(key) ? value : udf
4949
}
5050
return value
5151
}

src/utils.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const FUNCTION_TAG = '__@json.function__'
1010
export const NUMBER_TAG = '__@json.number__'
1111

1212
const UDF = 'undefined'
13-
export const undefined = void UDF
13+
export const udf = void UDF
1414

1515
export const isString = (value: any): value is string => typeof value === 'string'
1616
export const hasOwnProperty = (obj: any, prop: string): boolean =>
@@ -25,8 +25,9 @@ export const isMap = (value: any): value is Map<any, any> => value instanceof Ma
2525
export const isSet = (value: any): value is Set<any> => value instanceof Set
2626
export const isRegExp = (value: any): value is RegExp => value instanceof RegExp
2727
export const isURL = (value: any): value is URL => typeof URL !== UDF && value instanceof URL
28+
const isNumber = (value: any): value is number => typeof value === 'number'
2829
export const isNonFiniteNumber = (value: any): value is number =>
29-
typeof value === 'number' && !Number.isFinite(value)
30+
isNumber(value) && !Number.isFinite(value)
3031
export const isInteger = (value: any): value is number => Number.isInteger(value)
3132
const hasBuffer = (): boolean => typeof Buffer !== UDF && isFunction(Buffer.isBuffer)
3233

@@ -54,22 +55,22 @@ export const getTypedArrayName = (value: any): string | null => {
5455
}
5556

5657
const TYPEDARRAY_CTORS: Record<string, any> = {
57-
Int8Array: typeof Int8Array !== UDF ? Int8Array : undefined,
58-
Uint8Array: typeof Uint8Array !== UDF ? Uint8Array : undefined,
59-
Uint8ClampedArray: typeof Uint8ClampedArray !== UDF ? Uint8ClampedArray : undefined,
58+
Int8Array: typeof Int8Array !== UDF ? Int8Array : udf,
59+
Uint8Array: typeof Uint8Array !== UDF ? Uint8Array : udf,
60+
Uint8ClampedArray: typeof Uint8ClampedArray !== UDF ? Uint8ClampedArray : udf,
6061

61-
Int16Array: typeof Int16Array !== UDF ? Int16Array : undefined,
62-
Uint16Array: typeof Uint16Array !== UDF ? Uint16Array : undefined,
63-
Float16Array: typeof Float16Array !== UDF ? Float16Array : undefined,
62+
Int16Array: typeof Int16Array !== UDF ? Int16Array : udf,
63+
Uint16Array: typeof Uint16Array !== UDF ? Uint16Array : udf,
64+
Float16Array: typeof Float16Array !== UDF ? Float16Array : udf,
6465

65-
Int32Array: typeof Int32Array !== UDF ? Int32Array : undefined,
66-
Uint32Array: typeof Uint32Array !== UDF ? Uint32Array : undefined,
67-
Float32Array: typeof Float32Array !== UDF ? Float32Array : undefined,
66+
Int32Array: typeof Int32Array !== UDF ? Int32Array : udf,
67+
Uint32Array: typeof Uint32Array !== UDF ? Uint32Array : udf,
68+
Float32Array: typeof Float32Array !== UDF ? Float32Array : udf,
6869

69-
Float64Array: typeof Float64Array !== UDF ? Float64Array : undefined,
70+
Float64Array: typeof Float64Array !== UDF ? Float64Array : udf,
7071

71-
BigInt64Array: typeof BigInt64Array !== UDF ? BigInt64Array : undefined,
72-
BigUint64Array: typeof BigUint64Array !== UDF ? BigUint64Array : undefined,
72+
BigInt64Array: typeof BigInt64Array !== UDF ? BigInt64Array : udf,
73+
BigUint64Array: typeof BigUint64Array !== UDF ? BigUint64Array : udf,
7374
}
7475

7576
export const toHex = (value: Uint8Array): string => {
@@ -105,6 +106,9 @@ export const toSerializable = (value: any, options: { allowFunction?: boolean }
105106
if (isNonFiniteNumber(value)) {
106107
return { [NUMBER_TAG]: String(value) }
107108
}
109+
if (isNumber(value) && !Number.isSafeInteger(value)) {
110+
throw new Error('Number exceeds safe integer range')
111+
}
108112

109113
if (isDate(value)) {
110114
return { [DATE_TAG]: value.getTime() }

test/index.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,13 @@ describe('json-web3', () => {
167167
expect(output.ninf).toBe(Number.NEGATIVE_INFINITY)
168168
})
169169

170+
it('throws when number exceeds safe integer range', () => {
171+
expect(() => stringify({ v: Number.MAX_SAFE_INTEGER + 1 })).toThrowError(/safe integer range/i)
172+
expect(() => stringify_UNSAFE({ v: Number.MIN_SAFE_INTEGER - 1 })).toThrowError(
173+
/safe integer range/i,
174+
)
175+
})
176+
170177
it('serializes Date using timestamp and restores Date on parse', () => {
171178
const d = new Date('2020-01-02T03:04:05.006Z')
172179
const text = stringify({ d })

0 commit comments

Comments
 (0)