UNPKG

7.07 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 if (!promise) {
42 promise = asPromise(request);
43 }
44 return promise;
45 };
46 let iteration = 0;
47 const iterateHandlers = (newOptions) => {
48 const handler = defaults.handlers[iteration++] ?? lastHandler;
49 const result = handler(newOptions, iterateHandlers);
50 if (is.promise(result) && !request.options.isStream) {
51 if (!promise) {
52 promise = asPromise(request);
53 }
54 if (result !== promise) {
55 const descriptors = Object.getOwnPropertyDescriptors(promise);
56 for (const key in descriptors) {
57 if (key in result) {
58 // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
59 delete descriptors[key];
60 }
61 }
62 // eslint-disable-next-line @typescript-eslint/no-floating-promises
63 Object.defineProperties(result, descriptors);
64 result.cancel = promise.cancel;
65 }
66 }
67 return result;
68 };
69 return iterateHandlers(request.options);
70 });
71 got.extend = (...instancesOrOptions) => {
72 const options = new Options(undefined, undefined, defaults.options);
73 const handlers = [...defaults.handlers];
74 let mutableDefaults;
75 for (const value of instancesOrOptions) {
76 if (isGotInstance(value)) {
77 options.merge(value.defaults.options);
78 handlers.push(...value.defaults.handlers);
79 mutableDefaults = value.defaults.mutableDefaults;
80 }
81 else {
82 options.merge(value);
83 if (value.handlers) {
84 handlers.push(...value.handlers);
85 }
86 mutableDefaults = value.mutableDefaults;
87 }
88 }
89 return create({
90 options,
91 handlers,
92 mutableDefaults: Boolean(mutableDefaults),
93 });
94 };
95 // Pagination
96 const paginateEach = (async function* (url, options) {
97 let normalizedOptions = new Options(url, options, defaults.options);
98 normalizedOptions.resolveBodyOnly = false;
99 const { pagination } = normalizedOptions;
100 assert.function_(pagination.transform);
101 assert.function_(pagination.shouldContinue);
102 assert.function_(pagination.filter);
103 assert.function_(pagination.paginate);
104 assert.number(pagination.countLimit);
105 assert.number(pagination.requestLimit);
106 assert.number(pagination.backoff);
107 const allItems = [];
108 let { countLimit } = pagination;
109 let numberOfRequests = 0;
110 while (numberOfRequests < pagination.requestLimit) {
111 if (numberOfRequests !== 0) {
112 // eslint-disable-next-line no-await-in-loop
113 await delay(pagination.backoff);
114 }
115 // eslint-disable-next-line no-await-in-loop
116 const response = (await got(undefined, undefined, normalizedOptions));
117 // eslint-disable-next-line no-await-in-loop
118 const parsed = await pagination.transform(response);
119 const currentItems = [];
120 assert.array(parsed);
121 for (const item of parsed) {
122 if (pagination.filter({ item, currentItems, allItems })) {
123 if (!pagination.shouldContinue({ item, currentItems, allItems })) {
124 return;
125 }
126 yield item;
127 if (pagination.stackAllItems) {
128 allItems.push(item);
129 }
130 currentItems.push(item);
131 if (--countLimit <= 0) {
132 return;
133 }
134 }
135 }
136 const optionsToMerge = pagination.paginate({
137 response,
138 currentItems,
139 allItems,
140 });
141 if (optionsToMerge === false) {
142 return;
143 }
144 if (optionsToMerge === response.request.options) {
145 normalizedOptions = response.request.options;
146 }
147 else {
148 normalizedOptions.merge(optionsToMerge);
149 assert.any([is.urlInstance, is.undefined], optionsToMerge.url);
150 if (optionsToMerge.url !== undefined) {
151 normalizedOptions.prefixUrl = '';
152 normalizedOptions.url = optionsToMerge.url;
153 }
154 }
155 numberOfRequests++;
156 }
157 });
158 got.paginate = paginateEach;
159 got.paginate.all = (async (url, options) => {
160 const results = [];
161 for await (const item of paginateEach(url, options)) {
162 results.push(item);
163 }
164 return results;
165 });
166 // For those who like very descriptive names
167 got.paginate.each = paginateEach;
168 // Stream API
169 got.stream = ((url, options) => got(url, { ...options, isStream: true }));
170 // Shortcuts
171 for (const method of aliases) {
172 got[method] = ((url, options) => got(url, { ...options, method }));
173 got.stream[method] = ((url, options) => got(url, { ...options, method, isStream: true }));
174 }
175 if (!defaults.mutableDefaults) {
176 Object.freeze(defaults.handlers);
177 defaults.options.freeze();
178 }
179 Object.defineProperty(got, 'defaults', {
180 value: defaults,
181 writable: false,
182 configurable: false,
183 enumerable: true,
184 });
185 return got;
186};
187export default create;