@@ -214,9 +214,12 @@ var parseObject = function (chain, val, options, valuesParsed) {
214214 return leaf ;
215215} ;
216216
217- var splitKeyIntoSegments = function splitKeyIntoSegments ( givenKey , options ) {
218- var key = options . allowDots ? givenKey . replace ( / \. ( [ ^ . [ ] + ) / g, '[$1]' ) : givenKey ;
217+ // Split a key like "a[b][c[]]" into ['a', '[b]', '[c[]]'] while preserving
218+ // qs parse semantics for depth/prototype guards.
219+ var splitKeyIntoSegments = function splitKeyIntoSegments ( originalKey , options ) {
220+ var key = options . allowDots ? originalKey . replace ( / \. ( [ ^ . [ ] + ) / g, '[$1]' ) : originalKey ;
219221
222+ // depth <= 0 keeps the whole key as one segment
220223 if ( options . depth <= 0 ) {
221224 if ( ! options . plainObjects && has . call ( Object . prototype , key ) ) {
222225 if ( ! options . allowPrototypes ) {
@@ -227,47 +230,74 @@ var splitKeyIntoSegments = function splitKeyIntoSegments(givenKey, options) {
227230 return [ key ] ;
228231 }
229232
230- var brackets = / ( \[ [ ^ [ \] ] * ] ) / ;
231- var child = / ( \[ [ ^ [ \] ] * ] ) / g;
232-
233- var segment = brackets . exec ( key ) ;
234- var parent = segment ? key . slice ( 0 , segment . index ) : key ;
235-
236- var keys = [ ] ;
233+ var segments = [ ] ;
237234
235+ // parent before the first '[' (may be empty if key starts with '[')
236+ var first = key . indexOf ( '[' ) ;
237+ var parent = first >= 0 ? key . slice ( 0 , first ) : key ;
238238 if ( parent ) {
239239 if ( ! options . plainObjects && has . call ( Object . prototype , parent ) ) {
240240 if ( ! options . allowPrototypes ) {
241241 return ;
242242 }
243243 }
244244
245- keys [ keys . length ] = parent ;
245+ segments [ segments . length ] = parent ;
246246 }
247247
248- var i = 0 ;
249- while ( ( segment = child . exec ( key ) ) !== null && i < options . depth ) {
250- i += 1 ;
251-
252- var segmentContent = segment [ 1 ] . slice ( 1 , - 1 ) ;
253- if ( ! options . plainObjects && has . call ( Object . prototype , segmentContent ) ) {
254- if ( ! options . allowPrototypes ) {
255- return ;
248+ var n = key . length ;
249+ var open = first ;
250+ var collected = 0 ;
251+
252+ while ( open >= 0 && collected < options . depth ) {
253+ var level = 1 ;
254+ var i = open + 1 ;
255+ var close = - 1 ;
256+
257+ // balance nested '[' and ']' inside this bracket group using a nesting level counter
258+ while ( i < n && close < 0 ) {
259+ var cu = key . charCodeAt ( i ) ;
260+ if ( cu === 0x5B ) { // '['
261+ level += 1 ;
262+ } else if ( cu === 0x5D ) { // ']'
263+ level -= 1 ;
264+ if ( level === 0 ) {
265+ close = i ; // found matching close; loop will exit by condition
266+ }
256267 }
268+ i += 1 ;
257269 }
258270
259- keys [ keys . length ] = segment [ 1 ] ;
271+ if ( close < 0 ) {
272+ // Unterminated group: wrap the raw remainder in one bracket pair so it stays
273+ // a single literal segment (e.g. "[[]b" -> "[[]b]"); we do not infer missing ']'.
274+ segments [ segments . length ] = '[' + key . slice ( open ) + ']' ;
275+ return segments ;
276+ }
277+
278+ var seg = key . slice ( open , close + 1 ) ;
279+ // prototype guard for the content of this group
280+ var content = seg . slice ( 1 , - 1 ) ;
281+ if ( ! options . plainObjects && has . call ( Object . prototype , content ) && ! options . allowPrototypes ) {
282+ return ;
283+ }
284+
285+ segments [ segments . length ] = seg ;
286+ collected += 1 ;
287+
288+ // find the next '[' after this balanced group
289+ open = key . indexOf ( '[' , close + 1 ) ;
260290 }
261291
262- if ( segment ) {
292+ if ( open >= 0 ) {
263293 if ( options . strictDepth === true ) {
264294 throw new RangeError ( 'Input depth exceeded depth option of ' + options . depth + ' and strictDepth is true' ) ;
265295 }
266296
267- keys [ keys . length ] = '[' + key . slice ( segment . index ) + ']' ;
297+ segments [ segments . length ] = '[' + key . slice ( open ) + ']' ;
268298 }
269299
270- return keys ;
300+ return segments ;
271301} ;
272302
273303var parseKeys = function parseQueryStringKeys ( givenKey , val , options , valuesParsed ) {
0 commit comments