Comparing version 0.5.1 to 0.6.0
315
lib/qs.js
@@ -27,17 +27,24 @@ /* | ||
"use strict" ; | ||
/* | ||
Most of this code is directly borrowed from the core Node.js 'querystring' module. | ||
If option 'autoNumber' is on, valid numbers are cast to number | ||
If option 'keyPath' is on, this modification allow object dot notation and left-hand-side array notation: | ||
a.b=1&a.c=2 -> { a: { b: '1' , c: '2' } } | ||
a[0]=one&a[1]=two -> { a: [ 'one' , 'two' ] } | ||
a.b=1&a.c=2 -> { a: { b: '1' , c: '2' } } | ||
a[0]=one&a[1]=two -> { a: [ 'one' , 'two' ] } | ||
If option 'brackets' is on: | ||
a=[b,c,d] -> { a: [ 'b' , 'c' , 'd' ] } | ||
a=[b,c,d] -> { a: [ 'b' , 'c' , 'd' ] } | ||
If option 'autoPush' is on: | ||
a=b&a=c&a=d -> { a: [ 'b' , 'c' , 'd' ] } | ||
a=b&a=c&a=d -> { a: [ 'b' , 'c' , 'd' ] } | ||
If option 'restQueryFilter' is on: | ||
.path.to.prop.$lt=10 -> { "path.to.prop": { $lt: 10 } } | ||
Still, not everything is possible. | ||
@@ -48,13 +55,13 @@ */ | ||
'use strict' ; | ||
const tree = require( 'tree-kit' ) ; | ||
// Load modules | ||
var tree = require( 'tree-kit' ) ; | ||
const QueryString = {} ; | ||
module.exports = QueryString ; | ||
var QueryString = {} ; | ||
module.exports = QueryString ; | ||
const partRe = /\+/g ; | ||
const numberRe = /^-?[0-9]+(?:\.[0-9]+)?$/ ; | ||
@@ -68,16 +75,15 @@ | ||
*/ | ||
QueryString.parse = QueryString.decode = function( str , options ) { | ||
if ( ! options ) { options = {} ; } | ||
QueryString.parse = QueryString.decode = function( str , options = {} ) { | ||
var i , iMax , j , jMax , count = 0 , | ||
strParts , current , | ||
key , value , operator , | ||
indexOfEq , indexOfDotDollar , | ||
object = {} ; | ||
//console.log( "QS-kit: " , str , options ) ; | ||
var i , iMax , j , jMax , x , idx , k , v , count = 0 , obj = {} ; | ||
var sep = options.separator || '&' ; | ||
var eq = options.equal || '=' ; | ||
if ( typeof str !== 'string' || str.length === 0 ) { return obj ; } | ||
if ( typeof str !== 'string' || str.length === 0 ) { return object ; } | ||
var regexp = /\+/g ; | ||
str = str.split( sep ) ; | ||
strParts = str.split( sep ) ; | ||
@@ -87,35 +93,49 @@ var maxItems = 1000 ; | ||
iMax = str.length ; | ||
iMax = strParts.length ; | ||
var decode = QueryString.unescape ; | ||
for ( i = 0 ; i < iMax && count < maxItems ; i ++ , count ++ ) { | ||
current = strParts[ i ].replace( partRe , '%20' ) ; | ||
indexOfEq = current.indexOf( eq ) ; | ||
if ( typeof options.decodeURIComponent === 'function' ) { | ||
decode = options.decodeURIComponent ; | ||
} | ||
if ( indexOfEq === -1 ) { continue ; } | ||
for ( i = 0 ; i < iMax && count < maxItems ; i ++ , count ++ ) { | ||
x = str[ i ].replace( regexp , '%20' ) ; | ||
idx = x.indexOf( eq ) ; | ||
key = decodeURIComponent( current.substring( 0 , indexOfEq ) ) ; | ||
if ( idx === -1 ) { continue ; } | ||
if ( ! key ) { throw Error( "Empty key" ) ; } | ||
k = decodeStr( x.substring( 0 , idx ) , decode ) ; | ||
//console.log( "key:",key ) ; | ||
if ( ! k ) { throw Error( "Empty key" ) ; } | ||
value = current.substring( indexOfEq + 1 ) ; | ||
//console.log( "k:",k ) ; | ||
if ( options.restQueryFilter && key[ 0 ] === '.' ) { | ||
if ( options.autoNumber && numberRe.test( value ) ) { value = parseFloat( value ) ; } | ||
v = x.substring( idx + 1 ) ; | ||
indexOfDotDollar = key.lastIndexOf( '.$' ) ; | ||
if ( options.brackets && v[ 0 ] === '[' && v[ v.length - 1 ] === ']' ) { | ||
if ( v.length === 2 ) { | ||
if ( indexOfDotDollar >= 0 ) { | ||
operator = key.slice( indexOfDotDollar + 1 ) ; | ||
key = key.slice( 1 , indexOfDotDollar ) ; | ||
object[ key ] = {} ; | ||
object[ key ][ operator ] = value ; | ||
} | ||
else { | ||
key = key.slice( 1 ) ; | ||
object[ key ] = value ; | ||
} | ||
continue ; | ||
} | ||
if ( options.brackets && value[ 0 ] === '[' && value[ value.length - 1 ] === ']' ) { | ||
if ( value.length === 2 ) { | ||
// This is an empty array | ||
v = [] ; | ||
value = [] ; | ||
} | ||
else { | ||
v = v.substring( 1 , v.length - 1 ).split( ',' ) ; | ||
jMax = v.length ; | ||
value = value.substring( 1 , value.length - 1 ).split( ',' ) ; | ||
jMax = value.length ; | ||
for ( j = 0 ; j < jMax && count < maxItems ; j ++ , count ++ ) { | ||
v[ j ] = decodeStr( v[ j ] , decode ) ; | ||
value[ j ] = decodeURIComponent( value[ j ] ) ; | ||
if ( options.autoNumber && numberRe.test( value[ j ] ) ) { value[ j ] = parseFloat( value[ j ] ) ; } | ||
} | ||
@@ -125,16 +145,23 @@ } | ||
else { | ||
v = decodeStr( v , decode ) ; | ||
value = decodeURIComponent( value ) ; | ||
if ( options.autoNumber && numberRe.test( value ) ) { value = parseFloat( value ) ; } | ||
} | ||
//console.log( "tree.path.set( obj , k , v ):\nobj:" , obj , "\nk:" , k , "\nv:" , v ) ; | ||
//console.log( "tree.path.set( object , k , value ):\nobject:" , object , "\nk:" , k , "\nvalue:" , value ) ; | ||
if ( options.keyPath ) { | ||
if ( options.autoPush ) { tree.path.autoPush( obj , k , v ) ; } | ||
else { tree.path.set( obj , k , v ) ; } | ||
if ( options.autoPush ) { tree.path.autoPush( object , key , value ) ; } | ||
else { tree.path.set( object , key , value ) ; } | ||
} | ||
else if ( obj[ k ] === undefined || ! options.autoPush ) { obj[ k ] = v ; } | ||
else if ( Array.isArray( obj[ k ] ) ) { obj[ k ].push( v ) ; } | ||
else { obj[ k ] = [ obj[ k ] , v ] ; } | ||
else if ( object[ key ] === undefined || ! options.autoPush ) { | ||
object[ key ] = value ; | ||
} | ||
else if ( Array.isArray( object[ key ] ) ) { | ||
object[ key ].push( value ) ; | ||
} | ||
else { | ||
object[ key ] = [ object[ key ] , value ] ; | ||
} | ||
} | ||
return obj ; | ||
return object ; | ||
} ; | ||
@@ -146,5 +173,3 @@ | ||
QueryString.stringify = QueryString.encode = function( obj , options ) { | ||
if ( ! options ) { options = {} ; } | ||
QueryString.stringify = QueryString.encode = function( object , options = {} ) { | ||
var i , iMax , j , k , v , ks , keys , | ||
@@ -155,10 +180,7 @@ lastField , fields = '' , vLength , vLast ; | ||
var eq = options.equal || '=' ; | ||
var encode = QueryString.escape ; | ||
obj = tree.extend( EXTEND_OPTIONS , {} , obj ) ; | ||
object = tree.extend( EXTEND_OPTIONS , {} , object ) ; | ||
if ( typeof options.encodeURIComponent === 'function' ) { encode = options.encodeURIComponent ; } | ||
if ( obj !== null && typeof obj === 'object' ) { | ||
keys = Object.keys( obj ) ; | ||
if ( object !== null && typeof object === 'object' ) { | ||
keys = Object.keys( object ) ; | ||
iMax = keys.length ; | ||
@@ -169,4 +191,4 @@ lastField = iMax - 1 ; | ||
k = keys[ i ] ; | ||
v = obj[ k ] ; | ||
ks = encode( stringifyPrimitive( k ) ) ; | ||
v = object[ k ] ; | ||
ks = encodeURIComponent( stringifyPrimitive( k ) ) ; | ||
@@ -181,3 +203,3 @@ if ( Array.isArray( v ) ) { | ||
for ( j = 0 ; j < vLength ; j ++ ) { | ||
fields += encode( stringifyPrimitive( v[ j ] ) ) ; | ||
fields += encodeURIComponent( stringifyPrimitive( v[ j ] ) ) ; | ||
if ( j < vLast ) { fields += ',' ; } | ||
@@ -195,3 +217,3 @@ } | ||
for ( j = 0 ; j < vLength ; j ++ ) { | ||
fields += ks + '[' + j + ']' + eq + encode( stringifyPrimitive( v[ j ] ) ) ; | ||
fields += ks + '[' + j + ']' + eq + encodeURIComponent( stringifyPrimitive( v[ j ] ) ) ; | ||
if ( j < vLast ) { fields += '&' ; } | ||
@@ -204,3 +226,3 @@ } | ||
else { | ||
fields += ks + eq + encode( stringifyPrimitive( v ) ) ; | ||
fields += ks + eq + encodeURIComponent( stringifyPrimitive( v ) ) ; | ||
if ( i < lastField ) { fields += sep ; } | ||
@@ -218,173 +240,8 @@ } | ||
// Unmodified part, from Node.js v4.1.1 | ||
// Turn off eslint, since it's not my code... | ||
/* eslint-disable */ | ||
function charCode(c) { | ||
return c.charCodeAt(0); | ||
function stringifyPrimitive( v ) { | ||
if ( typeof v === 'string' ) { return v ; } | ||
if ( typeof v === 'number' && isFinite( v ) ) { return '' + v ; } | ||
if ( typeof v === 'boolean' ) { return v ? 'true' : 'false' ; } | ||
return '' ; | ||
} | ||
// a safe fast alternative to decodeURIComponent | ||
QueryString.unescapeBuffer = function(s, decodeSpaces) { | ||
var out = Buffer.allocUnsafe(s.length); | ||
var state = 'CHAR'; // states: CHAR, HEX0, HEX1 | ||
var n, m, hexchar; | ||
for (var inIndex = 0, outIndex = 0; inIndex <= s.length; inIndex++) { | ||
var c = s.charCodeAt(inIndex); | ||
switch (state) { | ||
case 'CHAR': | ||
switch (c) { | ||
case charCode('%'): | ||
n = 0; | ||
m = 0; | ||
state = 'HEX0'; | ||
break; | ||
case charCode('+'): | ||
if (decodeSpaces) c = charCode(' '); | ||
// falls through | ||
default: | ||
out[outIndex++] = c; | ||
break; | ||
} | ||
break; | ||
case 'HEX0': | ||
state = 'HEX1'; | ||
hexchar = c; | ||
if (charCode('0') <= c && c <= charCode('9')) { | ||
n = c - charCode('0'); | ||
} else if (charCode('a') <= c && c <= charCode('f')) { | ||
n = c - charCode('a') + 10; | ||
} else if (charCode('A') <= c && c <= charCode('F')) { | ||
n = c - charCode('A') + 10; | ||
} else { | ||
out[outIndex++] = charCode('%'); | ||
out[outIndex++] = c; | ||
state = 'CHAR'; | ||
break; | ||
} | ||
break; | ||
case 'HEX1': | ||
state = 'CHAR'; | ||
if (charCode('0') <= c && c <= charCode('9')) { | ||
m = c - charCode('0'); | ||
} else if (charCode('a') <= c && c <= charCode('f')) { | ||
m = c - charCode('a') + 10; | ||
} else if (charCode('A') <= c && c <= charCode('F')) { | ||
m = c - charCode('A') + 10; | ||
} else { | ||
out[outIndex++] = charCode('%'); | ||
out[outIndex++] = hexchar; | ||
out[outIndex++] = c; | ||
break; | ||
} | ||
out[outIndex++] = 16 * n + m; | ||
break; | ||
} | ||
} | ||
// TODO support returning arbitrary buffers. | ||
return out.slice(0, outIndex - 1); | ||
}; | ||
QueryString.unescape = function(s, decodeSpaces) { | ||
try { | ||
return decodeURIComponent(s); | ||
} catch (e) { | ||
return QueryString.unescapeBuffer(s, decodeSpaces).toString(); | ||
} | ||
}; | ||
var hexTable = new Array(256); | ||
for (var i = 0; i < 256; ++i) | ||
hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); | ||
QueryString.escape = function(str) { | ||
// replaces encodeURIComponent | ||
// http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 | ||
str = '' + str; | ||
var len = str.length; | ||
var out = ''; | ||
var i, c; | ||
if (len === 0) | ||
return str; | ||
for (i = 0; i < len; ++i) { | ||
c = str.charCodeAt(i); | ||
// These characters do not need escaping (in order): | ||
// ! - . _ ~ | ||
// ' ( ) * | ||
// digits | ||
// alpha (uppercase) | ||
// alpha (lowercase) | ||
if (c === 0x21 || c === 0x2D || c === 0x2E || c === 0x5F || c === 0x7E || | ||
(c >= 0x27 && c <= 0x2A) || | ||
(c >= 0x30 && c <= 0x39) || | ||
(c >= 0x41 && c <= 0x5A) || | ||
(c >= 0x61 && c <= 0x7A)) { | ||
out += str[i]; | ||
continue; | ||
} | ||
// Other ASCII characters | ||
if (c < 0x80) { | ||
out += hexTable[c]; | ||
continue; | ||
} | ||
// Multi-byte characters ... | ||
if (c < 0x800) { | ||
out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]; | ||
continue; | ||
} | ||
if (c < 0xD800 || c >= 0xE000) { | ||
out += hexTable[0xE0 | (c >> 12)] + | ||
hexTable[0x80 | ((c >> 6) & 0x3F)] + | ||
hexTable[0x80 | (c & 0x3F)]; | ||
continue; | ||
} | ||
// Surrogate pair | ||
++i; | ||
c = 0x10000 + (((c & 0x3FF) << 10) | (str.charCodeAt(i) & 0x3FF)); | ||
out += hexTable[0xF0 | (c >> 18)] + | ||
hexTable[0x80 | ((c >> 12) & 0x3F)] + | ||
hexTable[0x80 | ((c >> 6) & 0x3F)] + | ||
hexTable[0x80 | (c & 0x3F)]; | ||
} | ||
return out; | ||
}; | ||
var stringifyPrimitive = function(v) { | ||
if (typeof v === 'string') | ||
return v; | ||
if (typeof v === 'number' && isFinite(v)) | ||
return '' + v; | ||
if (typeof v === 'boolean') | ||
return v ? 'true' : 'false'; | ||
return ''; | ||
}; | ||
function decodeStr(s, decoder) { | ||
try { | ||
return decoder(s); | ||
} catch (e) { | ||
return QueryString.unescape(s, true); | ||
} | ||
} | ||
{ | ||
"name": "qs-kit", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"description": "A query string manipulation toolbox.", | ||
@@ -5,0 +5,0 @@ "main": "lib/qs.js", |
@@ -51,2 +51,9 @@ /* | ||
it( "'autoNumber' option" , () => { | ||
var options = { autoNumber: true } ; | ||
expect( qs.parse( "a=10&b=20&c=string" , options ) ).to.equal( { a: 10 , b: 20 , c: "string" } ) ; | ||
expect( qs.parse( "a=1&b=-2&c=12.345&d=-123.45" , options ) ).to.equal( { a: 1 , b: -2 , c: 12.345 , d: -123.45 } ) ; | ||
expect( qs.parse( "a=10ab&b=2a" , options ) ).to.equal( { a: "10ab" , b: "2a" } ) ; | ||
} ) ; | ||
it( "String encoded" , () => { | ||
@@ -57,3 +64,3 @@ expect( qs.parse( "key=some%20value" ) ).to.equal( { key: "some value" } ) ; | ||
it( "Array 'autoPush' option" , () => { | ||
options = { autoPush: true } ; | ||
var options = { autoPush: true } ; | ||
expect( qs.parse( "key=one&key=two&key=three" ) ).to.equal( { key: 'three' } ) ; | ||
@@ -70,3 +77,3 @@ expect( qs.parse( "key=one&key=two&key=three" , options ) ).to.equal( { key: [ 'one' , 'two' , 'three' ] } ) ; | ||
it( "Array 'keyPath' option" , () => { | ||
options = { keyPath: true } ; | ||
var options = { keyPath: true } ; | ||
expect( qs.parse( "a[0]=val&a[1]=val2" ) ).to.equal( { "a[0]": 'val' , "a[1]": 'val2' } ) ; | ||
@@ -77,5 +84,7 @@ expect( qs.parse( "a[0]=val&a[1]=val2" , options ) ).to.equal( { a: [ 'val' , 'val2' ] } ) ; | ||
it( "Object 'keyPath' option" , () => { | ||
options = { keyPath: true } ; | ||
var options = { keyPath: true } ; | ||
expect( qs.parse( "a.b=val" ) ).to.equal( { "a.b": 'val' } ) ; | ||
expect( qs.parse( "a.b=val" , options ) ).to.equal( { a: { b: 'val' } } ) ; | ||
expect( qs.parse( "a.b=val&a.c=ue" , options ) ).to.equal( { a: { b: 'val' , c: 'ue' } } ) ; | ||
expect( qs.parse( "a.b=val&c.d=ue" , options ) ).to.equal( { a: { b: 'val' } , c: { d: 'ue' } } ) ; | ||
} ) ; | ||
@@ -89,2 +98,8 @@ | ||
} ) ; | ||
it( "'restQueryFilter' option" , () => { | ||
var options = { restQueryFilter: true , autoNumber: true } ; | ||
expect( qs.parse( ".path.to.prop=value" , options ) ).to.equal( { "path.to.prop": "value" } ) ; | ||
expect( qs.parse( ".path.to.prop.$lt=10" , options ) ).to.equal( { "path.to.prop": { $lt: 10 } } ) ; | ||
} ) ; | ||
} ) ; | ||
@@ -115,5 +130,1 @@ | ||
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
22430
391