@autofleet/network 1.6.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} +62 -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,12 +24,18 @@ const HTTPMethods = [
|
|
|
30
24
|
'put',
|
|
31
25
|
'patch',
|
|
32
26
|
'options',
|
|
33
|
-
];
|
|
27
|
+
] as const;
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
}
|
|
37
37
|
|
|
38
|
-
const defaultSettings = {
|
|
38
|
+
const defaultSettings: NetworkSettings = {
|
|
39
39
|
timeout: 10_000,
|
|
40
40
|
headers: {
|
|
41
41
|
'X-AF-AUTH': 'ANYONE',
|
|
@@ -48,17 +48,7 @@ const defaultSettings = {
|
|
|
48
48
|
keepAlive: true,
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
const createRequestString = (request) => `[${(request.method || '').toUpperCase()}] ${request.baseURL ?? 'unknown-base-url'}${request.url ?? '/unknown-url'}`;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @typedef {object} NetworkSettingsBase
|
|
56
|
-
* @property {string} [serviceName]
|
|
57
|
-
* @property {string} [serviceUrl]
|
|
58
|
-
* @property {boolean} [keepAlive]
|
|
59
|
-
* @property {import('@autofleet/logger').LoggerInstanceManager} [logger]
|
|
60
|
-
* @typedef {import('axios').CreateAxiosDefaults & NetworkSettingsBase} NetworkSettings
|
|
61
|
-
*/
|
|
51
|
+
const createRequestString = (request: AxiosRequestConfig): string => `[${(request.method || '').toUpperCase()}] ${request.baseURL ?? 'unknown-base-url'}${request.url ?? '/unknown-url'}`;
|
|
62
52
|
|
|
63
53
|
/*
|
|
64
54
|
The default free socket keepalive timeout, in milliseconds.
|
|
@@ -69,31 +59,21 @@ const createRequestString = (request) => `[${(request.method || '').toUpperCase(
|
|
|
69
59
|
There is also autoscaling reason for that, if we don't timeout at some point,
|
|
70
60
|
new pods that were auto scaled will not be used.
|
|
71
61
|
*/
|
|
72
|
-
const FREE_SOCKET_TIMEOUT = process.env.FREE_SOCKET_TIMEOUT || 5_000;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
head;
|
|
88
|
-
/** @type {import('axios').AxiosInstance['put']} */
|
|
89
|
-
put;
|
|
90
|
-
/** @type {import('axios').AxiosInstance['patch']} */
|
|
91
|
-
patch;
|
|
92
|
-
/** @type {import('axios').AxiosInstance['options']} */
|
|
93
|
-
options;
|
|
94
|
-
|
|
95
|
-
/** @param {NetworkSettings} [settings] */
|
|
96
|
-
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 = {}) {
|
|
97
77
|
this.settings = merge(defaultSettings, settings);
|
|
98
78
|
if (this.settings.keepAlive) {
|
|
99
79
|
this.settings.httpAgent = new HttpAgent({ freeSocketTimeout: FREE_SOCKET_TIMEOUT });
|
|
@@ -117,8 +97,7 @@ module.exports = class Network {
|
|
|
117
97
|
this.#addLogs();
|
|
118
98
|
}
|
|
119
99
|
|
|
120
|
-
|
|
121
|
-
#addRetry(settings) {
|
|
100
|
+
#addRetry(settings: NetworkSettings): void {
|
|
122
101
|
axiosRetry(this.axios, {
|
|
123
102
|
retries: 0,
|
|
124
103
|
retryDelay: axiosRetry.exponentialDelay,
|
|
@@ -126,7 +105,7 @@ module.exports = class Network {
|
|
|
126
105
|
});
|
|
127
106
|
}
|
|
128
107
|
|
|
129
|
-
#addLogs() {
|
|
108
|
+
#addLogs(): void {
|
|
130
109
|
const logger = this.settings.logger ?? Logger();
|
|
131
110
|
this.axios.interceptors.request.use((request) => {
|
|
132
111
|
logger.info(`Start Request: ${createRequestString(request)}`);
|
|
@@ -156,7 +135,7 @@ module.exports = class Network {
|
|
|
156
135
|
});
|
|
157
136
|
}
|
|
158
137
|
|
|
159
|
-
#createBaseUrl() {
|
|
138
|
+
#createBaseUrl(): void {
|
|
160
139
|
if (!this.settings.serviceUrl && !this.settings.serviceName) {
|
|
161
140
|
throw new Error('At least one of the settings Missing serviceUrl or serviceName');
|
|
162
141
|
}
|
|
@@ -176,29 +155,25 @@ module.exports = class Network {
|
|
|
176
155
|
/**
|
|
177
156
|
* Build class methods that wrap axios methods
|
|
178
157
|
*/
|
|
179
|
-
#buildClassHttpMethods() {
|
|
158
|
+
#buildClassHttpMethods(): void {
|
|
180
159
|
HTTPMethods.forEach((method) => {
|
|
181
|
-
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]);
|
|
182
161
|
});
|
|
183
162
|
}
|
|
184
163
|
|
|
185
164
|
/**
|
|
186
|
-
* @
|
|
187
|
-
* @param
|
|
188
|
-
* @
|
|
189
|
-
* @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.
|
|
190
168
|
*/
|
|
191
|
-
async getAllPages(url, options = {}) {
|
|
192
|
-
|
|
193
|
-
let
|
|
194
|
-
|
|
195
|
-
let resultsInFirstPage = null;
|
|
196
|
-
/** @type {number | null} */
|
|
197
|
-
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;
|
|
198
173
|
|
|
199
174
|
const localOptions = { params: { page: 1 }, ...options };
|
|
200
175
|
while ((localOptions.params.page === 1 || lastResultsSize === resultsInFirstPage) && lastResultsSize !== 0) {
|
|
201
|
-
const { data } = await this.get(url, localOptions);
|
|
176
|
+
const { data } = await this.get<T[]>(url, localOptions);
|
|
202
177
|
lastResultsSize = data.length;
|
|
203
178
|
if (localOptions.params.page === 1) {
|
|
204
179
|
resultsInFirstPage = data.length;
|
|
@@ -215,21 +190,18 @@ module.exports = class Network {
|
|
|
215
190
|
* Fetches all pages from a paginated API endpoint until all results are retrieved
|
|
216
191
|
* or an optional page limit is reached.
|
|
217
192
|
*
|
|
218
|
-
* @
|
|
219
|
-
* @param
|
|
220
|
-
* @param
|
|
221
|
-
* @
|
|
222
|
-
* Set to -1 for no limit.
|
|
223
|
-
* @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.
|
|
224
197
|
*/
|
|
225
|
-
async getAllPagesFromQueryEndpoint(url, options = {}, pageLimit = 100) {
|
|
226
|
-
|
|
227
|
-
let currentResult = [];
|
|
198
|
+
public async getAllPagesFromQueryEndpoint<T>(url: string, options: object = {}, pageLimit: number = 100): Promise<T[]> {
|
|
199
|
+
let currentResult: T[] = [];
|
|
228
200
|
|
|
229
201
|
let moreResultsToLoad = true;
|
|
230
202
|
let page = 1;
|
|
231
203
|
while (moreResultsToLoad && (pageLimit === -1 || page < pageLimit)) {
|
|
232
|
-
const { data } = await this.post(url, {
|
|
204
|
+
const { data } = await this.post<{ rows: T[]; count: number; }>(url, {
|
|
233
205
|
...options,
|
|
234
206
|
page,
|
|
235
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
|
+
}));
|