@uppy/utils
Version:
Shared utility functions for Uppy Core and plugins maintained by the Uppy team.
304 lines (302 loc) • 11.5 kB
JavaScript
function _classPrivateFieldLooseBase(e, t) { if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance"); return e; }
var id = 0;
function _classPrivateFieldLooseKey(e) { return "__private_" + id++ + "_" + e; }
function createCancelError(cause) {
return new Error('Cancelled', {
cause
});
}
function abortOn(signal) {
if (signal != null) {
var _this$then;
const abortPromise = () => this.abort(signal.reason);
signal.addEventListener('abort', abortPromise, {
once: true
});
const removeAbortListener = () => {
signal.removeEventListener('abort', abortPromise);
};
(_this$then = this.then) == null || _this$then.call(this, removeAbortListener, removeAbortListener);
}
return this;
}
var _activeRequests = /*#__PURE__*/_classPrivateFieldLooseKey("activeRequests");
var _queuedHandlers = /*#__PURE__*/_classPrivateFieldLooseKey("queuedHandlers");
var _paused = /*#__PURE__*/_classPrivateFieldLooseKey("paused");
var _pauseTimer = /*#__PURE__*/_classPrivateFieldLooseKey("pauseTimer");
var _downLimit = /*#__PURE__*/_classPrivateFieldLooseKey("downLimit");
var _upperLimit = /*#__PURE__*/_classPrivateFieldLooseKey("upperLimit");
var _rateLimitingTimer = /*#__PURE__*/_classPrivateFieldLooseKey("rateLimitingTimer");
var _call = /*#__PURE__*/_classPrivateFieldLooseKey("call");
var _queueNext = /*#__PURE__*/_classPrivateFieldLooseKey("queueNext");
var _next = /*#__PURE__*/_classPrivateFieldLooseKey("next");
var _queue = /*#__PURE__*/_classPrivateFieldLooseKey("queue");
var _dequeue = /*#__PURE__*/_classPrivateFieldLooseKey("dequeue");
var _resume = /*#__PURE__*/_classPrivateFieldLooseKey("resume");
var _increaseLimit = /*#__PURE__*/_classPrivateFieldLooseKey("increaseLimit");
export class RateLimitedQueue {
constructor(limit) {
Object.defineProperty(this, _dequeue, {
value: _dequeue2
});
Object.defineProperty(this, _queue, {
value: _queue2
});
Object.defineProperty(this, _next, {
value: _next2
});
Object.defineProperty(this, _queueNext, {
value: _queueNext2
});
Object.defineProperty(this, _call, {
value: _call2
});
Object.defineProperty(this, _activeRequests, {
writable: true,
value: 0
});
Object.defineProperty(this, _queuedHandlers, {
writable: true,
value: []
});
Object.defineProperty(this, _paused, {
writable: true,
value: false
});
Object.defineProperty(this, _pauseTimer, {
writable: true,
value: void 0
});
Object.defineProperty(this, _downLimit, {
writable: true,
value: 1
});
Object.defineProperty(this, _upperLimit, {
writable: true,
value: void 0
});
Object.defineProperty(this, _rateLimitingTimer, {
writable: true,
value: void 0
});
Object.defineProperty(this, _resume, {
writable: true,
value: () => this.resume()
});
Object.defineProperty(this, _increaseLimit, {
writable: true,
value: () => {
if (_classPrivateFieldLooseBase(this, _paused)[_paused]) {
_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], 0);
return;
}
_classPrivateFieldLooseBase(this, _downLimit)[_downLimit] = this.limit;
this.limit = Math.ceil((_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] + _classPrivateFieldLooseBase(this, _downLimit)[_downLimit]) / 2);
for (let i = _classPrivateFieldLooseBase(this, _downLimit)[_downLimit]; i <= this.limit; i++) {
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
}
if (_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] - _classPrivateFieldLooseBase(this, _downLimit)[_downLimit] > 3) {
_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], 2000);
} else {
_classPrivateFieldLooseBase(this, _downLimit)[_downLimit] = Math.floor(_classPrivateFieldLooseBase(this, _downLimit)[_downLimit] / 2);
}
}
});
if (typeof limit !== 'number' || limit === 0) {
this.limit = Infinity;
} else {
this.limit = limit;
}
}
run(fn, queueOptions) {
if (!_classPrivateFieldLooseBase(this, _paused)[_paused] && _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] < this.limit) {
return _classPrivateFieldLooseBase(this, _call)[_call](fn);
}
return _classPrivateFieldLooseBase(this, _queue)[_queue](fn, queueOptions);
}
wrapSyncFunction(fn, queueOptions) {
var _this = this;
return function () {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
const queuedRequest = _this.run(() => {
fn(...args);
queueMicrotask(() => queuedRequest.done());
return () => {};
}, queueOptions);
return {
abortOn,
abort() {
queuedRequest.abort();
}
};
};
}
wrapPromiseFunction(fn, queueOptions) {
var _this2 = this;
return function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
let queuedRequest;
const outerPromise = new Promise((resolve, reject) => {
queuedRequest = _this2.run(() => {
let cancelError;
let innerPromise;
try {
innerPromise = Promise.resolve(fn(...args));
} catch (err) {
innerPromise = Promise.reject(err);
}
innerPromise.then(result => {
if (cancelError) {
reject(cancelError);
} else {
queuedRequest.done();
resolve(result);
}
}, err => {
if (cancelError) {
reject(cancelError);
} else {
queuedRequest.done();
reject(err);
}
});
return cause => {
cancelError = createCancelError(cause);
};
}, queueOptions);
});
outerPromise.abort = cause => {
queuedRequest.abort(cause);
};
outerPromise.abortOn = abortOn;
return outerPromise;
};
}
resume() {
_classPrivateFieldLooseBase(this, _paused)[_paused] = false;
clearTimeout(_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer]);
for (let i = 0; i < this.limit; i++) {
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
}
}
/**
* Freezes the queue for a while or indefinitely.
*
* @param {number | null } [duration] Duration for the pause to happen, in milliseconds.
* If omitted, the queue won't resume automatically.
*/
pause(duration) {
if (duration === void 0) {
duration = null;
}
_classPrivateFieldLooseBase(this, _paused)[_paused] = true;
clearTimeout(_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer]);
if (duration != null) {
_classPrivateFieldLooseBase(this, _pauseTimer)[_pauseTimer] = setTimeout(_classPrivateFieldLooseBase(this, _resume)[_resume], duration);
}
}
/**
* Pauses the queue for a duration, and lower the limit of concurrent requests
* when the queue resumes. When the queue resumes, it tries to progressively
* increase the limit in `this.#increaseLimit` until another call is made to
* `this.rateLimit`.
* Call this function when using the RateLimitedQueue for network requests and
* the remote server responds with 429 HTTP code.
*
* @param {number} duration in milliseconds.
*/
rateLimit(duration) {
clearTimeout(_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer]);
this.pause(duration);
if (this.limit > 1 && Number.isFinite(this.limit)) {
_classPrivateFieldLooseBase(this, _upperLimit)[_upperLimit] = this.limit - 1;
this.limit = _classPrivateFieldLooseBase(this, _downLimit)[_downLimit];
_classPrivateFieldLooseBase(this, _rateLimitingTimer)[_rateLimitingTimer] = setTimeout(_classPrivateFieldLooseBase(this, _increaseLimit)[_increaseLimit], duration);
}
}
get isPaused() {
return _classPrivateFieldLooseBase(this, _paused)[_paused];
}
}
function _call2(fn) {
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] += 1;
let done = false;
let cancelActive;
try {
cancelActive = fn();
} catch (err) {
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
throw err;
}
return {
abort: cause => {
if (done) return;
done = true;
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
cancelActive == null || cancelActive(cause);
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
},
done: () => {
if (done) return;
done = true;
_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1;
_classPrivateFieldLooseBase(this, _queueNext)[_queueNext]();
}
};
}
function _queueNext2() {
// Do it soon but not immediately, this allows clearing out the entire queue synchronously
// one by one without continuously _advancing_ it (and starting new tasks before immediately
// aborting them)
queueMicrotask(() => _classPrivateFieldLooseBase(this, _next)[_next]());
}
function _next2() {
if (_classPrivateFieldLooseBase(this, _paused)[_paused] || _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] >= this.limit) {
return;
}
if (_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].length === 0) {
return;
}
// Dispatch the next request, and update the abort/done handlers
// so that cancelling it does the Right Thing (and doesn't just try
// to dequeue an already-running request).
const next = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].shift();
if (next == null) {
throw new Error('Invariant violation: next is null');
}
const handler = _classPrivateFieldLooseBase(this, _call)[_call](next.fn);
next.abort = handler.abort;
next.done = handler.done;
}
function _queue2(fn, options) {
const handler = {
fn,
priority: (options == null ? void 0 : options.priority) || 0,
abort: () => {
_classPrivateFieldLooseBase(this, _dequeue)[_dequeue](handler);
},
done: () => {
throw new Error('Cannot mark a queued request as done: this indicates a bug');
}
};
const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].findIndex(other => {
return handler.priority > other.priority;
});
if (index === -1) {
_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].push(handler);
} else {
_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 0, handler);
}
return handler;
}
function _dequeue2(handler) {
const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].indexOf(handler);
if (index !== -1) {
_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 1);
}
}
export const internalRateLimitedQueue = Symbol('__queue');