@verdaccio/proxy 6.0.0-6-next.20 → 6.0.0-6-next.21

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.
@@ -1,691 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.ProxyStorage = void 0;
7
-
8
- var _JSONStream = _interopRequireDefault(require("JSONStream"));
9
-
10
- var _debug = _interopRequireDefault(require("debug"));
11
-
12
- var _lodash = _interopRequireDefault(require("lodash"));
13
-
14
- var _request = _interopRequireDefault(require("request"));
15
-
16
- var _stream = _interopRequireWildcard(require("stream"));
17
-
18
- var _undici = require("undici");
19
-
20
- var _url = require("url");
21
-
22
- var _core = require("@verdaccio/core");
23
-
24
- var _streams = require("@verdaccio/streams");
25
-
26
- var _utils = require("@verdaccio/utils");
27
-
28
- var _proxyUtils = require("./proxy-utils");
29
-
30
- function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
31
-
32
- function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
33
-
34
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
35
-
36
- /* global AbortController */
37
- const LoggerApi = require('@verdaccio/logger');
38
-
39
- const debug = (0, _debug.default)('verdaccio:proxy');
40
-
41
- const encode = function (thing) {
42
- return encodeURIComponent(thing).replace(/^%40/, '@');
43
- };
44
-
45
- const jsonContentType = _core.HEADERS.JSON;
46
- const contentTypeAccept = `${jsonContentType};`;
47
- /**
48
- * Just a helper (`config[key] || default` doesn't work because of zeroes)
49
- */
50
-
51
- const setConfig = (config, key, def) => {
52
- return _lodash.default.isNil(config[key]) === false ? config[key] : def;
53
- };
54
-
55
- /**
56
- * Implements Storage interface
57
- * (same for storage.js, local-storage.js, up-storage.js)
58
- */
59
- class ProxyStorage {
60
- // FIXME: upname is assigned to each instance
61
- // @ts-ignore
62
- // FIXME: proxy can be boolean or object, something smells here
63
- // @ts-ignore
64
- // @ts-ignore
65
-
66
- /**
67
- * Constructor
68
- * @param {*} config
69
- * @param {*} mainConfig
70
- */
71
- constructor(config, mainConfig) {
72
- this.config = config;
73
- this.failed_requests = 0;
74
- this.userAgent = mainConfig.user_agent;
75
- this.ca = config.ca;
76
- this.logger = LoggerApi.logger.child({
77
- sub: 'out'
78
- });
79
- this.server_id = mainConfig.server_id;
80
- this.url = new _url.URL(this.config.url);
81
-
82
- this._setupProxy(this.url.hostname, config, mainConfig, this.url.protocol === 'https:');
83
-
84
- this.config.url = this.config.url.replace(/\/$/, '');
85
-
86
- if (this.config.timeout && Number(this.config.timeout) >= 1000) {
87
- this.logger.warn(['Too big timeout value: ' + this.config.timeout, 'We changed time format to nginx-like one', '(see http://nginx.org/en/docs/syntax.html)', 'so please update your config accordingly'].join('\n'));
88
- } // a bunch of different configurable timers
89
-
90
-
91
- this.maxage = (0, _proxyUtils.parseInterval)(setConfig(this.config, 'maxage', '2m'));
92
- this.timeout = (0, _proxyUtils.parseInterval)(setConfig(this.config, 'timeout', '30s'));
93
- this.max_fails = Number(setConfig(this.config, 'max_fails', 2));
94
- this.fail_timeout = (0, _proxyUtils.parseInterval)(setConfig(this.config, 'fail_timeout', '5m'));
95
- this.strict_ssl = Boolean(setConfig(this.config, 'strict_ssl', true));
96
- this.agent_options = setConfig(this.config, 'agent_options', {
97
- keepAlive: true,
98
- maxSockets: 40,
99
- maxFreeSockets: 10
100
- });
101
- }
102
- /**
103
- * Fetch an asset.
104
- * @param {*} options
105
- * @param {*} cb
106
- * @return {Request}
107
- */
108
-
109
-
110
- request(options, cb) {
111
- let json;
112
-
113
- if (this._statusCheck() === false) {
114
- const streamRead = new _stream.default.Readable();
115
- process.nextTick(function () {
116
- if (cb) {
117
- cb(_core.errorUtils.getInternalError(_core.errorUtils.API_ERROR.UPLINK_OFFLINE));
118
- }
119
-
120
- streamRead.emit('error', _core.errorUtils.getInternalError(_core.errorUtils.API_ERROR.UPLINK_OFFLINE));
121
- });
122
-
123
- streamRead._read = function () {}; // preventing 'Uncaught, unspecified "error" event'
124
-
125
-
126
- streamRead.on('error', function () {});
127
- return streamRead;
128
- }
129
-
130
- const self = this;
131
-
132
- const headers = this._setHeaders(options);
133
-
134
- this._addProxyHeaders(options.req, headers);
135
-
136
- this._overrideWithUpLinkConfLocaligHeaders(headers);
137
-
138
- const method = options.method || 'GET';
139
- const uri = options.uri_full || this.config.url + options.uri;
140
- self.logger.info({
141
- method: method,
142
- headers: headers,
143
- uri: uri
144
- }, "making request: '@{method} @{uri}'");
145
-
146
- if (_core.validatioUtils.isObject(options.json)) {
147
- json = JSON.stringify(options.json);
148
- headers['Content-Type'] = headers['Content-Type'] || _core.HEADERS.JSON;
149
- }
150
-
151
- const requestCallback = cb ? function (err, res, body) {
152
- let error;
153
- const responseLength = err ? 0 : body.length; // $FlowFixMe
154
-
155
- processBody();
156
- logActivity(); // $FlowFixMe
157
-
158
- cb(err, res, body);
159
- /**
160
- * Perform a decode.
161
- */
162
-
163
- function processBody() {
164
- if (err) {
165
- error = err.message;
166
- return;
167
- }
168
-
169
- if (options.json && res.statusCode < 300) {
170
- try {
171
- // $FlowFixMe
172
- body = JSON.parse(body.toString(_core.CHARACTER_ENCODING.UTF8));
173
- } catch (_err) {
174
- body = {};
175
- err = _err;
176
- error = err.message;
177
- }
178
- }
179
-
180
- if (!err && _core.validatioUtils.isObject(body)) {
181
- if (_lodash.default.isString(body.error)) {
182
- error = body.error;
183
- }
184
- }
185
- }
186
- /**
187
- * Perform a log.
188
- */
189
-
190
-
191
- function logActivity() {
192
- let message = "@{!status}, req: '@{request.method} @{request.url}'"; // FIXME: use LOG_VERDACCIO_BYTES
193
-
194
- message += error ? ', error: @{!error}' : ', bytes: @{bytes.in}/@{bytes.out}';
195
- self.logger.http({
196
- // if error is null/false change this to undefined so it wont log
197
- err: err || undefined,
198
- request: {
199
- method: method,
200
- url: uri
201
- },
202
- status: res != null ? res.statusCode : 'ERR',
203
- error: error,
204
- bytes: {
205
- in: json ? json.length : 0,
206
- out: responseLength || 0
207
- }
208
- }, message);
209
- }
210
- } : undefined;
211
- let requestOptions = {
212
- url: uri,
213
- method: method,
214
- headers: headers,
215
- body: json,
216
- proxy: this.proxy,
217
- encoding: null,
218
- gzip: true,
219
- timeout: this.timeout,
220
- strictSSL: this.strict_ssl,
221
- agentOptions: this.agent_options
222
- };
223
-
224
- if (this.ca) {
225
- requestOptions = Object.assign({}, requestOptions, {
226
- ca: this.ca
227
- });
228
- }
229
-
230
- const req = (0, _request.default)(requestOptions, requestCallback);
231
- let statusCalled = false;
232
- req.on('response', function (res) {
233
- // FIXME: _verdaccio_aborted seems not used
234
- // @ts-ignore
235
- if (!req._verdaccio_aborted && !statusCalled) {
236
- statusCalled = true;
237
-
238
- self._statusCheck(true);
239
- }
240
-
241
- if (_lodash.default.isNil(requestCallback) === false) {
242
- (function do_log() {
243
- const message = "@{!status}, req: '@{request.method} @{request.url}' (streaming)";
244
- self.logger.http({
245
- request: {
246
- method: method,
247
- url: uri
248
- },
249
- status: _lodash.default.isNull(res) === false ? res.statusCode : 'ERR'
250
- }, message);
251
- })();
252
- }
253
- }); // eslint-disable-next-line @typescript-eslint/no-unused-vars
254
-
255
- req.on('error', function (_err) {
256
- // FIXME: _verdaccio_aborted seems not used
257
- // @ts-ignore
258
- if (!req._verdaccio_aborted && !statusCalled) {
259
- statusCalled = true;
260
-
261
- self._statusCheck(false);
262
- }
263
- }); // @ts-ignore
264
-
265
- return req;
266
- }
267
- /**
268
- * Set default headers.
269
- * @param {Object} options
270
- * @return {Object}
271
- * @private
272
- */
273
-
274
-
275
- _setHeaders(options) {
276
- const headers = options.headers || {};
277
- const accept = _core.HEADERS.ACCEPT;
278
- const acceptEncoding = _core.HEADERS.ACCEPT_ENCODING;
279
- const userAgent = _core.HEADERS.USER_AGENT;
280
- headers[accept] = headers[accept] || contentTypeAccept;
281
- headers[acceptEncoding] = headers[acceptEncoding] || 'gzip'; // registry.npmjs.org will only return search result if user-agent include string 'npm'
282
-
283
- headers[userAgent] = headers[userAgent] || `npm (${this.userAgent})`;
284
- return this._setAuth(headers);
285
- }
286
- /**
287
- * Validate configuration auth and assign Header authorization
288
- * @param {Object} headers
289
- * @return {Object}
290
- * @private
291
- */
292
-
293
-
294
- _setAuth(headers) {
295
- const {
296
- auth
297
- } = this.config;
298
-
299
- if (_lodash.default.isNil(auth) || headers[_core.HEADERS.AUTHORIZATION]) {
300
- return headers;
301
- }
302
-
303
- if (_lodash.default.isObject(auth) === false && _lodash.default.isObject(auth.token) === false) {
304
- this._throwErrorAuth('Auth invalid');
305
- } // get NPM_TOKEN http://blog.npmjs.org/post/118393368555/deploying-with-npm-private-modules
306
- // or get other variable export in env
307
- // https://github.com/verdaccio/verdaccio/releases/tag/v2.5.0
308
-
309
-
310
- let token;
311
- const tokenConf = auth;
312
-
313
- if (_lodash.default.isNil(tokenConf.token) === false && _lodash.default.isString(tokenConf.token)) {
314
- token = tokenConf.token;
315
- } else if (_lodash.default.isNil(tokenConf.token_env) === false) {
316
- if (_lodash.default.isString(tokenConf.token_env)) {
317
- token = process.env[tokenConf.token_env];
318
- } else if (_lodash.default.isBoolean(tokenConf.token_env) && tokenConf.token_env) {
319
- token = process.env.NPM_TOKEN;
320
- } else {
321
- this.logger.error(_core.constants.ERROR_CODE.token_required);
322
-
323
- this._throwErrorAuth(_core.constants.ERROR_CODE.token_required);
324
- }
325
- } else {
326
- token = process.env.NPM_TOKEN;
327
- }
328
-
329
- if (_lodash.default.isNil(token)) {
330
- this._throwErrorAuth(_core.constants.ERROR_CODE.token_required);
331
- } // define type Auth allow basic and bearer
332
-
333
-
334
- const type = tokenConf.type || _core.TOKEN_BASIC;
335
-
336
- this._setHeaderAuthorization(headers, type, token);
337
-
338
- return headers;
339
- }
340
- /**
341
- * @param {string} message
342
- * @throws {Error}
343
- * @private
344
- */
345
-
346
-
347
- _throwErrorAuth(message) {
348
- this.logger.error(message);
349
- throw new Error(message);
350
- }
351
- /**
352
- * Assign Header authorization with type authentication
353
- * @param {Object} headers
354
- * @param {string} type
355
- * @param {string} token
356
- * @private
357
- */
358
-
359
-
360
- _setHeaderAuthorization(headers, type, token) {
361
- const _type = type.toLowerCase();
362
-
363
- if (_type !== _core.TOKEN_BEARER.toLowerCase() && _type !== _core.TOKEN_BASIC.toLowerCase()) {
364
- this._throwErrorAuth(`Auth type '${_type}' not allowed`);
365
- }
366
-
367
- type = _lodash.default.upperFirst(type);
368
- headers[_core.HEADERS.AUTHORIZATION] = (0, _utils.buildToken)(type, token);
369
- }
370
- /**
371
- * It will add or override specified headers from config file.
372
- *
373
- * Eg:
374
- *
375
- * uplinks:
376
- npmjs:
377
- url: https://registry.npmjs.org/
378
- headers:
379
- Accept: "application/vnd.npm.install-v2+json; q=1.0"
380
- verdaccio-staging:
381
- url: https://mycompany.com/npm
382
- headers:
383
- Accept: "application/json"
384
- authorization: "Basic YourBase64EncodedCredentials=="
385
- * @param {Object} headers
386
- * @private
387
- */
388
-
389
-
390
- _overrideWithUpLinkConfLocaligHeaders(headers) {
391
- if (!this.config.headers) {
392
- return headers;
393
- } // add/override headers specified in the config
394
-
395
- /* eslint guard-for-in: 0 */
396
-
397
-
398
- for (const key in this.config.headers) {
399
- headers[key] = this.config.headers[key];
400
- }
401
- }
402
- /**
403
- * Get a remote package metadata
404
- * @param {*} name package name
405
- * @param {*} options request options, eg: eTag.
406
- * @param {*} callback
407
- */
408
-
409
-
410
- getRemoteMetadata(name, options, callback) {
411
- const headers = {};
412
-
413
- if (_lodash.default.isNil(options.etag) === false) {
414
- headers['If-None-Match'] = options.etag;
415
- headers[_core.HEADERS.ACCEPT] = contentTypeAccept;
416
- }
417
-
418
- this.request({
419
- uri: `/${encode(name)}`,
420
- json: true,
421
- headers: headers,
422
- req: options.req
423
- }, (err, res, body) => {
424
- if (err) {
425
- return callback(err);
426
- }
427
-
428
- if (res.statusCode === _core.HTTP_STATUS.NOT_FOUND) {
429
- return callback(_core.errorUtils.getNotFound(_core.errorUtils.API_ERROR.NOT_PACKAGE_UPLINK));
430
- }
431
-
432
- if (!(res.statusCode >= _core.HTTP_STATUS.OK && res.statusCode < _core.HTTP_STATUS.MULTIPLE_CHOICES)) {
433
- const error = _core.errorUtils.getInternalError(`${_core.errorUtils.API_ERROR.BAD_STATUS_CODE}: ${res.statusCode}`);
434
-
435
- error.remoteStatus = res.statusCode;
436
- return callback(error);
437
- }
438
-
439
- callback(null, body, res.headers.etag);
440
- });
441
- }
442
- /**
443
- * Fetch a tarball from the uplink.
444
- * @param {String} url
445
- * @return {Stream}
446
- */
447
-
448
-
449
- fetchTarball(url) {
450
- const stream = new _streams.ReadTarball({});
451
- let current_length = 0;
452
- let expected_length;
453
-
454
- stream.abort = () => {};
455
-
456
- const readStream = this.request({
457
- uri_full: url,
458
- encoding: null,
459
- headers: {
460
- Accept: contentTypeAccept
461
- }
462
- });
463
- readStream.on('response', function (res) {
464
- if (res.statusCode === _core.HTTP_STATUS.NOT_FOUND) {
465
- return stream.emit('error', _core.errorUtils.getNotFound(_core.errorUtils.API_ERROR.NOT_FILE_UPLINK));
466
- }
467
-
468
- if (!(res.statusCode >= _core.HTTP_STATUS.OK && res.statusCode < _core.HTTP_STATUS.MULTIPLE_CHOICES)) {
469
- return stream.emit('error', _core.errorUtils.getInternalError(`bad uplink status code: ${res.statusCode}`));
470
- }
471
-
472
- if (res.headers[_core.HEADER_TYPE.CONTENT_LENGTH]) {
473
- expected_length = res.headers[_core.HEADER_TYPE.CONTENT_LENGTH];
474
- stream.emit(_core.HEADER_TYPE.CONTENT_LENGTH, res.headers[_core.HEADER_TYPE.CONTENT_LENGTH]);
475
- }
476
-
477
- readStream.pipe(stream);
478
- });
479
- readStream.on('error', function (err) {
480
- stream.emit('error', err);
481
- });
482
- readStream.on('data', function (data) {
483
- current_length += data.length;
484
- });
485
- readStream.on('end', function (data) {
486
- if (data) {
487
- current_length += data.length;
488
- }
489
-
490
- if (expected_length && current_length != expected_length) {
491
- stream.emit('error', _core.errorUtils.getInternalError(_core.errorUtils.API_ERROR.CONTENT_MISMATCH));
492
- }
493
- });
494
- return stream;
495
- }
496
- /**
497
- * Perform a stream search.
498
- * @param {*} options request options
499
- * @return {Stream}
500
- */
501
-
502
-
503
- async search({
504
- url,
505
- abort
506
- }) {
507
- debug('search url %o', url);
508
- let response;
509
-
510
- try {
511
- const fullURL = new _url.URL(`${this.url}${url}`); // FIXME: a better way to remove duplicate slashes?
512
-
513
- const uri = fullURL.href.replace(/([^:]\/)\/+/g, '$1');
514
- this.logger.http({
515
- uri,
516
- uplink: this.upname
517
- }, 'search request to uplink @{uplink} - @{uri}');
518
- response = await (0, _undici.fetch)(uri, {
519
- method: 'GET',
520
- // FUTURE: whitelist domains what we are sending not need it headers, security check
521
- // headers: new Headers({
522
- // ...headers,
523
- // connection: 'keep-alive',
524
- // }),
525
- signal: abort === null || abort === void 0 ? void 0 : abort.signal
526
- });
527
- debug('response.status %o', response.status);
528
-
529
- if (response.status >= _core.HTTP_STATUS.BAD_REQUEST) {
530
- throw _core.errorUtils.getInternalError(`bad status code ${response.status} from uplink`);
531
- }
532
-
533
- const streamSearch = new _stream.PassThrough({
534
- objectMode: true
535
- });
536
- const res = await response.text();
537
-
538
- const streamResponse = _stream.Readable.from(res); // objects is one of the properties on the body, it ignores date and total
539
-
540
-
541
- streamResponse.pipe(_JSONStream.default.parse('objects')).pipe(streamSearch, {
542
- end: true
543
- });
544
- return streamSearch;
545
- } catch (err) {
546
- this.logger.error({
547
- errorMessage: err === null || err === void 0 ? void 0 : err.message,
548
- name: this.upname
549
- }, 'proxy uplink @{name} search error: @{errorMessage}');
550
- throw err;
551
- }
552
- }
553
- /**
554
- * Add proxy headers.
555
- * FIXME: object mutations, it should return an new object
556
- * @param {*} req the http request
557
- * @param {*} headers the request headers
558
- */
559
-
560
-
561
- _addProxyHeaders(req, headers) {
562
- if (req) {
563
- // Only submit X-Forwarded-For field if we don't have a proxy selected
564
- // in the config file.
565
- //
566
- // Otherwise misconfigured proxy could return 407:
567
- // https://github.com/rlidwka/sinopia/issues/254
568
- // @ts-ignore
569
- if (!this.proxy) {
570
- headers[_core.HEADERS.FORWARDED_FOR] = (req.headers['x-forwarded-for'] ? req.headers['x-forwarded-for'] + ', ' : '') + req.connection.remoteAddress;
571
- }
572
- } // always attach Via header to avoid loops, even if we're not proxying
573
-
574
-
575
- headers['Via'] = req !== null && req !== void 0 && req.headers['via'] ? req.headers['via'] + ', ' : '';
576
- headers['Via'] += '1.1 ' + this.server_id + ' (Verdaccio)';
577
- }
578
- /**
579
- * Check whether the remote host is available.
580
- * @param {*} alive
581
- * @return {Boolean}
582
- */
583
-
584
-
585
- _statusCheck(alive) {
586
- if (arguments.length === 0) {
587
- return this._ifRequestFailure() === false;
588
- }
589
-
590
- if (alive) {
591
- if (this.failed_requests >= this.max_fails) {
592
- this.logger.warn({
593
- host: this.url.host
594
- }, 'host @{host} is back online');
595
- }
596
-
597
- this.failed_requests = 0;
598
- } else {
599
- this.failed_requests++;
600
-
601
- if (this.failed_requests === this.max_fails) {
602
- this.logger.warn({
603
- host: this.url.host
604
- }, 'host @{host} is now offline');
605
- }
606
- }
607
-
608
- this.last_request_time = Date.now();
609
- }
610
- /**
611
- * If the request failure.
612
- * @return {boolean}
613
- * @private
614
- */
615
-
616
-
617
- _ifRequestFailure() {
618
- return this.failed_requests >= this.max_fails && Math.abs(Date.now() - this.last_request_time) < this.fail_timeout;
619
- }
620
- /**
621
- * Set up a proxy.
622
- * @param {*} hostname
623
- * @param {*} config
624
- * @param {*} mainconfig
625
- * @param {*} isHTTPS
626
- */
627
-
628
-
629
- _setupProxy(hostname, config, mainconfig, isHTTPS) {
630
- let noProxyList;
631
- const proxy_key = isHTTPS ? 'https_proxy' : 'http_proxy'; // get http_proxy and no_proxy configs
632
-
633
- if (proxy_key in config) {
634
- this.proxy = config[proxy_key];
635
- } else if (proxy_key in mainconfig) {
636
- this.proxy = mainconfig[proxy_key];
637
- }
638
-
639
- if ('no_proxy' in config) {
640
- noProxyList = config.no_proxy;
641
- } else if ('no_proxy' in mainconfig) {
642
- noProxyList = mainconfig.no_proxy;
643
- } // use wget-like algorithm to determine if proxy shouldn't be used
644
-
645
-
646
- if (hostname[0] !== '.') {
647
- hostname = '.' + hostname;
648
- }
649
-
650
- if (_lodash.default.isString(noProxyList) && noProxyList.length) {
651
- noProxyList = noProxyList.split(',');
652
- }
653
-
654
- if (_lodash.default.isArray(noProxyList)) {
655
- for (let i = 0; i < noProxyList.length; i++) {
656
- let noProxyItem = noProxyList[i];
657
-
658
- if (noProxyItem[0] !== '.') {
659
- noProxyItem = '.' + noProxyItem;
660
- }
661
-
662
- if (hostname.lastIndexOf(noProxyItem) === hostname.length - noProxyItem.length) {
663
- if (this.proxy) {
664
- this.logger.debug({
665
- url: this.url.href,
666
- rule: noProxyItem
667
- }, 'not using proxy for @{url}, excluded by @{rule} rule'); // @ts-ignore
668
-
669
- this.proxy = false;
670
- }
671
-
672
- break;
673
- }
674
- }
675
- } // if it's non-string (i.e. "false"), don't use it
676
-
677
-
678
- if (_lodash.default.isString(this.proxy) === false) {
679
- delete this.proxy;
680
- } else {
681
- this.logger.debug({
682
- url: this.url.href,
683
- proxy: this.proxy
684
- }, 'using proxy @{proxy} for @{url}');
685
- }
686
- }
687
-
688
- }
689
-
690
- exports.ProxyStorage = ProxyStorage;
691
- //# sourceMappingURL=up-storage.js.map