@autofleet/network 1.6.1-beta-194901dc.0 → 1.7.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 +15 -7
- package/{index.js → src/index.ts} +64 -90
- package/tsconfig.json +18 -0
- package/tsup.config.ts +14 -0
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/network",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "index.js",
|
|
6
7
|
"scripts": {
|
|
7
|
-
"test": "
|
|
8
|
+
"test": "tsx index.test.js",
|
|
9
|
+
"build": "tsup"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=18"
|
|
8
13
|
},
|
|
9
14
|
"author": "Dor Shay",
|
|
10
15
|
"license": "ISC",
|
|
@@ -13,17 +18,20 @@
|
|
|
13
18
|
"agentkeepalive": "^4.6.0",
|
|
14
19
|
"axios": "^0.29.0",
|
|
15
20
|
"axios-retry": "^4.5.0",
|
|
16
|
-
"deepmerge": "^3.
|
|
21
|
+
"deepmerge": "^4.3.1",
|
|
17
22
|
"qs": "^6.14.0"
|
|
18
23
|
},
|
|
19
24
|
"peerDependencies": {
|
|
20
|
-
"@autofleet/logger": ">=4.
|
|
25
|
+
"@autofleet/logger": ">=4.2.0"
|
|
21
26
|
},
|
|
22
27
|
"devDependencies": {
|
|
23
|
-
"@autofleet/logger": "^4.
|
|
24
|
-
"@types/axios": "^0.9.36",
|
|
28
|
+
"@autofleet/logger": "^4.2.0",
|
|
25
29
|
"@types/node": "^18.19.75",
|
|
30
|
+
"@types/qs": "^6.9.18",
|
|
26
31
|
"dotenv": "^16.4.7",
|
|
27
|
-
"nock": "^14.0.1"
|
|
32
|
+
"nock": "^14.0.1",
|
|
33
|
+
"tsup": "^8.3.6",
|
|
34
|
+
"tsx": "^4.19.2",
|
|
35
|
+
"typescript": "^5.7.3"
|
|
28
36
|
}
|
|
29
37
|
}
|
|
@@ -1,25 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/** @type {import('qs').stringify | undefined} */
|
|
19
|
-
let qsStringify;
|
|
20
|
-
const lazilyLoadQsStringify = () => {
|
|
21
|
-
qsStringify ??= require('qs').stringify;
|
|
22
|
-
return qsStringify;
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import axios, { type CreateAxiosDefaults, type AxiosRequestConfig, type AxiosInstance, type AxiosResponse } from 'axios';
|
|
3
|
+
import axiosRetry from 'axios-retry';
|
|
4
|
+
import Logger from '@autofleet/logger';
|
|
5
|
+
import merge from 'deepmerge';
|
|
6
|
+
import axiosCacheAdapter from '@autofleet/axios-cache-adapter';
|
|
7
|
+
import { HttpAgent, HttpsAgent } from 'agentkeepalive';
|
|
8
|
+
import type { stringify } from 'qs';
|
|
9
|
+
|
|
10
|
+
const { setup } = axiosCacheAdapter;
|
|
11
|
+
const safeRequire = createRequire(import.meta.url);
|
|
12
|
+
|
|
13
|
+
let qsStringify: typeof stringify | undefined;
|
|
14
|
+
const lazilyLoadQsStringify = (...args: Parameters<typeof stringify>) => {
|
|
15
|
+
qsStringify ??= safeRequire('qs').stringify;
|
|
16
|
+
return qsStringify(...args);
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
const HTTPMethods = [
|
|
@@ -30,32 +24,31 @@ const HTTPMethods = [
|
|
|
30
24
|
'put',
|
|
31
25
|
'patch',
|
|
32
26
|
'options',
|
|
33
|
-
];
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
const qsStringifyOptions: NonNullable<Parameters<typeof stringify>[1]> = { arrayFormat: 'brackets' };
|
|
30
|
+
|
|
31
|
+
interface NetworkSettings extends CreateAxiosDefaults {
|
|
32
|
+
serviceName?: string;
|
|
33
|
+
serviceUrl?: string;
|
|
34
|
+
keepAlive?: boolean;
|
|
35
|
+
logger?: import('@autofleet/logger').LoggerInstanceManager;
|
|
36
|
+
}
|
|
34
37
|
|
|
35
|
-
const defaultSettings = {
|
|
38
|
+
const defaultSettings: NetworkSettings = {
|
|
36
39
|
timeout: 10_000,
|
|
37
40
|
headers: {
|
|
38
41
|
'X-AF-AUTH': 'ANYONE',
|
|
39
42
|
'X-IAF-ORIGIN-SERVICE': process.env.AF_SERVICE_NAME || null,
|
|
40
43
|
},
|
|
41
|
-
paramsSerializer: params => lazilyLoadQsStringify(params,
|
|
44
|
+
paramsSerializer: params => lazilyLoadQsStringify(params, qsStringifyOptions),
|
|
42
45
|
'axios-retry': {
|
|
43
46
|
shouldResetTimeout: true,
|
|
44
47
|
},
|
|
45
48
|
keepAlive: true,
|
|
46
49
|
};
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
const createRequestString = (request) => `[${(request.method || '').toUpperCase()}] ${request.baseURL ?? 'unknown-base-url'}${request.url ?? '/unknown-url'}`;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @typedef {object} NetworkSettingsBase
|
|
53
|
-
* @property {string} [serviceName]
|
|
54
|
-
* @property {string} [serviceUrl]
|
|
55
|
-
* @property {boolean} [keepAlive]
|
|
56
|
-
* @property {import('@autofleet/logger').LoggerInstanceManager} [logger]
|
|
57
|
-
* @typedef {import('axios').CreateAxiosDefaults & NetworkSettingsBase} NetworkSettings
|
|
58
|
-
*/
|
|
51
|
+
const createRequestString = (request: AxiosRequestConfig): string => `[${(request.method || '').toUpperCase()}] ${request.baseURL ?? 'unknown-base-url'}${request.url ?? '/unknown-url'}`;
|
|
59
52
|
|
|
60
53
|
/*
|
|
61
54
|
The default free socket keepalive timeout, in milliseconds.
|
|
@@ -66,31 +59,21 @@ const createRequestString = (request) => `[${(request.method || '').toUpperCase(
|
|
|
66
59
|
There is also autoscaling reason for that, if we don't timeout at some point,
|
|
67
60
|
new pods that were auto scaled will not be used.
|
|
68
61
|
*/
|
|
69
|
-
const FREE_SOCKET_TIMEOUT = process.env.FREE_SOCKET_TIMEOUT || 5_000;
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
head;
|
|
85
|
-
/** @type {import('axios').AxiosInstance['put']} */
|
|
86
|
-
put;
|
|
87
|
-
/** @type {import('axios').AxiosInstance['patch']} */
|
|
88
|
-
patch;
|
|
89
|
-
/** @type {import('axios').AxiosInstance['options']} */
|
|
90
|
-
options;
|
|
91
|
-
|
|
92
|
-
/** @param {NetworkSettings} [settings] */
|
|
93
|
-
constructor(settings = {}) {
|
|
62
|
+
const FREE_SOCKET_TIMEOUT = Number.parseInt(process.env.FREE_SOCKET_TIMEOUT, 10) || 5_000;
|
|
63
|
+
|
|
64
|
+
export default class Network {
|
|
65
|
+
private readonly settings: NetworkSettings;
|
|
66
|
+
private readonly axios: AxiosInstance;
|
|
67
|
+
|
|
68
|
+
public get: AxiosInstance['get'];
|
|
69
|
+
public post: AxiosInstance['post'];
|
|
70
|
+
public delete: AxiosInstance['delete'];
|
|
71
|
+
public head: AxiosInstance['head'];
|
|
72
|
+
public put: AxiosInstance['put'];
|
|
73
|
+
public patch: AxiosInstance['patch'];
|
|
74
|
+
public options: AxiosInstance['options'];
|
|
75
|
+
|
|
76
|
+
constructor(settings: NetworkSettings = {}) {
|
|
94
77
|
this.settings = merge(defaultSettings, settings);
|
|
95
78
|
if (this.settings.keepAlive) {
|
|
96
79
|
this.settings.httpAgent = new HttpAgent({ freeSocketTimeout: FREE_SOCKET_TIMEOUT });
|
|
@@ -114,8 +97,7 @@ module.exports = class Network {
|
|
|
114
97
|
this.#addLogs();
|
|
115
98
|
}
|
|
116
99
|
|
|
117
|
-
|
|
118
|
-
#addRetry(settings) {
|
|
100
|
+
#addRetry(settings: NetworkSettings): void {
|
|
119
101
|
axiosRetry(this.axios, {
|
|
120
102
|
retries: 0,
|
|
121
103
|
retryDelay: axiosRetry.exponentialDelay,
|
|
@@ -123,7 +105,7 @@ module.exports = class Network {
|
|
|
123
105
|
});
|
|
124
106
|
}
|
|
125
107
|
|
|
126
|
-
#addLogs() {
|
|
108
|
+
#addLogs(): void {
|
|
127
109
|
const logger = this.settings.logger ?? Logger();
|
|
128
110
|
this.axios.interceptors.request.use((request) => {
|
|
129
111
|
logger.info(`Start Request: ${createRequestString(request)}`);
|
|
@@ -134,7 +116,6 @@ module.exports = class Network {
|
|
|
134
116
|
logger.info(`Finish Request: ${createRequestString(response.config)}`);
|
|
135
117
|
return response;
|
|
136
118
|
}, (error) => {
|
|
137
|
-
debugger;
|
|
138
119
|
if(error.request?._currentRequest?.reusedSocket && ['ECONNRESET', 'EPIPE'].includes(error.code)) {
|
|
139
120
|
// See https://www.npmjs.com/package/agentkeepalive
|
|
140
121
|
// Support req.reusedSocket
|
|
@@ -154,7 +135,7 @@ module.exports = class Network {
|
|
|
154
135
|
});
|
|
155
136
|
}
|
|
156
137
|
|
|
157
|
-
#createBaseUrl() {
|
|
138
|
+
#createBaseUrl(): void {
|
|
158
139
|
if (!this.settings.serviceUrl && !this.settings.serviceName) {
|
|
159
140
|
throw new Error('At least one of the settings Missing serviceUrl or serviceName');
|
|
160
141
|
}
|
|
@@ -174,29 +155,25 @@ module.exports = class Network {
|
|
|
174
155
|
/**
|
|
175
156
|
* Build class methods that wrap axios methods
|
|
176
157
|
*/
|
|
177
|
-
#buildClassHttpMethods() {
|
|
158
|
+
#buildClassHttpMethods(): void {
|
|
178
159
|
HTTPMethods.forEach((method) => {
|
|
179
|
-
this[method] = (...args) => this.axios[method](...args);
|
|
160
|
+
this[method] = async <T = any, R = AxiosResponse<T>>(...args: Parameters<AxiosInstance[typeof method]>): Promise<R> => this.axios[method](...args as [url: string, leaveUsAlone?: unknown]);
|
|
180
161
|
});
|
|
181
162
|
}
|
|
182
163
|
|
|
183
164
|
/**
|
|
184
|
-
* @
|
|
185
|
-
* @param
|
|
186
|
-
* @
|
|
187
|
-
* @returns {Promise<T[]>} - A promise that resolves to an array of all results.
|
|
165
|
+
* @param url The endpoint URL to send the request to.
|
|
166
|
+
* @param options Additional options to include in the request payload.
|
|
167
|
+
* @returns A promise that resolves to an array of all results.
|
|
188
168
|
*/
|
|
189
|
-
async getAllPages(url, options = {}) {
|
|
190
|
-
|
|
191
|
-
let
|
|
192
|
-
|
|
193
|
-
let resultsInFirstPage = null;
|
|
194
|
-
/** @type {number | null} */
|
|
195
|
-
let lastResultsSize = null;
|
|
169
|
+
public async getAllPages<T>(url: string, options: object = {}): Promise<T[]> {
|
|
170
|
+
let currentResult: T[] = [];
|
|
171
|
+
let resultsInFirstPage: number | null = null;
|
|
172
|
+
let lastResultsSize: number | null = null;
|
|
196
173
|
|
|
197
174
|
const localOptions = { params: { page: 1 }, ...options };
|
|
198
175
|
while ((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {
|
|
199
|
-
const { data } = await this.get(url, localOptions);
|
|
176
|
+
const { data } = await this.get<T[]>(url, localOptions);
|
|
200
177
|
lastResultsSize = data.length;
|
|
201
178
|
if (localOptions.params.page === 1) {
|
|
202
179
|
resultsInFirstPage = data.length;
|
|
@@ -213,21 +190,18 @@ module.exports = class Network {
|
|
|
213
190
|
* Fetches all pages from a paginated API endpoint until all results are retrieved
|
|
214
191
|
* or an optional page limit is reached.
|
|
215
192
|
*
|
|
216
|
-
* @
|
|
217
|
-
* @param
|
|
218
|
-
* @param
|
|
219
|
-
* @
|
|
220
|
-
* Set to -1 for no limit.
|
|
221
|
-
* @returns {Promise<T[]>} - A promise that resolves to an array of all results.
|
|
193
|
+
* @param url The endpoint URL to send the request to.
|
|
194
|
+
* @param options Additional options to include in the request payload.
|
|
195
|
+
* @param pageLimit The maximum number of pages to fetch. Set to -1 for no limit.
|
|
196
|
+
* @returns A promise that resolves to an array of all results.
|
|
222
197
|
*/
|
|
223
|
-
async getAllPagesFromQueryEndpoint(url, options = {}, pageLimit = 100) {
|
|
224
|
-
|
|
225
|
-
let currentResult = [];
|
|
198
|
+
public async getAllPagesFromQueryEndpoint<T>(url: string, options: object = {}, pageLimit: number = 100): Promise<T[]> {
|
|
199
|
+
let currentResult: T[] = [];
|
|
226
200
|
|
|
227
201
|
let moreResultsToLoad = true;
|
|
228
202
|
let page = 1;
|
|
229
203
|
while (moreResultsToLoad && (pageLimit === -1 || page < pageLimit)) {
|
|
230
|
-
const { data } = await this.post(url, {
|
|
204
|
+
const { data } = await this.post<{ rows: T[]; count: number; }>(url, {
|
|
231
205
|
...options,
|
|
232
206
|
page,
|
|
233
207
|
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "./lib",
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"experimentalDecorators": false,
|
|
11
|
+
"emitDecoratorMetadata": false,
|
|
12
|
+
"allowJs": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"removeComments": true,
|
|
15
|
+
},
|
|
16
|
+
"include": ["src", "tsup.config.ts"],
|
|
17
|
+
"exclude": ["node_modules"]
|
|
18
|
+
}
|
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig((options) => ({
|
|
4
|
+
entry: ['src/index.ts'],
|
|
5
|
+
target: 'node18.0',
|
|
6
|
+
dts: true,
|
|
7
|
+
outDir: 'lib',
|
|
8
|
+
format: ['esm', 'cjs'],
|
|
9
|
+
clean: !options.watch,
|
|
10
|
+
minify: !options.watch,
|
|
11
|
+
removeNodeProtocol: false,
|
|
12
|
+
treeshake: true,
|
|
13
|
+
sourcemap: true,
|
|
14
|
+
}));
|