188 lines
6.9 KiB
JavaScript
188 lines
6.9 KiB
JavaScript
import is, { assert } from '@sindresorhus/is';
|
|
import asPromise from './as-promise/index.js';
|
|
import Request from './core/index.js';
|
|
import Options from './core/options.js';
|
|
// The `delay` package weighs 10KB (!)
|
|
const delay = async (ms) => new Promise(resolve => {
|
|
setTimeout(resolve, ms);
|
|
});
|
|
const isGotInstance = (value) => is.function_(value);
|
|
const aliases = [
|
|
'get',
|
|
'post',
|
|
'put',
|
|
'patch',
|
|
'head',
|
|
'delete',
|
|
];
|
|
const create = (defaults) => {
|
|
defaults = {
|
|
options: new Options(undefined, undefined, defaults.options),
|
|
handlers: [...defaults.handlers],
|
|
mutableDefaults: defaults.mutableDefaults,
|
|
};
|
|
Object.defineProperty(defaults, 'mutableDefaults', {
|
|
enumerable: true,
|
|
configurable: false,
|
|
writable: false,
|
|
});
|
|
// Got interface
|
|
const got = ((url, options, defaultOptions = defaults.options) => {
|
|
const request = new Request(url, options, defaultOptions);
|
|
let promise;
|
|
const lastHandler = (normalized) => {
|
|
// Note: `options` is `undefined` when `new Options(...)` fails
|
|
request.options = normalized;
|
|
request._noPipe = !normalized.isStream;
|
|
void request.flush();
|
|
if (normalized.isStream) {
|
|
return request;
|
|
}
|
|
if (!promise) {
|
|
promise = asPromise(request);
|
|
}
|
|
return promise;
|
|
};
|
|
let iteration = 0;
|
|
const iterateHandlers = (newOptions) => {
|
|
const handler = defaults.handlers[iteration++] ?? lastHandler;
|
|
const result = handler(newOptions, iterateHandlers);
|
|
if (is.promise(result) && !request.options.isStream) {
|
|
if (!promise) {
|
|
promise = asPromise(request);
|
|
}
|
|
if (result !== promise) {
|
|
const descriptors = Object.getOwnPropertyDescriptors(promise);
|
|
for (const key in descriptors) {
|
|
if (key in result) {
|
|
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
delete descriptors[key];
|
|
}
|
|
}
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
Object.defineProperties(result, descriptors);
|
|
result.cancel = promise.cancel;
|
|
}
|
|
}
|
|
return result;
|
|
};
|
|
return iterateHandlers(request.options);
|
|
});
|
|
got.extend = (...instancesOrOptions) => {
|
|
const options = new Options(undefined, undefined, defaults.options);
|
|
const handlers = [...defaults.handlers];
|
|
let mutableDefaults;
|
|
for (const value of instancesOrOptions) {
|
|
if (isGotInstance(value)) {
|
|
options.merge(value.defaults.options);
|
|
handlers.push(...value.defaults.handlers);
|
|
mutableDefaults = value.defaults.mutableDefaults;
|
|
}
|
|
else {
|
|
options.merge(value);
|
|
if (value.handlers) {
|
|
handlers.push(...value.handlers);
|
|
}
|
|
mutableDefaults = value.mutableDefaults;
|
|
}
|
|
}
|
|
return create({
|
|
options,
|
|
handlers,
|
|
mutableDefaults: Boolean(mutableDefaults),
|
|
});
|
|
};
|
|
// Pagination
|
|
const paginateEach = (async function* (url, options) {
|
|
let normalizedOptions = new Options(url, options, defaults.options);
|
|
normalizedOptions.resolveBodyOnly = false;
|
|
const { pagination } = normalizedOptions;
|
|
assert.function_(pagination.transform);
|
|
assert.function_(pagination.shouldContinue);
|
|
assert.function_(pagination.filter);
|
|
assert.function_(pagination.paginate);
|
|
assert.number(pagination.countLimit);
|
|
assert.number(pagination.requestLimit);
|
|
assert.number(pagination.backoff);
|
|
const allItems = [];
|
|
let { countLimit } = pagination;
|
|
let numberOfRequests = 0;
|
|
while (numberOfRequests < pagination.requestLimit) {
|
|
if (numberOfRequests !== 0) {
|
|
// eslint-disable-next-line no-await-in-loop
|
|
await delay(pagination.backoff);
|
|
}
|
|
// eslint-disable-next-line no-await-in-loop
|
|
const response = (await got(undefined, undefined, normalizedOptions));
|
|
// eslint-disable-next-line no-await-in-loop
|
|
const parsed = await pagination.transform(response);
|
|
const currentItems = [];
|
|
assert.array(parsed);
|
|
for (const item of parsed) {
|
|
if (pagination.filter({ item, currentItems, allItems })) {
|
|
if (!pagination.shouldContinue({ item, currentItems, allItems })) {
|
|
return;
|
|
}
|
|
yield item;
|
|
if (pagination.stackAllItems) {
|
|
allItems.push(item);
|
|
}
|
|
currentItems.push(item);
|
|
if (--countLimit <= 0) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
const optionsToMerge = pagination.paginate({
|
|
response,
|
|
currentItems,
|
|
allItems,
|
|
});
|
|
if (optionsToMerge === false) {
|
|
return;
|
|
}
|
|
if (optionsToMerge === response.request.options) {
|
|
normalizedOptions = response.request.options;
|
|
}
|
|
else {
|
|
normalizedOptions.merge(optionsToMerge);
|
|
assert.any([is.urlInstance, is.undefined], optionsToMerge.url);
|
|
if (optionsToMerge.url !== undefined) {
|
|
normalizedOptions.prefixUrl = '';
|
|
normalizedOptions.url = optionsToMerge.url;
|
|
}
|
|
}
|
|
numberOfRequests++;
|
|
}
|
|
});
|
|
got.paginate = paginateEach;
|
|
got.paginate.all = (async (url, options) => {
|
|
const results = [];
|
|
for await (const item of paginateEach(url, options)) {
|
|
results.push(item);
|
|
}
|
|
return results;
|
|
});
|
|
// For those who like very descriptive names
|
|
got.paginate.each = paginateEach;
|
|
// Stream API
|
|
got.stream = ((url, options) => got(url, { ...options, isStream: true }));
|
|
// Shortcuts
|
|
for (const method of aliases) {
|
|
got[method] = ((url, options) => got(url, { ...options, method }));
|
|
got.stream[method] = ((url, options) => got(url, { ...options, method, isStream: true }));
|
|
}
|
|
if (!defaults.mutableDefaults) {
|
|
Object.freeze(defaults.handlers);
|
|
defaults.options.freeze();
|
|
}
|
|
Object.defineProperty(got, 'defaults', {
|
|
value: defaults,
|
|
writable: false,
|
|
configurable: false,
|
|
enumerable: true,
|
|
});
|
|
return got;
|
|
};
|
|
export default create;
|