request-got-adapter 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,306 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.prepare = prepare;
7
+ const node_http_1 = __importDefault(require("node:http"));
8
+ const node_https_1 = __importDefault(require("node:https"));
9
+ const node_querystring_1 = __importDefault(require("node:querystring"));
10
+ const form_data_1 = __importDefault(require("form-data"));
11
+ const qs_1 = __importDefault(require("qs"));
12
+ const cookies_1 = require("./cookies");
13
+ const headers_1 = require("./headers");
14
+ const oauth_1 = require("./oauth");
15
+ const GAP_OPTIONS = [
16
+ 'har',
17
+ 'aws',
18
+ 'httpSignature',
19
+ 'proxy',
20
+ 'tunnel',
21
+ 'multipart',
22
+ 'jsonReviver',
23
+ 'jsonReplacer',
24
+ 'pool',
25
+ 'removeRefererHeader',
26
+ 'preambleCRLF',
27
+ 'postambleCRLF'
28
+ ];
29
+ // request percent-encodes RFC 3986 reserved characters that encodeURIComponent leaves alone
30
+ const rfc3986 = (value) => value.replace(/[!'()*]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`);
31
+ // request-promise-core's defaultTransformations.HEAD: resolve headers instead of the empty body
32
+ const defaultHeadTransform = (_body, response, resolveWithFullResponse) => resolveWithFullResponse ? response : response.headers;
33
+ function resolveUri(options) {
34
+ let raw = options.uri ?? options.url;
35
+ if (raw !== null && typeof raw === 'object' && typeof raw.href === 'string')
36
+ raw = raw.href;
37
+ if (options.baseUrl !== undefined) {
38
+ if (typeof options.baseUrl !== 'string')
39
+ throw new Error('options.baseUrl must be a string');
40
+ if (typeof raw !== 'string')
41
+ throw new Error('options.uri must be a string when using options.baseUrl');
42
+ if (raw.startsWith('//') || /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(raw)) {
43
+ throw new Error('options.uri must be a path when using options.baseUrl');
44
+ }
45
+ const baseEndsWithSlash = options.baseUrl.endsWith('/');
46
+ const uriStartsWithSlash = raw.startsWith('/');
47
+ if (baseEndsWithSlash && uriStartsWithSlash)
48
+ return options.baseUrl + raw.slice(1);
49
+ if (baseEndsWithSlash || uriStartsWithSlash || raw === '')
50
+ return options.baseUrl + raw;
51
+ return `${options.baseUrl}/${raw}`;
52
+ }
53
+ if (raw === undefined || raw === null)
54
+ throw new Error('options.uri is a required argument');
55
+ if (typeof raw !== 'string')
56
+ throw new Error('options.uri must be a string');
57
+ if (!/^https?:/.test(raw))
58
+ throw new Error(`Invalid URI "${raw}"`);
59
+ return raw;
60
+ }
61
+ function applyQs(urlString, options) {
62
+ if (options.qs === undefined || Object.keys(options.qs).length === 0)
63
+ return urlString;
64
+ const queryIndex = urlString.indexOf('?');
65
+ const base = queryIndex === -1 ? urlString : urlString.slice(0, queryIndex);
66
+ const existing = queryIndex === -1 ? '' : urlString.slice(queryIndex + 1);
67
+ let serialized;
68
+ if (options.useQuerystring === true) {
69
+ const merged = { ...node_querystring_1.default.parse(existing), ...options.qs };
70
+ serialized = node_querystring_1.default.stringify(merged);
71
+ }
72
+ else {
73
+ const merged = { ...qs_1.default.parse(existing, options.qsParseOptions), ...options.qs };
74
+ serialized = qs_1.default.stringify(merged, options.qsStringifyOptions);
75
+ }
76
+ serialized = rfc3986(serialized);
77
+ return serialized === '' ? base : `${base}?${serialized}`;
78
+ }
79
+ function prepareBody(options, headers) {
80
+ let body = options.body;
81
+ let jsonMode = false;
82
+ if (options.json !== undefined && options.json !== false) {
83
+ jsonMode = true;
84
+ (0, headers_1.setHeaderIfAbsent)(headers, 'accept', 'application/json');
85
+ if (options.json !== true)
86
+ body = options.json;
87
+ }
88
+ if (options.form !== undefined) {
89
+ body =
90
+ typeof options.form === 'string'
91
+ ? rfc3986(options.form)
92
+ : rfc3986(qs_1.default.stringify(options.form, options.qsStringifyOptions));
93
+ (0, headers_1.setHeaderIfAbsent)(headers, 'content-type', 'application/x-www-form-urlencoded');
94
+ return { body, jsonMode };
95
+ }
96
+ if (options.formData !== undefined) {
97
+ const formData = new form_data_1.default();
98
+ for (const [field, entry] of Object.entries(options.formData)) {
99
+ appendFormDataEntry(formData, field, entry);
100
+ }
101
+ for (const [name, value] of Object.entries(formData.getHeaders())) {
102
+ (0, headers_1.setHeaderIfAbsent)(headers, name, value);
103
+ }
104
+ return { body: formData, jsonMode };
105
+ }
106
+ if (body !== undefined) {
107
+ if (jsonMode) {
108
+ const contentType = (0, headers_1.getHeader)(headers, 'content-type');
109
+ if (typeof contentType === 'string' && /^application\/x-www-form-urlencoded\b/.test(contentType)) {
110
+ body = rfc3986(String(body));
111
+ }
112
+ else {
113
+ body = JSON.stringify(body);
114
+ (0, headers_1.setHeaderIfAbsent)(headers, 'content-type', 'application/json');
115
+ }
116
+ }
117
+ else if (typeof body !== 'string' && !Buffer.isBuffer(body)) {
118
+ throw new TypeError('Argument error, options.body.');
119
+ }
120
+ }
121
+ return { body, jsonMode };
122
+ }
123
+ function appendFormDataEntry(formData, field, entry) {
124
+ if (Array.isArray(entry)) {
125
+ for (const item of entry)
126
+ appendFormDataEntry(formData, field, item);
127
+ return;
128
+ }
129
+ if (entry !== null && typeof entry === 'object' && 'value' in entry) {
130
+ const { value, options } = entry;
131
+ formData.append(field, value, options);
132
+ return;
133
+ }
134
+ formData.append(field, typeof entry === 'number' ? String(entry) : entry);
135
+ }
136
+ function applyImmediateAuth(options, headers) {
137
+ const auth = options.auth;
138
+ if (!auth)
139
+ return undefined;
140
+ if (auth.sendImmediately === false)
141
+ return auth;
142
+ if (auth.bearer !== undefined) {
143
+ const token = typeof auth.bearer === 'function' ? auth.bearer() : auth.bearer;
144
+ (0, headers_1.setHeaderIfAbsent)(headers, 'authorization', `Bearer ${token}`);
145
+ }
146
+ else {
147
+ const user = auth.user ?? auth.username ?? '';
148
+ const pass = auth.pass ?? auth.password ?? '';
149
+ (0, headers_1.setHeaderIfAbsent)(headers, 'authorization', `Basic ${Buffer.from(`${user}:${pass}`).toString('base64')}`);
150
+ }
151
+ return undefined;
152
+ }
153
+ // request pools agents keyed by their options; cache here so repeated calls reuse sockets
154
+ const agentCache = new Map();
155
+ function cachedAgents(agentOptions) {
156
+ const key = JSON.stringify(agentOptions);
157
+ let agents = agentCache.get(key);
158
+ if (!agents) {
159
+ agents = { http: new node_http_1.default.Agent(agentOptions), https: new node_https_1.default.Agent(agentOptions) };
160
+ agentCache.set(key, agents);
161
+ }
162
+ return agents;
163
+ }
164
+ function resolveAgents(options) {
165
+ if (options.agent)
166
+ return { http: options.agent, https: options.agent };
167
+ if (options.forever === true) {
168
+ return cachedAgents({ keepAlive: true, ...options.agentOptions });
169
+ }
170
+ if (options.agentOptions) {
171
+ return cachedAgents(options.agentOptions);
172
+ }
173
+ return undefined;
174
+ }
175
+ function buildHttpsOptions(options) {
176
+ const httpsOptions = {};
177
+ if (options.strictSSL === false || options.rejectUnauthorized === false)
178
+ httpsOptions.rejectUnauthorized = false;
179
+ if (options.ca !== undefined)
180
+ httpsOptions.certificateAuthority = options.ca;
181
+ if (options.cert !== undefined)
182
+ httpsOptions.certificate = options.cert;
183
+ if (options.key !== undefined)
184
+ httpsOptions.key = options.key;
185
+ if (options.pfx !== undefined)
186
+ httpsOptions.pfx = options.pfx;
187
+ if (options.passphrase !== undefined)
188
+ httpsOptions.passphrase = options.passphrase;
189
+ return Object.keys(httpsOptions).length > 0 ? httpsOptions : undefined;
190
+ }
191
+ function prepare(rpOptions) {
192
+ for (const gapOption of GAP_OPTIONS) {
193
+ if (rpOptions[gapOption] !== undefined) {
194
+ throw new Error(`request-got-adapter: option '${gapOption}' is not implemented (see README GAPS section)`);
195
+ }
196
+ }
197
+ if (typeof rpOptions.followRedirect === 'function') {
198
+ throw new Error("request-got-adapter: option 'followRedirect' as a function is not implemented (see README GAPS section)");
199
+ }
200
+ const method = (rpOptions.method ?? 'GET').toUpperCase();
201
+ const headers = { ...rpOptions.headers };
202
+ let url = applyQs(resolveUri(rpOptions), rpOptions);
203
+ const prepared = prepareBody(rpOptions, headers);
204
+ let body = prepared.body;
205
+ const jsonMode = prepared.jsonMode;
206
+ const deferredAuth = applyImmediateAuth(rpOptions, headers);
207
+ if (rpOptions.oauth) {
208
+ const contentType = (0, headers_1.getHeader)(headers, 'content-type');
209
+ const isUrlencodedForm = typeof contentType === 'string' &&
210
+ /^application\/x-www-form-urlencoded\b/.test(contentType) &&
211
+ typeof body === 'string';
212
+ const transport = rpOptions.oauth.transport_method ?? 'header';
213
+ if (transport === 'body' && (method !== 'POST' || !isUrlencodedForm)) {
214
+ throw new Error("oauth: transport_method of 'body' requires 'POST' and content-type 'application/x-www-form-urlencoded'");
215
+ }
216
+ const oauthParams = (0, oauth_1.buildOAuthParams)(rpOptions.oauth, method, url, isUrlencodedForm ? body : undefined, typeof body === 'string' || Buffer.isBuffer(body) ? body : undefined);
217
+ if (transport === 'header') {
218
+ (0, headers_1.setHeader)(headers, 'Authorization', (0, oauth_1.toOAuthHeader)(oauthParams));
219
+ }
220
+ else if (transport === 'query') {
221
+ url += (url.includes('?') ? '&' : '?') + (0, oauth_1.toOAuthQuery)(oauthParams);
222
+ }
223
+ else {
224
+ body = `${body}&${(0, oauth_1.toOAuthQuery)(oauthParams)}`;
225
+ }
226
+ }
227
+ const gotHeaders = { ...headers };
228
+ // got injects its own user-agent unless explicitly disabled; request sends none
229
+ if (!(0, headers_1.hasHeader)(gotHeaders, 'user-agent'))
230
+ gotHeaders['user-agent'] = undefined;
231
+ const decompress = rpOptions.gzip === true;
232
+ if (decompress)
233
+ (0, headers_1.setHeaderIfAbsent)(gotHeaders, 'accept-encoding', 'gzip, deflate');
234
+ const followRedirect = method === 'GET' || method === 'HEAD' ? rpOptions.followRedirect !== false : rpOptions.followAllRedirects === true;
235
+ const gotOptions = {
236
+ method,
237
+ headers: gotHeaders,
238
+ throwHttpErrors: false,
239
+ retry: { limit: 0 },
240
+ decompress,
241
+ followRedirect,
242
+ maxRedirects: rpOptions.maxRedirects ?? 10,
243
+ allowGetBody: true,
244
+ hooks: {
245
+ beforeRedirect: [
246
+ (updatedOptions, plainResponse) => {
247
+ const status = plainResponse.statusCode;
248
+ if (rpOptions.followOriginalHttpMethod === true) {
249
+ updatedOptions.method = method;
250
+ return;
251
+ }
252
+ if (status !== 307 && status !== 308 && updatedOptions.method !== 'HEAD') {
253
+ updatedOptions.method = 'GET';
254
+ updatedOptions.body = undefined;
255
+ (0, headers_1.deleteHeader)(updatedOptions.headers, 'content-type');
256
+ (0, headers_1.deleteHeader)(updatedOptions.headers, 'content-length');
257
+ }
258
+ }
259
+ ]
260
+ }
261
+ };
262
+ if (body !== undefined)
263
+ gotOptions.body = body;
264
+ if (typeof rpOptions.timeout === 'number') {
265
+ gotOptions.timeout = {
266
+ lookup: rpOptions.timeout,
267
+ connect: rpOptions.timeout,
268
+ secureConnect: rpOptions.timeout,
269
+ response: rpOptions.timeout
270
+ };
271
+ }
272
+ const httpsOptions = buildHttpsOptions(rpOptions);
273
+ if (httpsOptions)
274
+ gotOptions.https = httpsOptions;
275
+ const agent = resolveAgents(rpOptions);
276
+ if (agent)
277
+ gotOptions.agent = agent;
278
+ const cookieJar = (0, cookies_1.resolveCookieJar)(rpOptions.jar);
279
+ if (cookieJar)
280
+ gotOptions.cookieJar = cookieJar;
281
+ if (rpOptions.localAddress !== undefined)
282
+ gotOptions.localAddress = rpOptions.localAddress;
283
+ if (rpOptions.family !== undefined)
284
+ gotOptions.dnsLookupIpVersion = rpOptions.family;
285
+ if (rpOptions.lookup !== undefined)
286
+ gotOptions.dnsLookup = rpOptions.lookup;
287
+ return {
288
+ url,
289
+ method,
290
+ gotOptions,
291
+ requestHeaders: headers,
292
+ simple: rpOptions.simple !== false,
293
+ resolveWithFullResponse: rpOptions.resolveWithFullResponse === true,
294
+ jsonMode,
295
+ encoding: rpOptions.encoding,
296
+ transform: typeof rpOptions.transform === 'function'
297
+ ? rpOptions.transform
298
+ : method === 'HEAD'
299
+ ? defaultHeadTransform
300
+ : undefined,
301
+ transform2xxOnly: rpOptions.transform2xxOnly === true,
302
+ time: rpOptions.time === true,
303
+ deferredAuth,
304
+ rpOptions
305
+ };
306
+ }
package/errors.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/errors'
package/errors.js ADDED
@@ -0,0 +1,3 @@
1
+ // Mirrors require('request-promise-native/errors') for consumers using classic
2
+ // (non-"exports") module resolution.
3
+ module.exports = require('./dist/errors')
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "request-got-adapter",
3
+ "version": "0.1.0",
4
+ "description": "Drop-in replacement for request-promise-native, backed by got. Same API, same options, same errors — no deprecated request-family dependencies.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/toriihq/request-got-adapter.git"
9
+ },
10
+ "keywords": [
11
+ "request",
12
+ "request-promise",
13
+ "request-promise-native",
14
+ "got",
15
+ "http",
16
+ "https",
17
+ "client",
18
+ "drop-in",
19
+ "adapter",
20
+ "migration"
21
+ ],
22
+ "main": "dist/index.js",
23
+ "types": "dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "default": "./dist/index.js"
28
+ },
29
+ "./errors": {
30
+ "types": "./errors.d.ts",
31
+ "default": "./errors.js"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "errors.js",
38
+ "errors.d.ts"
39
+ ],
40
+ "engines": {
41
+ "node": ">=22"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc",
45
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
46
+ "lint": "biome check src test type-fixtures",
47
+ "lint:fix": "biome check --write src test type-fixtures",
48
+ "typecheck": "tsc --noEmit",
49
+ "prepublishOnly": "npm run build"
50
+ },
51
+ "dependencies": {
52
+ "form-data": "^4.0.6",
53
+ "got": "^14.6.6",
54
+ "qs": "^6.15.3",
55
+ "tough-cookie": "^6.0.2"
56
+ },
57
+ "devDependencies": {
58
+ "@biomejs/biome": "^1.9.4",
59
+ "@types/jest": "^29.5.14",
60
+ "@types/node": "^22.0.0",
61
+ "@types/qs": "^6.14.0",
62
+ "jest": "^29.7.0",
63
+ "ts-jest": "^29.2.5",
64
+ "typescript": "^5.6.0"
65
+ }
66
+ }