@nest-omni/core 3.1.2-6 → 3.1.2-8
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/common/dto/dto-extensions.js +6 -5
- package/common/utils.d.ts +1 -0
- package/common/utils.js +6 -0
- package/http-client/config/http-client.config.d.ts +6 -0
- package/http-client/config/http-client.config.js +87 -0
- package/http-client/config/index.d.ts +1 -0
- package/http-client/config/index.js +17 -0
- package/http-client/decorators/http-client.decorators.d.ts +72 -0
- package/http-client/decorators/http-client.decorators.js +204 -0
- package/http-client/decorators/index.d.ts +1 -0
- package/http-client/decorators/index.js +17 -0
- package/http-client/entities/http-log.entity.d.ts +98 -0
- package/http-client/entities/http-log.entity.js +143 -0
- package/http-client/entities/index.d.ts +1 -0
- package/http-client/entities/index.js +17 -0
- package/http-client/errors/http-client.errors.d.ts +56 -0
- package/http-client/errors/http-client.errors.js +149 -0
- package/http-client/errors/index.d.ts +1 -0
- package/http-client/errors/index.js +17 -0
- package/http-client/examples/advanced-usage.example.d.ts +23 -0
- package/http-client/examples/advanced-usage.example.js +319 -0
- package/http-client/examples/basic-usage.example.d.ts +61 -0
- package/http-client/examples/basic-usage.example.js +171 -0
- package/http-client/examples/index.d.ts +3 -0
- package/http-client/examples/index.js +19 -0
- package/http-client/examples/multi-api-configuration.example.d.ts +98 -0
- package/http-client/examples/multi-api-configuration.example.js +353 -0
- package/http-client/http-client.module.d.ts +11 -0
- package/http-client/http-client.module.js +254 -0
- package/http-client/index.d.ts +10 -0
- package/http-client/index.js +27 -0
- package/http-client/interfaces/api-client-config.interface.d.ts +152 -0
- package/http-client/interfaces/api-client-config.interface.js +12 -0
- package/http-client/interfaces/http-client-config.interface.d.ts +123 -0
- package/http-client/interfaces/http-client-config.interface.js +2 -0
- package/http-client/services/api-client-registry.service.d.ts +40 -0
- package/http-client/services/api-client-registry.service.js +411 -0
- package/http-client/services/cache.service.d.ts +24 -0
- package/http-client/services/cache.service.js +264 -0
- package/http-client/services/circuit-breaker.service.d.ts +33 -0
- package/http-client/services/circuit-breaker.service.js +180 -0
- package/http-client/services/http-client.service.d.ts +43 -0
- package/http-client/services/http-client.service.js +384 -0
- package/http-client/services/http-log-query.service.d.ts +56 -0
- package/http-client/services/http-log-query.service.js +414 -0
- package/http-client/services/index.d.ts +7 -0
- package/http-client/services/index.js +23 -0
- package/http-client/services/log-cleanup.service.d.ts +64 -0
- package/http-client/services/log-cleanup.service.js +268 -0
- package/http-client/services/logging.service.d.ts +36 -0
- package/http-client/services/logging.service.js +445 -0
- package/http-client/utils/call-stack-extractor.util.d.ts +29 -0
- package/http-client/utils/call-stack-extractor.util.js +138 -0
- package/http-client/utils/context-extractor.util.d.ts +44 -0
- package/http-client/utils/context-extractor.util.js +173 -0
- package/http-client/utils/curl-generator.util.d.ts +9 -0
- package/http-client/utils/curl-generator.util.js +169 -0
- package/http-client/utils/index.d.ts +4 -0
- package/http-client/utils/index.js +20 -0
- package/http-client/utils/retry-recorder.util.d.ts +30 -0
- package/http-client/utils/retry-recorder.util.js +143 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContextExtractor = void 0;
|
|
4
|
+
const providers_1 = require("../../providers");
|
|
5
|
+
class ContextExtractor {
|
|
6
|
+
static getHttpContext() {
|
|
7
|
+
try {
|
|
8
|
+
const requestId = providers_1.ContextProvider.getRequestId();
|
|
9
|
+
const authUser = providers_1.ContextProvider.getAuthUser();
|
|
10
|
+
const router = providers_1.ContextProvider.getRouter();
|
|
11
|
+
return {
|
|
12
|
+
requestId,
|
|
13
|
+
userId: authUser === null || authUser === void 0 ? void 0 : authUser.uid,
|
|
14
|
+
clientIp: router === null || router === void 0 ? void 0 : router.ip,
|
|
15
|
+
appId: authUser === null || authUser === void 0 ? void 0 : authUser.appId,
|
|
16
|
+
metadata: {
|
|
17
|
+
authUser,
|
|
18
|
+
router,
|
|
19
|
+
},
|
|
20
|
+
tags: [],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
catch (_a) {
|
|
24
|
+
return {
|
|
25
|
+
tags: [],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
static getRequestId() {
|
|
30
|
+
try {
|
|
31
|
+
return providers_1.ContextProvider.getRequestId();
|
|
32
|
+
}
|
|
33
|
+
catch (_a) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
static getUserId() {
|
|
38
|
+
try {
|
|
39
|
+
const authUser = providers_1.ContextProvider.getAuthUser();
|
|
40
|
+
return authUser === null || authUser === void 0 ? void 0 : authUser.uid;
|
|
41
|
+
}
|
|
42
|
+
catch (_a) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
static getClientIp() {
|
|
47
|
+
try {
|
|
48
|
+
const router = providers_1.ContextProvider.getRouter();
|
|
49
|
+
return router === null || router === void 0 ? void 0 : router.ip;
|
|
50
|
+
}
|
|
51
|
+
catch (_a) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
static getUserAgent() {
|
|
56
|
+
try {
|
|
57
|
+
const router = providers_1.ContextProvider.getRouter();
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
catch (_a) {
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
static getServiceName() {
|
|
65
|
+
try {
|
|
66
|
+
const authUser = providers_1.ContextProvider.getAuthUser();
|
|
67
|
+
return authUser === null || authUser === void 0 ? void 0 : authUser.appId;
|
|
68
|
+
}
|
|
69
|
+
catch (_a) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
static getMetadata() {
|
|
74
|
+
try {
|
|
75
|
+
const authUser = providers_1.ContextProvider.getAuthUser();
|
|
76
|
+
const router = providers_1.ContextProvider.getRouter();
|
|
77
|
+
return {
|
|
78
|
+
authUser,
|
|
79
|
+
router,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
catch (_a) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
static getTags() {
|
|
87
|
+
try {
|
|
88
|
+
const context = this.getHttpContext();
|
|
89
|
+
return (context === null || context === void 0 ? void 0 : context.tags) || [];
|
|
90
|
+
}
|
|
91
|
+
catch (_a) {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
static addTag(tag) {
|
|
96
|
+
try {
|
|
97
|
+
const context = this.getHttpContext();
|
|
98
|
+
if (context && context.tags) {
|
|
99
|
+
if (!context.tags.includes(tag)) {
|
|
100
|
+
context.tags.push(tag);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch (_a) {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
static getContextSummary() {
|
|
108
|
+
const context = this.getHttpContext();
|
|
109
|
+
return {
|
|
110
|
+
requestId: context.requestId,
|
|
111
|
+
userId: context.userId,
|
|
112
|
+
service: context.appId,
|
|
113
|
+
clientIp: context.clientIp,
|
|
114
|
+
hasUser: !!context.userId,
|
|
115
|
+
requestSource: this.getRequestSource().source,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
static getRequestSource() {
|
|
119
|
+
const context = this.getHttpContext();
|
|
120
|
+
if (!context.clientIp) {
|
|
121
|
+
return { source: 'unknown' };
|
|
122
|
+
}
|
|
123
|
+
const internalIPs = ['::1', '127.0.0.1', 'localhost'];
|
|
124
|
+
const isInternal = internalIPs.includes(context.clientIp) ||
|
|
125
|
+
context.clientIp.startsWith('192.168.') ||
|
|
126
|
+
context.clientIp.startsWith('10.') ||
|
|
127
|
+
(context.clientIp.startsWith('172.') &&
|
|
128
|
+
this.isInternal172IP(context.clientIp));
|
|
129
|
+
return {
|
|
130
|
+
source: isInternal ? 'internal' : 'external',
|
|
131
|
+
ip: context.clientIp,
|
|
132
|
+
userAgent: context.userAgent,
|
|
133
|
+
service: context.appId,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
static isInternalRequest() {
|
|
137
|
+
const requestSource = this.getRequestSource();
|
|
138
|
+
return requestSource.source === 'internal';
|
|
139
|
+
}
|
|
140
|
+
static getSecurityContext() {
|
|
141
|
+
try {
|
|
142
|
+
const authUser = providers_1.ContextProvider.getAuthUser();
|
|
143
|
+
return {
|
|
144
|
+
isAuthenticated: !!authUser,
|
|
145
|
+
userId: authUser === null || authUser === void 0 ? void 0 : authUser.uid,
|
|
146
|
+
tenantId: authUser === null || authUser === void 0 ? void 0 : authUser.tenantId,
|
|
147
|
+
role: authUser === null || authUser === void 0 ? void 0 : authUser.role,
|
|
148
|
+
permissions: [],
|
|
149
|
+
securityLevel: (authUser === null || authUser === void 0 ? void 0 : authUser.admin) ? 'high' : 'medium',
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
catch (_a) {
|
|
153
|
+
return {
|
|
154
|
+
isAuthenticated: false,
|
|
155
|
+
permissions: [],
|
|
156
|
+
securityLevel: 'low',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
static generateSpanId() {
|
|
161
|
+
return Math.random().toString(36).substring(2, 15);
|
|
162
|
+
}
|
|
163
|
+
static isInternal172IP(ip) {
|
|
164
|
+
if (!ip.startsWith('172.'))
|
|
165
|
+
return false;
|
|
166
|
+
const parts = ip.split('.');
|
|
167
|
+
if (parts.length !== 4)
|
|
168
|
+
return false;
|
|
169
|
+
const secondOctet = parseInt(parts[1]);
|
|
170
|
+
return secondOctet >= 16 && secondOctet <= 31;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.ContextExtractor = ContextExtractor;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
|
+
export declare class CurlGenerator {
|
|
3
|
+
static generateCurlCommand(config: AxiosRequestConfig, response?: AxiosResponse): string;
|
|
4
|
+
static generateSimplifiedCurl(config: AxiosRequestConfig): string;
|
|
5
|
+
static generateDebugCurl(config: AxiosRequestConfig, response?: AxiosResponse): string;
|
|
6
|
+
static parseCurlCommand(curlCommand: string): Partial<AxiosRequestConfig>;
|
|
7
|
+
private static buildUrl;
|
|
8
|
+
private static sanitizeHeaderValue;
|
|
9
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CurlGenerator = void 0;
|
|
4
|
+
class CurlGenerator {
|
|
5
|
+
static generateCurlCommand(config, response) {
|
|
6
|
+
const parts = ['curl'];
|
|
7
|
+
if (config.method && config.method.toLowerCase() !== 'get') {
|
|
8
|
+
parts.push(`-X ${config.method.toUpperCase()}`);
|
|
9
|
+
}
|
|
10
|
+
const url = this.buildUrl(config);
|
|
11
|
+
parts.push(`'${url}'`);
|
|
12
|
+
if (config.headers) {
|
|
13
|
+
Object.entries(config.headers).forEach(([key, value]) => {
|
|
14
|
+
if (value !== undefined && value !== null) {
|
|
15
|
+
parts.push(`-H '${key}: ${value}'`);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
if (config.data) {
|
|
20
|
+
const data = typeof config.data === 'string'
|
|
21
|
+
? config.data
|
|
22
|
+
: JSON.stringify(config.data, null, 2);
|
|
23
|
+
parts.push(`-d '${data}'`);
|
|
24
|
+
}
|
|
25
|
+
if (config.data instanceof FormData) {
|
|
26
|
+
const formDataParts = [];
|
|
27
|
+
config.data.forEach((value, key) => {
|
|
28
|
+
if (value instanceof File) {
|
|
29
|
+
formDataParts.push(`-F '${key}=@${value.name}'`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
formDataParts.push(`-F '${key}=${value}'`);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
parts.push(...formDataParts);
|
|
36
|
+
}
|
|
37
|
+
if (config.params && !url.includes('?')) {
|
|
38
|
+
const params = new URLSearchParams(config.params).toString();
|
|
39
|
+
if (params) {
|
|
40
|
+
parts[parts.length - 1] = parts[parts.length - 1].replace(/'/, `?${params}'`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
parts.push('-i');
|
|
44
|
+
parts.push('-s');
|
|
45
|
+
parts.push('-L');
|
|
46
|
+
if (response) {
|
|
47
|
+
parts.push(`# Response: ${response.status} ${response.statusText}`);
|
|
48
|
+
}
|
|
49
|
+
return parts.join(' \\\n ');
|
|
50
|
+
}
|
|
51
|
+
static generateSimplifiedCurl(config) {
|
|
52
|
+
const parts = ['curl'];
|
|
53
|
+
if (config.method && config.method.toLowerCase() !== 'get') {
|
|
54
|
+
parts.push(`-X ${config.method.toUpperCase()}`);
|
|
55
|
+
}
|
|
56
|
+
parts.push(`'${this.buildUrl(config)}'`);
|
|
57
|
+
const importantHeaders = [
|
|
58
|
+
'content-type',
|
|
59
|
+
'accept',
|
|
60
|
+
'authorization',
|
|
61
|
+
'x-api-key',
|
|
62
|
+
];
|
|
63
|
+
if (config.headers) {
|
|
64
|
+
Object.entries(config.headers)
|
|
65
|
+
.filter(([key]) => importantHeaders.includes(key.toLowerCase()))
|
|
66
|
+
.forEach(([key, value]) => {
|
|
67
|
+
if (value !== undefined && value !== null) {
|
|
68
|
+
const sanitizedValue = this.sanitizeHeaderValue(key, String(value));
|
|
69
|
+
parts.push(`-H '${key}: ${sanitizedValue}'`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (config.data) {
|
|
74
|
+
const data = typeof config.data === 'string'
|
|
75
|
+
? config.data.substring(0, 200) +
|
|
76
|
+
(config.data.length > 200 ? '...' : '')
|
|
77
|
+
: JSON.stringify(config.data).substring(0, 200) + '...';
|
|
78
|
+
parts.push(`-d '${data}'`);
|
|
79
|
+
}
|
|
80
|
+
return parts.join(' ');
|
|
81
|
+
}
|
|
82
|
+
static generateDebugCurl(config, response) {
|
|
83
|
+
var _a, _b, _c;
|
|
84
|
+
const curl = this.generateCurlCommand(config, response);
|
|
85
|
+
const debugInfo = [
|
|
86
|
+
'# Debug Information:',
|
|
87
|
+
`# Timestamp: ${new Date().toISOString()}`,
|
|
88
|
+
`# Request ID: ${((_a = config.headers) === null || _a === void 0 ? void 0 : _a['x-request-id']) || 'N/A'}`,
|
|
89
|
+
`# User-Agent: ${((_b = config.headers) === null || _b === void 0 ? void 0 : _b['user-agent']) || 'N/A'}`,
|
|
90
|
+
];
|
|
91
|
+
if (response) {
|
|
92
|
+
debugInfo.push(`# Response Time: ${((_c = response.config.metadata) === null || _c === void 0 ? void 0 : _c.responseTime) || 'N/A'}ms`);
|
|
93
|
+
debugInfo.push(`# Response Size: ${JSON.stringify(response.data).length} bytes`);
|
|
94
|
+
}
|
|
95
|
+
return debugInfo.join('\n') + '\n' + curl;
|
|
96
|
+
}
|
|
97
|
+
static parseCurlCommand(curlCommand) {
|
|
98
|
+
const config = {};
|
|
99
|
+
try {
|
|
100
|
+
const methodMatch = curlCommand.match(/-X\s+(\w+)/);
|
|
101
|
+
if (methodMatch) {
|
|
102
|
+
config.method = methodMatch[1].toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
const urlMatch = curlCommand.match(/curl[^']*'([^']+)'/);
|
|
105
|
+
if (urlMatch) {
|
|
106
|
+
config.url = urlMatch[1];
|
|
107
|
+
}
|
|
108
|
+
const headerMatches = curlCommand.match(/-H\s+'([^:]+):\s*([^']+)'/g) || [];
|
|
109
|
+
config.headers = {};
|
|
110
|
+
headerMatches.forEach((header) => {
|
|
111
|
+
const headerMatch = header.match(/-H\s+'([^:]+):\s*([^']+)'/);
|
|
112
|
+
if (headerMatch) {
|
|
113
|
+
config.headers[headerMatch[1]] = headerMatch[2];
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
const dataMatch = curlCommand.match(/-d\s+'([^']+)'/);
|
|
117
|
+
if (dataMatch) {
|
|
118
|
+
try {
|
|
119
|
+
config.data = JSON.parse(dataMatch[1]);
|
|
120
|
+
}
|
|
121
|
+
catch (_a) {
|
|
122
|
+
config.data = dataMatch[1];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
console.warn('Failed to parse curl command:', error);
|
|
128
|
+
}
|
|
129
|
+
return config;
|
|
130
|
+
}
|
|
131
|
+
static buildUrl(config) {
|
|
132
|
+
let url = config.url || '';
|
|
133
|
+
if (config.baseURL && !url.startsWith('http')) {
|
|
134
|
+
url = config.baseURL.replace(/\/$/, '') + '/' + url.replace(/^\//, '');
|
|
135
|
+
}
|
|
136
|
+
if (config.params) {
|
|
137
|
+
const searchParams = new URLSearchParams();
|
|
138
|
+
Object.entries(config.params).forEach(([key, value]) => {
|
|
139
|
+
if (value !== undefined && value !== null) {
|
|
140
|
+
searchParams.append(key, String(value));
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
const paramString = searchParams.toString();
|
|
144
|
+
if (paramString) {
|
|
145
|
+
url += (url.includes('?') ? '&' : '?') + paramString;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return url;
|
|
149
|
+
}
|
|
150
|
+
static sanitizeHeaderValue(key, value) {
|
|
151
|
+
const sensitiveKeys = [
|
|
152
|
+
'authorization',
|
|
153
|
+
'apikey',
|
|
154
|
+
'password',
|
|
155
|
+
'secret',
|
|
156
|
+
'token',
|
|
157
|
+
'x-api-key',
|
|
158
|
+
'x-auth-token',
|
|
159
|
+
'x-secret-key',
|
|
160
|
+
'cookie',
|
|
161
|
+
'set-cookie',
|
|
162
|
+
];
|
|
163
|
+
if (sensitiveKeys.some((sensitive) => key.toLowerCase().includes(sensitive))) {
|
|
164
|
+
return '[FILTERED]';
|
|
165
|
+
}
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.CurlGenerator = CurlGenerator;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./curl-generator.util"), exports);
|
|
18
|
+
__exportStar(require("./retry-recorder.util"), exports);
|
|
19
|
+
__exportStar(require("./context-extractor.util"), exports);
|
|
20
|
+
__exportStar(require("./call-stack-extractor.util"), exports);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { AxiosError, AxiosRequestConfig } from 'axios';
|
|
2
|
+
import { RetryRecord } from '../entities/http-log.entity';
|
|
3
|
+
export declare class RetryRecorder {
|
|
4
|
+
private records;
|
|
5
|
+
static recordRetry(attempt: number, error: AxiosError | any, config: AxiosRequestConfig, delay: number): RetryRecord;
|
|
6
|
+
static create(): RetryRecorder;
|
|
7
|
+
private static formatError;
|
|
8
|
+
private static getRetryReason;
|
|
9
|
+
private static sanitizeHeaders;
|
|
10
|
+
addRecord(record: RetryRecord): void;
|
|
11
|
+
getRecords(): RetryRecord[];
|
|
12
|
+
getStats(): {
|
|
13
|
+
totalRetries: number;
|
|
14
|
+
maxAttempts: number;
|
|
15
|
+
totalDelay: number;
|
|
16
|
+
averageDelay: number;
|
|
17
|
+
reasons: Record<string, number>;
|
|
18
|
+
};
|
|
19
|
+
reset(): void;
|
|
20
|
+
getLastRecord(): RetryRecord | null;
|
|
21
|
+
hasRetries(): boolean;
|
|
22
|
+
getRecordByAttempt(attempt: number): RetryRecord | null;
|
|
23
|
+
getTimeline(): Array<{
|
|
24
|
+
attempt: number;
|
|
25
|
+
timestamp: Date;
|
|
26
|
+
delay: number;
|
|
27
|
+
reason: string;
|
|
28
|
+
cumulativeDelay: number;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetryRecorder = void 0;
|
|
4
|
+
class RetryRecorder {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.records = [];
|
|
7
|
+
}
|
|
8
|
+
static recordRetry(attempt, error, config, delay) {
|
|
9
|
+
var _a;
|
|
10
|
+
return {
|
|
11
|
+
attempt,
|
|
12
|
+
timestamp: new Date(),
|
|
13
|
+
reason: this.getRetryReason(error),
|
|
14
|
+
delay,
|
|
15
|
+
error: this.formatError(error),
|
|
16
|
+
requestConfig: {
|
|
17
|
+
method: ((_a = config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN',
|
|
18
|
+
url: config.url || '',
|
|
19
|
+
headers: this.sanitizeHeaders(config.headers || {}),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
static create() {
|
|
24
|
+
return new RetryRecorder();
|
|
25
|
+
}
|
|
26
|
+
static formatError(error) {
|
|
27
|
+
var _a;
|
|
28
|
+
if (!error)
|
|
29
|
+
return undefined;
|
|
30
|
+
const formatted = {
|
|
31
|
+
message: error.message || 'Unknown error',
|
|
32
|
+
};
|
|
33
|
+
if (error.code) {
|
|
34
|
+
formatted.code = error.code;
|
|
35
|
+
}
|
|
36
|
+
if ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) {
|
|
37
|
+
formatted.statusCode = error.response.status;
|
|
38
|
+
}
|
|
39
|
+
return formatted;
|
|
40
|
+
}
|
|
41
|
+
static getRetryReason(error) {
|
|
42
|
+
if (!error.response) {
|
|
43
|
+
if (error.code === 'ECONNRESET') {
|
|
44
|
+
return 'Connection reset';
|
|
45
|
+
}
|
|
46
|
+
if (error.code === 'ETIMEDOUT') {
|
|
47
|
+
return 'Request timeout';
|
|
48
|
+
}
|
|
49
|
+
if (error.code === 'ENOTFOUND') {
|
|
50
|
+
return 'DNS resolution failed';
|
|
51
|
+
}
|
|
52
|
+
if (error.code === 'ECONNREFUSED') {
|
|
53
|
+
return 'Connection refused';
|
|
54
|
+
}
|
|
55
|
+
return 'Network error';
|
|
56
|
+
}
|
|
57
|
+
const status = error.response.status;
|
|
58
|
+
if (status >= 500) {
|
|
59
|
+
return `Server error: ${status}`;
|
|
60
|
+
}
|
|
61
|
+
if (status === 429) {
|
|
62
|
+
return 'Rate limited';
|
|
63
|
+
}
|
|
64
|
+
if (status === 408) {
|
|
65
|
+
return 'Request timeout';
|
|
66
|
+
}
|
|
67
|
+
return `HTTP error: ${status}`;
|
|
68
|
+
}
|
|
69
|
+
static sanitizeHeaders(headers) {
|
|
70
|
+
const sanitized = {};
|
|
71
|
+
const sensitiveKeys = [
|
|
72
|
+
'authorization',
|
|
73
|
+
'apikey',
|
|
74
|
+
'password',
|
|
75
|
+
'secret',
|
|
76
|
+
'token',
|
|
77
|
+
'x-api-key',
|
|
78
|
+
'x-auth-token',
|
|
79
|
+
'cookie',
|
|
80
|
+
'set-cookie',
|
|
81
|
+
];
|
|
82
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
83
|
+
if (sensitiveKeys.some((sensitive) => key.toLowerCase().includes(sensitive))) {
|
|
84
|
+
sanitized[key] = '[FILTERED]';
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
sanitized[key] = value;
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
return sanitized;
|
|
91
|
+
}
|
|
92
|
+
addRecord(record) {
|
|
93
|
+
this.records.push(record);
|
|
94
|
+
}
|
|
95
|
+
getRecords() {
|
|
96
|
+
return [...this.records];
|
|
97
|
+
}
|
|
98
|
+
getStats() {
|
|
99
|
+
const totalRetries = this.records.length;
|
|
100
|
+
const maxAttempts = Math.max(...this.records.map((r) => r.attempt), 0);
|
|
101
|
+
const totalDelay = this.records.reduce((sum, r) => sum + r.delay, 0);
|
|
102
|
+
const averageDelay = totalRetries > 0 ? totalDelay / totalRetries : 0;
|
|
103
|
+
const reasons = {};
|
|
104
|
+
this.records.forEach((record) => {
|
|
105
|
+
reasons[record.reason] = (reasons[record.reason] || 0) + 1;
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
totalRetries,
|
|
109
|
+
maxAttempts,
|
|
110
|
+
totalDelay,
|
|
111
|
+
averageDelay,
|
|
112
|
+
reasons,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
reset() {
|
|
116
|
+
this.records = [];
|
|
117
|
+
}
|
|
118
|
+
getLastRecord() {
|
|
119
|
+
return this.records.length > 0
|
|
120
|
+
? this.records[this.records.length - 1]
|
|
121
|
+
: null;
|
|
122
|
+
}
|
|
123
|
+
hasRetries() {
|
|
124
|
+
return this.records.length > 0;
|
|
125
|
+
}
|
|
126
|
+
getRecordByAttempt(attempt) {
|
|
127
|
+
return this.records.find((r) => r.attempt === attempt) || null;
|
|
128
|
+
}
|
|
129
|
+
getTimeline() {
|
|
130
|
+
let cumulativeDelay = 0;
|
|
131
|
+
return this.records.map((record) => {
|
|
132
|
+
cumulativeDelay += record.delay;
|
|
133
|
+
return {
|
|
134
|
+
attempt: record.attempt,
|
|
135
|
+
timestamp: record.timestamp,
|
|
136
|
+
delay: record.delay,
|
|
137
|
+
reason: record.reason,
|
|
138
|
+
cumulativeDelay,
|
|
139
|
+
};
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
exports.RetryRecorder = RetryRecorder;
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
|
@@ -29,6 +29,7 @@ __exportStar(require("./helpers"), exports);
|
|
|
29
29
|
__exportStar(require("./providers"), exports);
|
|
30
30
|
__exportStar(require("./redis-lock"), exports);
|
|
31
31
|
__exportStar(require("./cache"), exports);
|
|
32
|
+
__exportStar(require("./http-client"), exports);
|
|
32
33
|
__exportStar(require("./vault"), exports);
|
|
33
34
|
__exportStar(require("./setup"), exports);
|
|
34
35
|
__exportStar(require("./health-checker"), exports);
|
package/package.json
CHANGED