axios 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of axios might be problematic. Click here for more details.

package/index.d.ts CHANGED
@@ -29,13 +29,16 @@ export type Method =
29
29
  | 'post' | 'POST'
30
30
  | 'put' | 'PUT'
31
31
  | 'patch' | 'PATCH'
32
-
33
- export type ResponseType =
34
- | 'arraybuffer'
35
- | 'blob'
36
- | 'document'
37
- | 'json'
38
- | 'text'
32
+ | 'purge' | 'PURGE'
33
+ | 'link' | 'LINK'
34
+ | 'unlink' | 'UNLINK'
35
+
36
+ export type ResponseType =
37
+ | 'arraybuffer'
38
+ | 'blob'
39
+ | 'document'
40
+ | 'json'
41
+ | 'text'
39
42
  | 'stream'
40
43
 
41
44
  export interface AxiosRequestConfig {
@@ -49,22 +52,25 @@ export interface AxiosRequestConfig {
49
52
  paramsSerializer?: (params: any) => string;
50
53
  data?: any;
51
54
  timeout?: number;
55
+ timeoutErrorMessage?: string;
52
56
  withCredentials?: boolean;
53
57
  adapter?: AxiosAdapter;
54
58
  auth?: AxiosBasicCredentials;
55
59
  responseType?: ResponseType;
56
60
  xsrfCookieName?: string;
57
61
  xsrfHeaderName?: string;
58
- onUploadProgress?: (progressEvent: any) => void;
59
- onDownloadProgress?: (progressEvent: any) => void;
62
+ onUploadProgress?: (progressEvent: ProgressEvent) => void;
63
+ onDownloadProgress?: (progressEvent: ProgressEvent) => void;
60
64
  maxContentLength?: number;
61
- validateStatus?: (status: number) => boolean;
65
+ validateStatus?: ((status: number) => boolean | null);
66
+ maxBodyLength?: number;
62
67
  maxRedirects?: number;
63
68
  socketPath?: string | null;
64
69
  httpAgent?: any;
65
70
  httpsAgent?: any;
66
71
  proxy?: AxiosProxyConfig | false;
67
72
  cancelToken?: CancelToken;
73
+ decompress?: boolean;
68
74
  }
69
75
 
70
76
  export interface AxiosResponse<T = any> {
@@ -82,6 +88,7 @@ export interface AxiosError<T = any> extends Error {
82
88
  request?: any;
83
89
  response?: AxiosResponse<T>;
84
90
  isAxiosError: boolean;
91
+ toJSON: () => object;
85
92
  }
86
93
 
87
94
  export interface AxiosPromise<T = any> extends Promise<AxiosResponse<T>> {
@@ -133,6 +140,7 @@ export interface AxiosInstance {
133
140
  get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
134
141
  delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
135
142
  head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
143
+ options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>;
136
144
  post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
137
145
  put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
138
146
  patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
@@ -2,6 +2,7 @@
2
2
 
3
3
  var utils = require('./../utils');
4
4
  var settle = require('./../core/settle');
5
+ var buildFullPath = require('../core/buildFullPath');
5
6
  var buildURL = require('./../helpers/buildURL');
6
7
  var http = require('http');
7
8
  var https = require('https');
@@ -18,13 +19,10 @@ var isHttps = /https:?/;
18
19
  /*eslint consistent-return:0*/
19
20
  module.exports = function httpAdapter(config) {
20
21
  return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
21
- var timer;
22
22
  var resolve = function resolve(value) {
23
- clearTimeout(timer);
24
23
  resolvePromise(value);
25
24
  };
26
25
  var reject = function reject(value) {
27
- clearTimeout(timer);
28
26
  rejectPromise(value);
29
27
  };
30
28
  var data = config.data;
@@ -64,7 +62,8 @@ module.exports = function httpAdapter(config) {
64
62
  }
65
63
 
66
64
  // Parse url
67
- var parsed = url.parse(config.url);
65
+ var fullPath = buildFullPath(config.baseURL, config.url);
66
+ var parsed = url.parse(fullPath);
68
67
  var protocol = parsed.protocol || 'http:';
69
68
 
70
69
  if (!auth && parsed.auth) {
@@ -86,6 +85,7 @@ module.exports = function httpAdapter(config) {
86
85
  method: config.method.toUpperCase(),
87
86
  headers: headers,
88
87
  agent: agent,
88
+ agents: { http: config.httpAgent, https: config.httpsAgent },
89
89
  auth: auth
90
90
  };
91
91
 
@@ -118,8 +118,7 @@ module.exports = function httpAdapter(config) {
118
118
  return true;
119
119
  }
120
120
  if (proxyElement[0] === '.' &&
121
- parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement &&
122
- proxyElement.match(/\./g).length === parsed.hostname.match(/\./g).length) {
121
+ parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement) {
123
122
  return true;
124
123
  }
125
124
 
@@ -172,8 +171,8 @@ module.exports = function httpAdapter(config) {
172
171
  transport = isHttpsProxy ? httpsFollow : httpFollow;
173
172
  }
174
173
 
175
- if (config.maxContentLength && config.maxContentLength > -1) {
176
- options.maxBodyLength = config.maxContentLength;
174
+ if (config.maxBodyLength > -1) {
175
+ options.maxBodyLength = config.maxBodyLength;
177
176
  }
178
177
 
179
178
  // Create the request
@@ -182,22 +181,27 @@ module.exports = function httpAdapter(config) {
182
181
 
183
182
  // uncompress the response body transparently if required
184
183
  var stream = res;
185
- switch (res.headers['content-encoding']) {
186
- /*eslint default-case:0*/
187
- case 'gzip':
188
- case 'compress':
189
- case 'deflate':
190
- // add the unzipper to the body stream processing pipeline
191
- stream = (res.statusCode === 204) ? stream : stream.pipe(zlib.createUnzip());
192
-
193
- // remove the content-encoding in order to not confuse downstream operations
194
- delete res.headers['content-encoding'];
195
- break;
196
- }
197
184
 
198
185
  // return the last request in case of redirects
199
186
  var lastRequest = res.req || req;
200
187
 
188
+
189
+ // if no content, is HEAD request or decompress disabled we should not decompress
190
+ if (res.statusCode !== 204 && lastRequest.method !== 'HEAD' && config.decompress !== false) {
191
+ switch (res.headers['content-encoding']) {
192
+ /*eslint default-case:0*/
193
+ case 'gzip':
194
+ case 'compress':
195
+ case 'deflate':
196
+ // add the unzipper to the body stream processing pipeline
197
+ stream = stream.pipe(zlib.createUnzip());
198
+
199
+ // remove the content-encoding in order to not confuse downstream operations
200
+ delete res.headers['content-encoding'];
201
+ break;
202
+ }
203
+ }
204
+
201
205
  var response = {
202
206
  status: res.statusCode,
203
207
  statusText: res.statusMessage,
@@ -231,6 +235,9 @@ module.exports = function httpAdapter(config) {
231
235
  var responseData = Buffer.concat(responseBuffer);
232
236
  if (config.responseType !== 'arraybuffer') {
233
237
  responseData = responseData.toString(config.responseEncoding);
238
+ if (!config.responseEncoding || config.responseEncoding === 'utf8') {
239
+ responseData = utils.stripBOM(responseData);
240
+ }
234
241
  }
235
242
 
236
243
  response.data = responseData;
@@ -241,16 +248,21 @@ module.exports = function httpAdapter(config) {
241
248
 
242
249
  // Handle errors
243
250
  req.on('error', function handleRequestError(err) {
244
- if (req.aborted) return;
251
+ if (req.aborted && err.code !== 'ERR_FR_TOO_MANY_REDIRECTS') return;
245
252
  reject(enhanceError(err, config, null, req));
246
253
  });
247
254
 
248
255
  // Handle request timeout
249
256
  if (config.timeout) {
250
- timer = setTimeout(function handleRequestTimeout() {
257
+ // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system.
258
+ // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET.
259
+ // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up.
260
+ // And then these socket which be hang up will devoring CPU little by little.
261
+ // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect.
262
+ req.setTimeout(config.timeout, function handleRequestTimeout() {
251
263
  req.abort();
252
264
  reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED', req));
253
- }, config.timeout);
265
+ });
254
266
  }
255
267
 
256
268
  if (config.cancelToken) {
@@ -2,7 +2,9 @@
2
2
 
3
3
  var utils = require('./../utils');
4
4
  var settle = require('./../core/settle');
5
+ var cookies = require('./../helpers/cookies');
5
6
  var buildURL = require('./../helpers/buildURL');
7
+ var buildFullPath = require('../core/buildFullPath');
6
8
  var parseHeaders = require('./../helpers/parseHeaders');
7
9
  var isURLSameOrigin = require('./../helpers/isURLSameOrigin');
8
10
  var createError = require('../core/createError');
@@ -16,16 +18,24 @@ module.exports = function xhrAdapter(config) {
16
18
  delete requestHeaders['Content-Type']; // Let the browser set it
17
19
  }
18
20
 
21
+ if (
22
+ (utils.isBlob(requestData) || utils.isFile(requestData)) &&
23
+ requestData.type
24
+ ) {
25
+ delete requestHeaders['Content-Type']; // Let the browser set it
26
+ }
27
+
19
28
  var request = new XMLHttpRequest();
20
29
 
21
30
  // HTTP basic authentication
22
31
  if (config.auth) {
23
32
  var username = config.auth.username || '';
24
- var password = config.auth.password || '';
33
+ var password = unescape(encodeURIComponent(config.auth.password)) || '';
25
34
  requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
26
35
  }
27
36
 
28
- request.open(config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true);
37
+ var fullPath = buildFullPath(config.baseURL, config.url);
38
+ request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
29
39
 
30
40
  // Set the request timeout in MS
31
41
  request.timeout = config.timeout;
@@ -86,7 +96,11 @@ module.exports = function xhrAdapter(config) {
86
96
 
87
97
  // Handle timeout
88
98
  request.ontimeout = function handleTimeout() {
89
- reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
99
+ var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';
100
+ if (config.timeoutErrorMessage) {
101
+ timeoutErrorMessage = config.timeoutErrorMessage;
102
+ }
103
+ reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',
90
104
  request));
91
105
 
92
106
  // Clean up request
@@ -97,10 +111,8 @@ module.exports = function xhrAdapter(config) {
97
111
  // This is only done if running in a standard browser environment.
98
112
  // Specifically not if we're in a web worker, or react-native.
99
113
  if (utils.isStandardBrowserEnv()) {
100
- var cookies = require('./../helpers/cookies');
101
-
102
114
  // Add xsrf header
103
- var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
115
+ var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?
104
116
  cookies.read(config.xsrfCookieName) :
105
117
  undefined;
106
118
 
@@ -123,8 +135,8 @@ module.exports = function xhrAdapter(config) {
123
135
  }
124
136
 
125
137
  // Add withCredentials to request if needed
126
- if (config.withCredentials) {
127
- request.withCredentials = true;
138
+ if (!utils.isUndefined(config.withCredentials)) {
139
+ request.withCredentials = !!config.withCredentials;
128
140
  }
129
141
 
130
142
  // Add responseType to request if needed
@@ -164,7 +176,7 @@ module.exports = function xhrAdapter(config) {
164
176
  });
165
177
  }
166
178
 
167
- if (requestData === undefined) {
179
+ if (!requestData) {
168
180
  requestData = null;
169
181
  }
170
182
 
package/lib/core/Axios.js CHANGED
@@ -35,7 +35,15 @@ Axios.prototype.request = function request(config) {
35
35
  }
36
36
 
37
37
  config = mergeConfig(this.defaults, config);
38
- config.method = config.method ? config.method.toLowerCase() : 'get';
38
+
39
+ // Set config.method
40
+ if (config.method) {
41
+ config.method = config.method.toLowerCase();
42
+ } else if (this.defaults.method) {
43
+ config.method = this.defaults.method.toLowerCase();
44
+ } else {
45
+ config.method = 'get';
46
+ }
39
47
 
40
48
  // Hook up interceptors middleware
41
49
  var chain = [dispatchRequest, undefined];
@@ -65,7 +73,7 @@ Axios.prototype.getUri = function getUri(config) {
65
73
  utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
66
74
  /*eslint func-names:0*/
67
75
  Axios.prototype[method] = function(url, config) {
68
- return this.request(utils.merge(config || {}, {
76
+ return this.request(mergeConfig(config || {}, {
69
77
  method: method,
70
78
  url: url
71
79
  }));
@@ -75,7 +83,7 @@ utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData
75
83
  utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
76
84
  /*eslint func-names:0*/
77
85
  Axios.prototype[method] = function(url, data, config) {
78
- return this.request(utils.merge(config || {}, {
86
+ return this.request(mergeConfig(config || {}, {
79
87
  method: method,
80
88
  url: url,
81
89
  data: data
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ var isAbsoluteURL = require('../helpers/isAbsoluteURL');
4
+ var combineURLs = require('../helpers/combineURLs');
5
+
6
+ /**
7
+ * Creates a new URL by combining the baseURL with the requestedURL,
8
+ * only when the requestedURL is not already an absolute URL.
9
+ * If the requestURL is absolute, this function returns the requestedURL untouched.
10
+ *
11
+ * @param {string} baseURL The base URL
12
+ * @param {string} requestedURL Absolute or relative URL to combine
13
+ * @returns {string} The combined full path
14
+ */
15
+ module.exports = function buildFullPath(baseURL, requestedURL) {
16
+ if (baseURL && !isAbsoluteURL(requestedURL)) {
17
+ return combineURLs(baseURL, requestedURL);
18
+ }
19
+ return requestedURL;
20
+ };
@@ -4,8 +4,6 @@ var utils = require('./../utils');
4
4
  var transformData = require('./transformData');
5
5
  var isCancel = require('../cancel/isCancel');
6
6
  var defaults = require('../defaults');
7
- var isAbsoluteURL = require('./../helpers/isAbsoluteURL');
8
- var combineURLs = require('./../helpers/combineURLs');
9
7
 
10
8
  /**
11
9
  * Throws a `Cancel` if cancellation has been requested.
@@ -25,11 +23,6 @@ function throwIfCancellationRequested(config) {
25
23
  module.exports = function dispatchRequest(config) {
26
24
  throwIfCancellationRequested(config);
27
25
 
28
- // Support baseURL config
29
- if (config.baseURL && !isAbsoluteURL(config.url)) {
30
- config.url = combineURLs(config.baseURL, config.url);
31
- }
32
-
33
26
  // Ensure headers exist
34
27
  config.headers = config.headers || {};
35
28
 
@@ -44,7 +37,7 @@ module.exports = function dispatchRequest(config) {
44
37
  config.headers = utils.merge(
45
38
  config.headers.common || {},
46
39
  config.headers[config.method] || {},
47
- config.headers || {}
40
+ config.headers
48
41
  );
49
42
 
50
43
  utils.forEach(
@@ -20,7 +20,7 @@ module.exports = function enhanceError(error, config, code, request, response) {
20
20
  error.response = response;
21
21
  error.isAxiosError = true;
22
22
 
23
- error.toJSON = function() {
23
+ error.toJSON = function toJSON() {
24
24
  return {
25
25
  // Standard
26
26
  message: this.message,
@@ -15,37 +15,73 @@ module.exports = function mergeConfig(config1, config2) {
15
15
  config2 = config2 || {};
16
16
  var config = {};
17
17
 
18
- utils.forEach(['url', 'method', 'params', 'data'], function valueFromConfig2(prop) {
19
- if (typeof config2[prop] !== 'undefined') {
20
- config[prop] = config2[prop];
18
+ var valueFromConfig2Keys = ['url', 'method', 'data'];
19
+ var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];
20
+ var defaultToConfig2Keys = [
21
+ 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',
22
+ 'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
23
+ 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',
24
+ 'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent',
25
+ 'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'
26
+ ];
27
+ var directMergeKeys = ['validateStatus'];
28
+
29
+ function getMergedValue(target, source) {
30
+ if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
31
+ return utils.merge(target, source);
32
+ } else if (utils.isPlainObject(source)) {
33
+ return utils.merge({}, source);
34
+ } else if (utils.isArray(source)) {
35
+ return source.slice();
36
+ }
37
+ return source;
38
+ }
39
+
40
+ function mergeDeepProperties(prop) {
41
+ if (!utils.isUndefined(config2[prop])) {
42
+ config[prop] = getMergedValue(config1[prop], config2[prop]);
43
+ } else if (!utils.isUndefined(config1[prop])) {
44
+ config[prop] = getMergedValue(undefined, config1[prop]);
45
+ }
46
+ }
47
+
48
+ utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
49
+ if (!utils.isUndefined(config2[prop])) {
50
+ config[prop] = getMergedValue(undefined, config2[prop]);
21
51
  }
22
52
  });
23
53
 
24
- utils.forEach(['headers', 'auth', 'proxy'], function mergeDeepProperties(prop) {
25
- if (utils.isObject(config2[prop])) {
26
- config[prop] = utils.deepMerge(config1[prop], config2[prop]);
27
- } else if (typeof config2[prop] !== 'undefined') {
28
- config[prop] = config2[prop];
29
- } else if (utils.isObject(config1[prop])) {
30
- config[prop] = utils.deepMerge(config1[prop]);
31
- } else if (typeof config1[prop] !== 'undefined') {
32
- config[prop] = config1[prop];
54
+ utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);
55
+
56
+ utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
57
+ if (!utils.isUndefined(config2[prop])) {
58
+ config[prop] = getMergedValue(undefined, config2[prop]);
59
+ } else if (!utils.isUndefined(config1[prop])) {
60
+ config[prop] = getMergedValue(undefined, config1[prop]);
33
61
  }
34
62
  });
35
63
 
36
- utils.forEach([
37
- 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',
38
- 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
39
- 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength',
40
- 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken',
41
- 'socketPath'
42
- ], function defaultToConfig2(prop) {
43
- if (typeof config2[prop] !== 'undefined') {
44
- config[prop] = config2[prop];
45
- } else if (typeof config1[prop] !== 'undefined') {
46
- config[prop] = config1[prop];
64
+ utils.forEach(directMergeKeys, function merge(prop) {
65
+ if (prop in config2) {
66
+ config[prop] = getMergedValue(config1[prop], config2[prop]);
67
+ } else if (prop in config1) {
68
+ config[prop] = getMergedValue(undefined, config1[prop]);
47
69
  }
48
70
  });
49
71
 
72
+ var axiosKeys = valueFromConfig2Keys
73
+ .concat(mergeDeepPropertiesKeys)
74
+ .concat(defaultToConfig2Keys)
75
+ .concat(directMergeKeys);
76
+
77
+ var otherKeys = Object
78
+ .keys(config1)
79
+ .concat(Object.keys(config2))
80
+ .filter(function filterAxiosKeys(key) {
81
+ return axiosKeys.indexOf(key) === -1;
82
+ });
83
+
84
+ utils.forEach(otherKeys, mergeDeepProperties);
85
+
50
86
  return config;
51
87
  };
@@ -11,7 +11,7 @@ var createError = require('./createError');
11
11
  */
12
12
  module.exports = function settle(resolve, reject, response) {
13
13
  var validateStatus = response.config.validateStatus;
14
- if (!validateStatus || validateStatus(response.status)) {
14
+ if (!response.status || !validateStatus || validateStatus(response.status)) {
15
15
  resolve(response);
16
16
  } else {
17
17
  reject(createError(
package/lib/defaults.js CHANGED
@@ -15,13 +15,12 @@ function setContentTypeIfUnset(headers, value) {
15
15
 
16
16
  function getDefaultAdapter() {
17
17
  var adapter;
18
- // Only Node.JS has a process variable that is of [[Class]] process
19
- if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
20
- // For node use HTTP adapter
21
- adapter = require('./adapters/http');
22
- } else if (typeof XMLHttpRequest !== 'undefined') {
18
+ if (typeof XMLHttpRequest !== 'undefined') {
23
19
  // For browsers use XHR adapter
24
20
  adapter = require('./adapters/xhr');
21
+ } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
22
+ // For node use HTTP adapter
23
+ adapter = require('./adapters/http');
25
24
  }
26
25
  return adapter;
27
26
  }
@@ -75,6 +74,7 @@ var defaults = {
75
74
  xsrfHeaderName: 'X-XSRF-TOKEN',
76
75
 
77
76
  maxContentLength: -1,
77
+ maxBodyLength: -1,
78
78
 
79
79
  validateStatus: function validateStatus(status) {
80
80
  return status >= 200 && status < 300;
@@ -4,7 +4,6 @@ var utils = require('./../utils');
4
4
 
5
5
  function encode(val) {
6
6
  return encodeURIComponent(val).
7
- replace(/%40/gi, '@').
8
7
  replace(/%3A/gi, ':').
9
8
  replace(/%24/g, '$').
10
9
  replace(/%2C/gi, ',').
package/lib/utils.js CHANGED
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  var bind = require('./helpers/bind');
4
- var isBuffer = require('is-buffer');
5
4
 
6
5
  /*global toString:true*/
7
6
 
@@ -19,6 +18,27 @@ function isArray(val) {
19
18
  return toString.call(val) === '[object Array]';
20
19
  }
21
20
 
21
+ /**
22
+ * Determine if a value is undefined
23
+ *
24
+ * @param {Object} val The value to test
25
+ * @returns {boolean} True if the value is undefined, otherwise false
26
+ */
27
+ function isUndefined(val) {
28
+ return typeof val === 'undefined';
29
+ }
30
+
31
+ /**
32
+ * Determine if a value is a Buffer
33
+ *
34
+ * @param {Object} val The value to test
35
+ * @returns {boolean} True if value is a Buffer, otherwise false
36
+ */
37
+ function isBuffer(val) {
38
+ return val !== null && !isUndefined(val) && val.constructor !== null && !isUndefined(val.constructor)
39
+ && typeof val.constructor.isBuffer === 'function' && val.constructor.isBuffer(val);
40
+ }
41
+
22
42
  /**
23
43
  * Determine if a value is an ArrayBuffer
24
44
  *
@@ -76,23 +96,28 @@ function isNumber(val) {
76
96
  }
77
97
 
78
98
  /**
79
- * Determine if a value is undefined
99
+ * Determine if a value is an Object
80
100
  *
81
101
  * @param {Object} val The value to test
82
- * @returns {boolean} True if the value is undefined, otherwise false
102
+ * @returns {boolean} True if value is an Object, otherwise false
83
103
  */
84
- function isUndefined(val) {
85
- return typeof val === 'undefined';
104
+ function isObject(val) {
105
+ return val !== null && typeof val === 'object';
86
106
  }
87
107
 
88
108
  /**
89
- * Determine if a value is an Object
109
+ * Determine if a value is a plain Object
90
110
  *
91
111
  * @param {Object} val The value to test
92
- * @returns {boolean} True if value is an Object, otherwise false
112
+ * @return {boolean} True if value is a plain Object, otherwise false
93
113
  */
94
- function isObject(val) {
95
- return val !== null && typeof val === 'object';
114
+ function isPlainObject(val) {
115
+ if (toString.call(val) !== '[object Object]') {
116
+ return false;
117
+ }
118
+
119
+ var prototype = Object.getPrototypeOf(val);
120
+ return prototype === null || prototype === Object.prototype;
96
121
  }
97
122
 
98
123
  /**
@@ -251,34 +276,12 @@ function forEach(obj, fn) {
251
276
  function merge(/* obj1, obj2, obj3, ... */) {
252
277
  var result = {};
253
278
  function assignValue(val, key) {
254
- if (typeof result[key] === 'object' && typeof val === 'object') {
279
+ if (isPlainObject(result[key]) && isPlainObject(val)) {
255
280
  result[key] = merge(result[key], val);
256
- } else {
257
- result[key] = val;
258
- }
259
- }
260
-
261
- for (var i = 0, l = arguments.length; i < l; i++) {
262
- forEach(arguments[i], assignValue);
263
- }
264
- return result;
265
- }
266
-
267
- /**
268
- * Function equal to merge with the difference being that no reference
269
- * to original objects is kept.
270
- *
271
- * @see merge
272
- * @param {Object} obj1 Object to merge
273
- * @returns {Object} Result of all merge properties
274
- */
275
- function deepMerge(/* obj1, obj2, obj3, ... */) {
276
- var result = {};
277
- function assignValue(val, key) {
278
- if (typeof result[key] === 'object' && typeof val === 'object') {
279
- result[key] = deepMerge(result[key], val);
280
- } else if (typeof val === 'object') {
281
- result[key] = deepMerge({}, val);
281
+ } else if (isPlainObject(val)) {
282
+ result[key] = merge({}, val);
283
+ } else if (isArray(val)) {
284
+ result[key] = val.slice();
282
285
  } else {
283
286
  result[key] = val;
284
287
  }
@@ -309,6 +312,19 @@ function extend(a, b, thisArg) {
309
312
  return a;
310
313
  }
311
314
 
315
+ /**
316
+ * Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
317
+ *
318
+ * @param {string} content with BOM
319
+ * @return {string} content value without BOM
320
+ */
321
+ function stripBOM(content) {
322
+ if (content.charCodeAt(0) === 0xFEFF) {
323
+ content = content.slice(1);
324
+ }
325
+ return content;
326
+ }
327
+
312
328
  module.exports = {
313
329
  isArray: isArray,
314
330
  isArrayBuffer: isArrayBuffer,
@@ -318,6 +334,7 @@ module.exports = {
318
334
  isString: isString,
319
335
  isNumber: isNumber,
320
336
  isObject: isObject,
337
+ isPlainObject: isPlainObject,
321
338
  isUndefined: isUndefined,
322
339
  isDate: isDate,
323
340
  isFile: isFile,
@@ -328,7 +345,7 @@ module.exports = {
328
345
  isStandardBrowserEnv: isStandardBrowserEnv,
329
346
  forEach: forEach,
330
347
  merge: merge,
331
- deepMerge: deepMerge,
332
348
  extend: extend,
333
- trim: trim
349
+ trim: trim,
350
+ stripBOM: stripBOM
334
351
  };