Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions test/util-file.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,21 @@ describe('openapi-format CLI file tests', () => {
expect(result).toEqual({name: 'John', age: 30});
});

it('should keep Number.MAX_SAFE_INTEGER as a number in YAML', async () => {
const result = await parseString(`value: ${Number.MAX_SAFE_INTEGER}`);
expect(result).toEqual({value: Number.MAX_SAFE_INTEGER});
});

it('should keep Number.MIN_SAFE_INTEGER as a number in YAML', async () => {
const result = await parseString(`value: ${Number.MIN_SAFE_INTEGER}`);
expect(result).toEqual({value: Number.MIN_SAFE_INTEGER});
});

it('should encode YAML integers beyond the safe integer range', async () => {
const result = await parseString('positive: 9007199254740993\nnegative: -9007199254740993');
expect(result).toEqual({positive: '9007199254740993===', negative: '-9007199254740993==='});
});

it('should preserve quoted JSON examples with high-precision numbers and following schemas', async () => {
const yamlString = `openapi: 3.1.0
info:
Expand Down Expand Up @@ -749,6 +764,30 @@ components:
const output = encodeLargeNumbers(input);
expect(output).toBe(input);
});

test('should not encode Number.MAX_SAFE_INTEGER', () => {
const input = `key: ${Number.MAX_SAFE_INTEGER}\n`;
const output = encodeLargeNumbers(input);
expect(output).toBe('key: 9007199254740991\n');
});

test('should not encode Number.MIN_SAFE_INTEGER', () => {
const input = `key: ${Number.MIN_SAFE_INTEGER}\n`;
const output = encodeLargeNumbers(input);
expect(output).toBe('key: -9007199254740991\n');
});

test('should not encode a 16 digit integer that fits in a double', () => {
const input = 'key: 1234567890123456\n';
const output = encodeLargeNumbers(input);
expect(output).toBe('key: 1234567890123456\n');
});

test('should encode an integer beyond Number.MAX_SAFE_INTEGER', () => {
const input = 'key: 9007199254740993\n';
const output = encodeLargeNumbers(input);
expect(output).toBe('key: "9007199254740993==="\n');
});
});

describe('addQuotesToRefInString function', () => {
Expand Down
23 changes: 20 additions & 3 deletions utils/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,24 @@ async function getRemoteFile(filePath) {
return inputContent;
}

/**
* Check whether a numeric literal survives a round-trip through `Number(...)` and
* `Number#toString()`.
* Short literals (<= 15 digits) are treated as safe; longer ones are considered unsafe
* when parsing/stringifying changes the literal or produces exponential notation.
* @param {string} source - The raw numeric literal.
* @returns {boolean} True when the literal would not round-trip as the same string.
Comment thread
dimitropoulos marked this conversation as resolved.
Comment thread
dimitropoulos marked this conversation as resolved.
*/
function isUnsafeNumberLiteral(source) {
const parsed = Number(source).toString();
if (parsed.includes('e')) return true;

const digitCount = source.replace(/[^0-9]/g, '').length;
if (digitCount <= 15) return false;

return parsed !== source;
}

/**
* Convert large number value safely before parsing
* @param inputContent Input content.
Expand All @@ -510,7 +528,7 @@ function encodeLargeNumbers(inputContent) {
const rgx = new RegExp(endChar, 'g');
const number = rawInput.replace(/: /g, '').replace(rgx, '');
// Handle large numbers safely in javascript
if (Number(number).toString().includes('e') || number.replace('.', '').length > 15) {
if (isUnsafeNumberLiteral(number)) {
return `: "${number}==="${endChar}`;
} else {
return `: ${number}${endChar}`;
Expand All @@ -535,8 +553,7 @@ function encodeLargeNumberScalars(doc) {
}

const source = value.source;
const digitCount = source.replace(/[^0-9]/g, '').length;
if (Number(source).toString().includes('e') || digitCount > 15) {
if (isUnsafeNumberLiteral(source)) {
value.value = `${source}===`;
value.type = 'QUOTE_DOUBLE';
}
Expand Down
Loading