1 | 'use strict';
|
2 |
|
3 | const transport = require(typeof window !== 'undefined' ? './browser' : './node');
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | class Snekfetch extends transport.Parent {
|
11 | |
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | |
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | constructor(method, url, opts = {}) {
|
33 | super();
|
34 | this.options = Object.assign({
|
35 | qs: transport.querystring,
|
36 | method,
|
37 | url,
|
38 | redirect: 'follow',
|
39 | }, opts, {
|
40 | headers: {},
|
41 | query: undefined,
|
42 | data: undefined,
|
43 | });
|
44 | if (opts.headers)
|
45 | this.set(opts.headers);
|
46 | if (opts.query)
|
47 | this.query(opts.query);
|
48 | if (opts.data)
|
49 | this.send(opts.data);
|
50 | }
|
51 |
|
52 | |
53 |
|
54 |
|
55 |
|
56 |
|
57 |
|
58 | query(name, value) {
|
59 | if (this.options.query === undefined)
|
60 | this.options.query = {};
|
61 | if (typeof name === 'object')
|
62 | Object.assign(this.options.query, name);
|
63 | else
|
64 | this.options.query[name] = value;
|
65 |
|
66 | return this;
|
67 | }
|
68 |
|
69 | |
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | set(name, value) {
|
76 | if (typeof name === 'object') {
|
77 | for (const [k, v] of Object.entries(name))
|
78 | this.options.headers[k.toLowerCase()] = v;
|
79 | } else {
|
80 | this.options.headers[name.toLowerCase()] = value;
|
81 | }
|
82 |
|
83 | return this;
|
84 | }
|
85 |
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | attach(...args) {
|
94 | const form = this.options.data instanceof transport.FormData ?
|
95 | this.options.data : this.options.data = new transport.FormData();
|
96 | if (typeof args[0] === 'object') {
|
97 | for (const [k, v] of Object.entries(args[0]))
|
98 | this.attach(k, v);
|
99 | } else {
|
100 | form.append(...args);
|
101 | }
|
102 |
|
103 | return this;
|
104 | }
|
105 |
|
106 | |
107 |
|
108 |
|
109 |
|
110 |
|
111 | send(data) {
|
112 | if (data instanceof transport.FormData || transport.shouldSendRaw(data)) {
|
113 | this.options.data = data;
|
114 | } else if (data !== null && typeof data === 'object') {
|
115 | const header = this.options.headers['content-type'];
|
116 | let serialize;
|
117 | if (header) {
|
118 | if (header.includes('application/json'))
|
119 | serialize = JSON.stringify;
|
120 | else if (header.includes('urlencoded'))
|
121 | serialize = this.options.qs.stringify;
|
122 | } else {
|
123 | this.set('Content-Type', 'application/json');
|
124 | serialize = JSON.stringify;
|
125 | }
|
126 | this.options.data = serialize(data);
|
127 | } else {
|
128 | this.options.data = data;
|
129 | }
|
130 | return this;
|
131 | }
|
132 |
|
133 | then(resolver, rejector) {
|
134 | if (this._response)
|
135 | return this._response.then(resolver, rejector);
|
136 | this._finalizeRequest();
|
137 |
|
138 | return this._response = transport.request(this)
|
139 | .then(({ raw, headers, statusCode, statusText }) => {
|
140 |
|
141 | const self = this;
|
142 | |
143 |
|
144 |
|
145 |
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 | const res = {
|
154 | request: this.request,
|
155 | get body() {
|
156 | delete res.body;
|
157 | const type = res.headers['content-type'];
|
158 | if (raw instanceof ArrayBuffer)
|
159 | raw = new window.TextDecoder('utf8').decode(raw);
|
160 | if (/application\/json/.test(type)) {
|
161 | try {
|
162 | res.body = JSON.parse(raw);
|
163 | } catch (err) {
|
164 | res.body = String(raw);
|
165 | }
|
166 | } else if (/application\/x-www-form-urlencoded/.test(type)) {
|
167 | res.body = self.options.qs.parse(String(raw));
|
168 | } else {
|
169 | res.body = raw;
|
170 | }
|
171 | return res.body;
|
172 | },
|
173 | raw,
|
174 | ok: statusCode >= 200 && statusCode < 400,
|
175 | headers,
|
176 | statusCode,
|
177 | statusText,
|
178 | };
|
179 |
|
180 | if (res.ok)
|
181 | return res;
|
182 | const err = new Error(`${statusCode} ${statusText}`.trim());
|
183 | Object.assign(err, res);
|
184 | return Promise.reject(err);
|
185 | })
|
186 | .then(resolver, rejector);
|
187 | }
|
188 |
|
189 | catch(rejector) {
|
190 | return this.then(null, rejector);
|
191 | }
|
192 |
|
193 | |
194 |
|
195 |
|
196 |
|
197 |
|
198 | end(cb) {
|
199 | return this.then(
|
200 | (res) => (cb ? cb(null, res) : res),
|
201 | (err) => (cb ? cb(err, err.status ? err : null) : Promise.reject(err)),
|
202 | );
|
203 | }
|
204 |
|
205 | _finalizeRequest() {
|
206 | if (this.options.method !== 'HEAD')
|
207 | this.set('Accept-Encoding', 'gzip, deflate');
|
208 | if (this.options.data && this.options.data.getBoundary)
|
209 | this.set('Content-Type', `multipart/form-data; boundary=${this.options.data.getBoundary()}`);
|
210 |
|
211 | if (this.options.query) {
|
212 | const [url, query] = this.options.url.split('?');
|
213 | this.options.url = `${url}?${this.options.qs.stringify(this.options.query)}${query ? `&${query}` : ''}`;
|
214 | }
|
215 | }
|
216 |
|
217 | _read() {
|
218 | this.resume();
|
219 | if (this._response)
|
220 | return;
|
221 | this.catch((err) => this.emit('error', err));
|
222 | }
|
223 | }
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 |
|
233 | Snekfetch.METHODS = transport.METHODS.filter((m) => m !== 'M-SEARCH');
|
234 | for (const method of Snekfetch.METHODS) {
|
235 | Snekfetch[method.toLowerCase()] = function runMethod(url, opts) {
|
236 | const Constructor = this && this.prototype instanceof Snekfetch ? this : Snekfetch;
|
237 | return new Constructor(method, url, opts);
|
238 | };
|
239 | }
|
240 |
|
241 | module.exports = Snekfetch;
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|