Fix parser to return range for entire array when accessing array field (#212)

When calling parser.ranges.body('a', {type: 'json'}) for a body like
{a: [{b: 1}, {c: 2}]}, the parser now returns the range for the entire
array value instead of returning empty.

Changes:
- Store array field with full range (key + value) before processing elements
- hideKey option returns just the array value: [{b: 1}, {c: 2}]
- hideValue option returns just the key: "a"

Added tests for:
- Extracting entire array value directly
- hideKey/hideValue options for array fields
- Array of primitives
- Nested array fields (e.g., data.items)
This commit is contained in:
tsukino
2026-01-06 19:16:14 +08:00
committed by GitHub
parent 5a3a844527
commit eb18485361
2 changed files with 106 additions and 1 deletions

View File

@@ -929,6 +929,85 @@ describe('Parser', () => {
expect(ranges).toHaveLength(0);
});
it('should extract entire array value when accessing array field directly', () => {
const request =
'POST /api HTTP/1.1\r\n' +
'Content-Type: application/json\r\n' +
'\r\n' +
'{"a":[{"b":1},{"c":2}]}';
const parser = new Parser(request);
const ranges = parser.ranges.body('a', { type: 'json' });
expect(ranges).toHaveLength(1);
const extracted = request.substring(ranges[0].start, ranges[0].end);
// Should include the key and the entire array value
expect(extracted).toContain('"a"');
expect(extracted).toContain('[{"b":1},{"c":2}]');
});
it('should extract only array value with hideKey option', () => {
const request =
'POST /api HTTP/1.1\r\n' +
'Content-Type: application/json\r\n' +
'\r\n' +
'{"a":[{"b":1},{"c":2}]}';
const parser = new Parser(request);
const ranges = parser.ranges.body('a', { type: 'json', hideKey: true });
expect(ranges).toHaveLength(1);
const extracted = request.substring(ranges[0].start, ranges[0].end);
expect(extracted).toBe('[{"b":1},{"c":2}]');
expect(extracted).not.toContain('"a"');
});
it('should extract only array key with hideValue option', () => {
const request =
'POST /api HTTP/1.1\r\n' +
'Content-Type: application/json\r\n' +
'\r\n' +
'{"a":[{"b":1},{"c":2}]}';
const parser = new Parser(request);
const ranges = parser.ranges.body('a', { type: 'json', hideValue: true });
expect(ranges).toHaveLength(1);
const extracted = request.substring(ranges[0].start, ranges[0].end);
expect(extracted).toBe('"a"');
expect(extracted).not.toContain('[');
});
it('should extract array of primitives', () => {
const request =
'POST /api HTTP/1.1\r\n' +
'Content-Type: application/json\r\n' +
'\r\n' +
'{"numbers":[1,2,3,4,5]}';
const parser = new Parser(request);
const ranges = parser.ranges.body('numbers', { type: 'json', hideKey: true });
expect(ranges).toHaveLength(1);
const extracted = request.substring(ranges[0].start, ranges[0].end);
expect(extracted).toBe('[1,2,3,4,5]');
});
it('should extract nested array field', () => {
const request =
'POST /api HTTP/1.1\r\n' +
'Content-Type: application/json\r\n' +
'\r\n' +
'{"data":{"items":[1,2,3]}}';
const parser = new Parser(request);
const ranges = parser.ranges.body('data.items', { type: 'json', hideKey: true });
expect(ranges).toHaveLength(1);
const extracted = request.substring(ranges[0].start, ranges[0].end);
expect(extracted).toBe('[1,2,3]');
});
});
describe('Mixed Paths (Objects and Arrays)', () => {

View File

@@ -425,7 +425,33 @@ export class Parser {
// Handle different value types
if (typeof value === 'object' && value !== null) {
if (Array.isArray(value)) {
// Handle array
// Handle array - first store the array itself, then process elements
const valueStr = JSON.stringify(value);
const valueBytes = Buffer.from(valueStr, 'utf8');
const valueByteIndex = textBytes.indexOf(valueBytes, actualValueByteStart);
if (valueByteIndex !== -1) {
const valueByteEnd = valueByteIndex + valueBytes.length;
// Store the array itself as a field
result[pathKey] = {
value: value,
ranges: {
start: baseOffset + keyByteIndex,
end: baseOffset + valueByteEnd,
},
keyRange: {
start: baseOffset + keyByteIndex,
end: baseOffset + keyByteIndex + keyBytes.length,
},
valueRange: {
start: baseOffset + valueByteIndex,
end: baseOffset + valueByteEnd,
},
};
}
// Then recursively process array elements
this.processJsonArray(
value,
textBytes,