@mepkg/maxios 1.0.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.
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@mepkg/maxios",
3
+ "type": "module",
4
+ "main": "src/index.js",
5
+ "files": [
6
+ "src"
7
+ ],
8
+ "private": false,
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "scripts": {
13
+ "test": "node --test 'tests/**/*.test.js'",
14
+ "test:watch": "node --test --watch 'tests/**/*.test.js'"
15
+ },
16
+ "dependencies": {
17
+ "axios": "1.16.0",
18
+ "hpagent": "^1.2.0",
19
+ "lodash-es": "^4.18.1",
20
+ "melperjs": "^15.0.0"
21
+ },
22
+ "version": "1.0.0"
23
+ }
package/src/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './maxios.js';
2
+ export * from './tls-client.js';
package/src/maxios.js ADDED
@@ -0,0 +1,122 @@
1
+ import {HttpProxyAgent, HttpsProxyAgent} from "hpagent";
2
+ import {cookieDict, cookieHeader} from "melperjs";
3
+ import axios from "axios";
4
+ import {isNil, omitBy} from "lodash-es";
5
+
6
+
7
+ export class Maxios {
8
+ constructor(proxy = null, config = {}, cookies = {}) {
9
+ this.proxy = proxy;
10
+ this.cookies = cookies;
11
+ config = config || {};
12
+
13
+ const axiosOptions = {
14
+ responseType: 'text',
15
+ timeout: config.timeout ?? 10000
16
+ };
17
+ if (this.proxy) {
18
+ const proxyTimeout = axiosOptions.timeout * 0.75 || undefined;
19
+ axiosOptions.httpsAgent = new HttpsProxyAgent({proxy, timeout: proxyTimeout});
20
+ axiosOptions.httpAgent = new HttpProxyAgent({proxy, timeout: proxyTimeout});
21
+ }
22
+ this.client = axios.create(axiosOptions);
23
+
24
+ this.client.interceptors.request.use((request) => {
25
+ this.#requestCookies(request);
26
+ return request;
27
+ });
28
+ this.client.interceptors.response.use(response => {
29
+ this.#responseCookies(response);
30
+ this.#attachJsonMethod(response);
31
+ this.#attachFinalUrl(response);
32
+ return response;
33
+ }, error => {
34
+ if (error.response) {
35
+ this.#responseCookies(error.response);
36
+ this.#attachJsonMethod(error.response);
37
+ this.#attachFinalUrl(error.response);
38
+ }
39
+ return Promise.reject(error);
40
+ });
41
+ }
42
+
43
+ async request(config = {}) {
44
+ config.headers = omitBy(config.headers, isNil);
45
+ return this.client.request(config);
46
+ }
47
+
48
+ async get(url, config = {}) {
49
+ config.headers = omitBy(config.headers, isNil);
50
+ return this.client.get(url, config);
51
+ }
52
+
53
+ async post(url, data = {}, config = {}) {
54
+ config.headers = omitBy(config.headers, isNil);
55
+ return this.client.post(url, data, config);
56
+ }
57
+
58
+ async put(url, data = {}, config = {}) {
59
+ config.headers = omitBy(config.headers, isNil);
60
+ return this.client.put(url, data, config);
61
+ }
62
+
63
+ async delete(url, config = {}) {
64
+ config.headers = omitBy(config.headers, isNil);
65
+ return this.client.delete(url, config);
66
+ }
67
+
68
+ async patch(url, data = {}, config = {}) {
69
+ config.headers = omitBy(config.headers, isNil);
70
+ return this.client.patch(url, data, config);
71
+ }
72
+
73
+ async head(url, config = {}) {
74
+ config.headers = omitBy(config.headers, isNil);
75
+ return this.client.head(url, config);
76
+ }
77
+
78
+ async options(url, config = {}) {
79
+ config.headers = omitBy(config.headers, isNil);
80
+ return this.client.options(url, config);
81
+ }
82
+
83
+ #attachFinalUrl = (response) => {
84
+ response.finalUrl = response?.request?.res?.responseUrl || "";
85
+ }
86
+
87
+ #attachJsonMethod = (response) => {
88
+ response.json = function (isSilent = false) {
89
+ try {
90
+ return JSON.parse(this.data);
91
+ } catch (error) {
92
+ if (isSilent) {
93
+ return null;
94
+ }
95
+ throw error;
96
+ }
97
+ };
98
+ }
99
+
100
+ #responseCookies = (response) => {
101
+ if (!this.cookies) return;
102
+ const parsed = cookieDict(response);
103
+ Object.keys(parsed).forEach(key => {
104
+ if (!parsed[key]) {
105
+ delete parsed[key];
106
+ }
107
+ });
108
+ this.cookies = {...this.cookies, ...parsed};
109
+ }
110
+
111
+ #requestCookies = (request) => {
112
+ if (!this.cookies) return;
113
+ request.headers = request.headers || {};
114
+ if (!request.headers.cookie && !request.headers.Cookie) {
115
+ const header = cookieHeader(this.cookies);
116
+ header && (request.headers.cookie = header);
117
+ }
118
+ }
119
+
120
+ }
121
+
122
+ export const maxios = new Maxios(null, {}, false);
@@ -0,0 +1,192 @@
1
+ import {cookieDict, cookieHeader, Exception} from "melperjs";
2
+ import {isNil, omitBy} from "lodash-es";
3
+ import {maxios} from "./maxios.js";
4
+
5
+
6
+ export const TLS_SERVER = {
7
+ url: null,
8
+ key: null
9
+ };
10
+
11
+ export class TlsClient {
12
+ constructor(proxy = null, {
13
+ tlsServer = TLS_SERVER,
14
+ timeout = 30000,
15
+ httpVersion = 2,
16
+ impersonate = "chrome_windows",
17
+ visit = null,
18
+ maxRedirects = 0,
19
+ base64 = false,
20
+ validateStatus = (status) => (status >= 200 && status <= 299) || (maxRedirects && status >= 300 && status <= 399)
21
+ } = {}, cookies = {}) {
22
+ this.proxy = proxy;
23
+ this.config = {timeout, httpVersion, impersonate, visit, maxRedirects, base64, validateStatus};
24
+ this.cookies = cookies;
25
+ this.tlsServer = tlsServer;
26
+ if (!this.tlsServer.url) {
27
+ throw Exception("TLS Server URL is not defined");
28
+ }
29
+ }
30
+
31
+ async request({
32
+ headers,
33
+ data,
34
+ method,
35
+ url,
36
+ params,
37
+ timeout,
38
+ httpVersion,
39
+ proxy,
40
+ impersonate,
41
+ visit,
42
+ maxRedirects,
43
+ base64,
44
+ validateStatus
45
+ } = {}) {
46
+ let tlsResponse;
47
+ try {
48
+ {
49
+ const adjustedHeaders = omitBy(headers, isNil);
50
+ headers = {};
51
+ for (const key in adjustedHeaders) {
52
+ headers[key.toLowerCase()] = adjustedHeaders[key];
53
+ }
54
+ }
55
+ if (typeof data === "object") {
56
+ data = omitBy(data, isNil);
57
+ if (headers['content-type'] === 'application/json') {
58
+ data = JSON.stringify(data);
59
+ } else {
60
+ data = new URLSearchParams(data).toString();
61
+ }
62
+ }
63
+ if (params) {
64
+ const parsedUrl = new URL(url);
65
+ for (const key in params) {
66
+ parsedUrl.searchParams.append(key, params[key].toString());
67
+ }
68
+ url = parsedUrl.toString();
69
+ }
70
+ maxRedirects = maxRedirects ?? this.config.maxRedirects;
71
+ const request = {
72
+ "method": method || "GET",
73
+ "url": url,
74
+ "headers": headers || {},
75
+ "data": data || "",
76
+ "timeout": timeout || this.config.timeout,
77
+ "httpVersion": httpVersion || this.config.httpVersion,
78
+ "proxy": proxy || this.proxy || undefined,
79
+ "impersonate": impersonate || this.config.impersonate,
80
+ "visit": visit || this.config.visit,
81
+ "redirects": maxRedirects,
82
+ "base64": base64 ?? this.config.base64,
83
+ "key": this.tlsServer.key
84
+ };
85
+ this.#requestCookies(request);
86
+ const axiosResponse = await maxios.post(this.tlsServer.url + "/execute", request, {
87
+ timeout: request.timeout + 1000,
88
+ maxRedirects: 0,
89
+ validateStatus: status => status === 200
90
+ });
91
+ const result = axiosResponse.json();
92
+
93
+ const responseHeaders = result.headers || {};
94
+ for (const key in responseHeaders) {
95
+ if (responseHeaders?.[key]?.length === 1) {
96
+ responseHeaders[key] = responseHeaders[key][0];
97
+ }
98
+ }
99
+ tlsResponse = {
100
+ status: result.code,
101
+ headers: responseHeaders,
102
+ data: result.body,
103
+ finalUrl: result.final_url,
104
+ json: function (isSilent = false) {
105
+ try {
106
+ return JSON.parse(this.data);
107
+ } catch (error) {
108
+ if (isSilent) {
109
+ return null;
110
+ }
111
+ throw error;
112
+ }
113
+ }
114
+ };
115
+ this.#responseCookies(tlsResponse);
116
+ validateStatus = validateStatus || this.config.validateStatus;
117
+ if (validateStatus(result.code)) {
118
+ return tlsResponse;
119
+ }
120
+ } catch (e) {
121
+ throw Exception(`TlsClientException: ${e.response?.data || e.message}`, e.response, 'TlsClientException');
122
+ }
123
+
124
+ throw Exception("Request failed with status code " + tlsResponse?.status, tlsResponse, "TlsClientResponseException");
125
+ }
126
+
127
+ async get(url, config = {}) {
128
+ config.url = url;
129
+ config.method = "GET";
130
+ return this.request(config);
131
+ }
132
+
133
+ async post(url, data, config = {}) {
134
+ config.url = url;
135
+ config.method = "POST";
136
+ config.data = data;
137
+ return this.request(config);
138
+ }
139
+
140
+ async put(url, data, config = {}) {
141
+ config.url = url;
142
+ config.method = "PUT";
143
+ config.data = data;
144
+ return this.request(config);
145
+ }
146
+
147
+ async delete(url, config = {}) {
148
+ config.url = url;
149
+ config.method = "DELETE";
150
+ return this.request(config);
151
+ }
152
+
153
+ async patch(url, data, config = {}) {
154
+ config.url = url;
155
+ config.method = "PATCH";
156
+ config.data = data;
157
+ return this.request(config);
158
+ }
159
+
160
+ async head(url, config = {}) {
161
+ config.url = url;
162
+ config.method = "HEAD";
163
+ return this.request(config);
164
+ }
165
+
166
+ async options(url, config = {}) {
167
+ config.url = url;
168
+ config.method = "OPTIONS";
169
+ return this.request(config);
170
+ }
171
+
172
+ #responseCookies = (response) => {
173
+ if (!this.cookies) return;
174
+ const parsed = cookieDict(response);
175
+ Object.keys(parsed).forEach(key => {
176
+ if (!parsed[key]) {
177
+ delete parsed[key];
178
+ }
179
+ });
180
+ this.cookies = {...this.cookies, ...parsed};
181
+ }
182
+
183
+ #requestCookies = (request) => {
184
+ if (!this.cookies) return;
185
+ request.headers = request.headers || {};
186
+ if (!request.headers.cookie && !request.headers.Cookie) {
187
+ const header = cookieHeader(this.cookies);
188
+ header && (request.headers.cookie = header);
189
+ }
190
+ }
191
+
192
+ }