From 3a3119022835ce58a1ff03c529434b86b10bb360 Mon Sep 17 00:00:00 2001 From: marko1olo Date: Sat, 22 Aug 2026 21:44:59 +0400 Subject: [PATCH] fix: serialize BigInt in integer anyOf/oneOf and multi-type schemas AJV does not recognise BigInt as type 'integer', so values like 12n were always falling through to the TypeError branch in anyOf/oneOf union code, and in multi-type (type array) serializers the Number.isInteger() guard also rejects BigInt. Two targeted changes: - buildMultiTypeSerializer: add ypeof input === 'bigint' alongside Number.isInteger() in the integer branch condition. - buildOneOf: when an option schema declares type 'integer', prefix the AJV validator.validate() call with a BigInt type-guard so BigInt values are correctly routed to asInteger() (which already handles BigInt). asInteger() in lib/serializer.js already converts BigInt to string via .toString(), so no change is needed there. Fixes #501 --- index.js | 13 +++++++++++-- test/typesArray.test.js | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index e692d5e9..aa087805 100644 --- a/index.js +++ b/index.js @@ -946,7 +946,7 @@ function buildMultiTypeSerializer (context, location, input) { } case 'integer': { code += ` - ${statement}(Number.isInteger(${input}) || ${input} === null) { + ${statement}(Number.isInteger(${input}) || typeof ${input} === 'bigint' || ${input} === null) { ${nestedResult} } ` @@ -1214,8 +1214,17 @@ function buildOneOf (context, location, input) { const schemaRef = getValidatorSchemaRef(context, optionLocation) context.validatorSchemaRefs.add(schemaRef) + // AJV does not recognise BigInt as type "integer". + // Pre-route BigInt to the first integer-typed schema so it reaches + // asInteger() (which correctly handles bigint) instead of falling + // through to the TypeError branch. + const isIntegerSchema = optionSchema.type === 'integer' + const condition = isIntegerSchema + ? `typeof ${input} === 'bigint' || validator.validate("${schemaRef}", ${input})` + : `validator.validate("${schemaRef}", ${input})` + code += ` - ${index === 0 ? 'if' : 'else if'}(validator.validate("${schemaRef}", ${input})) { + ${index === 0 ? 'if' : 'else if'}(${condition}) { ${nestedResult} } ` diff --git a/test/typesArray.test.js b/test/typesArray.test.js index dc09d9ba..a9e0b86a 100644 --- a/test/typesArray.test.js +++ b/test/typesArray.test.js @@ -66,6 +66,25 @@ test('possibly nullable integer primitive alternative with null value', (t) => { t.assert.equal(value, '{"data":0}') }) + +test('bigint serialized as integer in type array [integer, null]', (t) => { + t.plan(3) + const schema = { type: 'object', properties: { data: { type: ['integer', 'null'] } } } + const stringify = build(schema) + t.assert.equal(stringify({ data: 12n }), '{"data":12}') + t.assert.equal(stringify({ data: -5n }), '{"data":-5}') + t.assert.equal(stringify({ data: null }), '{"data":null}') +}) + +test('bigint serialized as integer in anyOf union', (t) => { + t.plan(3) + const schema = { type: 'object', properties: { data: { anyOf: [{ type: 'integer' }, { type: 'null' }] } } } + const stringify = build(schema) + t.assert.equal(stringify({ data: 12n }), '{"data":12}') + t.assert.equal(stringify({ data: -5n }), '{"data":-5}') + t.assert.equal(stringify({ data: null }), '{"data":null}') +}) + test('possibly nullable number primitive alternative with null value', (t) => { t.plan(1)