UNPKG

6.97 kBJavaScriptView Raw
1import is, { assert } from '@sindresorhus/is';
2import asPromise from './as-promise/index.js';
3import Request from './core/index.js';
4import Options from './core/options.js';
5// The `delay` package weighs 10KB (!)
6const delay = async (ms) => new Promise(resolve => {
7 setTimeout(resolve, ms);
8});
9const isGotInstance = (value) => is.function_(value);
10const aliases = [
11 'get',
12 'post',
13 'put',
14 'patch',
15 'head',
16 'delete',
17];
18const create = (defaults) => {
19 defaults = {
20 options: new Options(undefined, undefined, defaults.options),
21 handlers: [...defaults.handlers],
22 mutableDefaults: defaults.mutableDefaults,
23 };
24 Object.defineProperty(defaults, 'mutableDefaults', {
25 enumerable: true,
26 configurable: false,
27 writable: false,
28 });
29 // Got interface
30 const got = ((url, options, defaultOptions = defaults.options) => {
31 const request = new Request(url, options, defaultOptions);
32 let promise;
33 const lastHandler = (normalized) => {
34 // Note: `options` is `undefined` when `new Options(...)` fails
35 request.options = normalized;
36 request._noPipe = !normalized.isStream;
37 void request.flush();
38 if (normalized.isStream) {
39 return request;
40 }
41 promise ||= asPromise(request);
42 return promise;
43 };
44 let iteration = 0;
45 const iterateHandlers = (newOptions) => {
46 const handler = defaults.handlers[iteration++] ?? lastHandler;
47 const result = handler(newOptions, iterateHandlers);
48 if (is.promise(result) && !request.options.isStream) {
49 promise ||= asPromise(request);
50 if (result !== promise) {
51 const descriptors = Object.getOwnPropertyDescriptors(promise);
52 for (const key in descriptors) {
53 if (key in result) {
54 // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
55 delete descriptors[key];
56 }
57 }
58 // eslint-disable-next-line @typescript-eslint/no-floating-promises
59 Object.defineProperties(result, descriptors);
60 result.cancel = promise.cancel;
61 }
62 }
63 return result;
64 };
65 return iterateHandlers(request.options);
66 });
67 got.extend = (...instancesOrOptions) => {
68 const options = new Options(undefined, undefined, defaults.options);
69 const handlers = [...defaults.handlers];
70 let mutableDefaults;
71 for (const value of instancesOrOptions) {
72 if (isGotInstance(value)) {
73 options.merge(value.defaults.options);
74 handlers.push(...value.defaults.handlers);
75 mutableDefaults = value.defaults.mutableDefaults;
76 }
77 else {
78 options.merge(value);
79 if (value.handlers) {
80 handlers.push(...value.handlers);
81 }
82 mutableDefaults = value.mutableDefaults;
83 }
84 }
85 return create({
86 options,
87 handlers,
88 mutableDefaults: Boolean(mutableDefaults),
89 });
90 };
91 // Pagination
92 const paginateEach = (async function* (url, options) {
93 let normalizedOptions = new Options(url, options, defaults.options);
94 normalizedOptions.resolveBodyOnly = false;
95 const { pagination } = normalizedOptions;
96 assert.function_(pagination.transform);
97 assert.function_(pagination.shouldContinue);
98 assert.function_(pagination.filter);
99 assert.function_(pagination.paginate);
100 assert.number(pagination.countLimit);
101 assert.number(pagination.requestLimit);
102 assert.number(pagination.backoff);
103 const allItems = [];
104 let { countLimit } = pagination;
105 let numberOfRequests = 0;
106 while (numberOfRequests < pagination.requestLimit) {
107 if (numberOfRequests !== 0) {
108 // eslint-disable-next-line no-await-in-loop
109 await delay(pagination.backoff);
110 }
111 // eslint-disable-next-line no-await-in-loop
112 const response = (await got(undefined, undefined, normalizedOptions));
113 // eslint-disable-next-line no-await-in-loop
114 const parsed = await pagination.transform(response);
115 const currentItems = [];
116 assert.array(parsed);
117 for (const item of parsed) {
118 if (pagination.filter({ item, currentItems, allItems })) {
119 if (!pagination.shouldContinue({ item, currentItems, allItems })) {
120 return;
121 }
122 yield item;
123 if (pagination.stackAllItems) {
124 allItems.push(item);
125 }
126 currentItems.push(item);
127 if (--countLimit <= 0) {
128 return;
129 }
130 }
131 }
132 const optionsToMerge = pagination.paginate({
133 response,
134 currentItems,
135 allItems,
136 });
137 if (optionsToMerge === false) {
138 return;
139 }
140 if (optionsToMerge === response.request.options) {
141 normalizedOptions = response.request.options;
142 }
143 else {
144 normalizedOptions.merge(optionsToMerge);
145 assert.any([is.urlInstance, is.undefined], optionsToMerge.url);
146 if (optionsToMerge.url !== undefined) {
147 normalizedOptions.prefixUrl = '';
148 normalizedOptions.url = optionsToMerge.url;
149 }
150 }
151 numberOfRequests++;
152 }
153 });
154 got.paginate = paginateEach;
155 got.paginate.all = (async (url, options) => {
156 const results = [];
157 for await (const item of paginateEach(url, options)) {
158 results.push(item);
159 }
160 return results;
161 });
162 // For those who like very descriptive names
163 got.paginate.each = paginateEach;
164 // Stream API
165 got.stream = ((url, options) => got(url, { ...options, isStream: true }));
166 // Shortcuts
167 for (const method of aliases) {
168 got[method] = ((url, options) => got(url, { ...options, method }));
169 got.stream[method] = ((url, options) => got(url, { ...options, method, isStream: true }));
170 }
171 if (!defaults.mutableDefaults) {
172 Object.freeze(defaults.handlers);
173 defaults.options.freeze();
174 }
175 Object.defineProperty(got, 'defaults', {
176 value: defaults,
177 writable: false,
178 configurable: false,
179 enumerable: true,
180 });
181 return got;
182};
183export default create;