@autofleet/network 1.6.1-beta-194901dc.0 → 1.7.1

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 CHANGED
@@ -1,10 +1,29 @@
1
1
  {
2
2
  "name": "@autofleet/network",
3
- "version": "1.6.1-beta-194901dc.0",
3
+ "version": "1.7.1",
4
4
  "description": "",
5
- "main": "index.js",
5
+ "type": "module",
6
+ "main": "./lib/index.cjs",
7
+ "module": "./lib/index.js",
8
+ "types": "./lib/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./lib/index.d.ts",
13
+ "default": "./lib/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./lib/index.d.cts",
17
+ "default": "./lib/index.cjs"
18
+ }
19
+ }
20
+ },
6
21
  "scripts": {
7
- "test": "node --test"
22
+ "test": "tsx index.test.js",
23
+ "build": "tsup"
24
+ },
25
+ "engines": {
26
+ "node": ">=18"
8
27
  },
9
28
  "author": "Dor Shay",
10
29
  "license": "ISC",
@@ -13,17 +32,20 @@
13
32
  "agentkeepalive": "^4.6.0",
14
33
  "axios": "^0.29.0",
15
34
  "axios-retry": "^4.5.0",
16
- "deepmerge": "^3.0.0",
35
+ "deepmerge": "^4.3.1",
17
36
  "qs": "^6.14.0"
18
37
  },
19
38
  "peerDependencies": {
20
- "@autofleet/logger": ">=4.0.0"
39
+ "@autofleet/logger": ">=4.2.0"
21
40
  },
22
41
  "devDependencies": {
23
- "@autofleet/logger": "^4.1.0",
24
- "@types/axios": "^0.9.36",
42
+ "@autofleet/logger": "^4.2.0",
25
43
  "@types/node": "^18.19.75",
44
+ "@types/qs": "^6.9.18",
26
45
  "dotenv": "^16.4.7",
27
- "nock": "^14.0.1"
46
+ "nock": "^14.0.1",
47
+ "tsup": "^8.3.6",
48
+ "tsx": "^4.19.2",
49
+ "typescript": "^5.7.3"
28
50
  }
29
51
  }
@@ -1,25 +1,19 @@
1
- const { default: axios } = require('axios');
2
- const { default: axiosRetry } = require('axios-retry');
3
- const { default: Logger } = require('@autofleet/logger');
4
- const merge = require('deepmerge');
5
- const { setup } = require('@autofleet/axios-cache-adapter');
6
- const { HttpAgent, HttpsAgent } = require('agentkeepalive');
7
-
8
- /**
9
- * Add support for nock testing
10
- * see https://github.com/axios/axios/issues/305
11
- */
12
- if (process.env.NODE_ENV === 'test') {
13
- require('dotenv').config();
14
- const httpAdapter = require('axios/lib/adapters/http');
15
- axios.defaults.adapter = httpAdapter;
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, { arrayFormat: 'brackets' }),
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
- /** @param {import('axios').AxiosRequestConfig} request @returns {string} */
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
- module.exports = class Network {
72
- /** @private @readonly @type {NetworkSettings} */
73
- settings;
74
- /** @private @readonly @type {import('axios').AxiosInstance} */
75
- axios;
76
-
77
- /** @type {import('axios').AxiosInstance['get']} */
78
- get;
79
- /** @type {import('axios').AxiosInstance['post']} */
80
- post;
81
- /** @type {import('axios').AxiosInstance['delete']} */
82
- delete;
83
- /** @type {import('axios').AxiosInstance['head']} */
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
- /** @param {NetworkSettings} settings */
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
- * @template T
185
- * @param {string} url - The endpoint URL to send the request to.
186
- * @param {object} [options={}] - Additional options to include in the request payload.
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
- /** @type {T[]} */
191
- let currentResult = [];
192
- /** @type {number | null} */
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
- * @template T
217
- * @param {string} url - The endpoint URL to send the request to.
218
- * @param {object} [options={}] - Additional options to include in the request payload.
219
- * @param {number} [pageLimit=100] - The maximum number of pages to fetch.
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
- /** @type {T[]} */
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
+ }));