@mcp-abap-adt/connection 0.2.5 → 0.2.7
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/LICENSE +1 -1
- package/dist/config/sapConfig.js +2 -2
- package/dist/connection/AbapConnection.d.ts +1 -1
- package/dist/connection/AbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.d.ts +6 -6
- package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.js +111 -93
- package/dist/connection/BaseAbapConnection.d.ts +3 -3
- package/dist/connection/BaseAbapConnection.d.ts.map +1 -1
- package/dist/connection/BaseAbapConnection.js +10 -10
- package/dist/connection/JwtAbapConnection.d.ts +6 -6
- package/dist/connection/JwtAbapConnection.d.ts.map +1 -1
- package/dist/connection/JwtAbapConnection.js +32 -21
- package/dist/connection/connectionFactory.d.ts +4 -4
- package/dist/connection/connectionFactory.d.ts.map +1 -1
- package/dist/connection/connectionFactory.js +3 -3
- package/dist/connection/csrfConfig.d.ts.map +1 -1
- package/dist/connection/csrfConfig.js +3 -3
- package/dist/index.d.ts +9 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -15
- package/dist/utils/timeouts.d.ts +1 -1
- package/dist/utils/timeouts.d.ts.map +1 -1
- package/dist/utils/timeouts.js +6 -6
- package/dist/utils/tokenRefresh.d.ts.map +1 -1
- package/dist/utils/tokenRefresh.js +6 -4
- package/package.json +7 -2
package/LICENSE
CHANGED
package/dist/config/sapConfig.js
CHANGED
|
@@ -15,8 +15,8 @@ function sapConfigSignature(config) {
|
|
|
15
15
|
client: config.client ?? null,
|
|
16
16
|
authType: config.authType,
|
|
17
17
|
username: config.username ?? null,
|
|
18
|
-
password: config.password ?
|
|
18
|
+
password: config.password ? 'set' : null,
|
|
19
19
|
jwtToken: jwtTokenPreview,
|
|
20
|
-
refreshToken: refreshTokenPreview
|
|
20
|
+
refreshToken: refreshTokenPreview,
|
|
21
21
|
});
|
|
22
22
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IAbapConnection, IAbapRequestOptions } from '@mcp-abap-adt/interfaces';
|
|
2
2
|
export type AbapRequestOptions = IAbapRequestOptions;
|
|
3
3
|
export type AbapConnection = IAbapConnection;
|
|
4
4
|
//# sourceMappingURL=AbapConnection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbapConnection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbapConnection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AxiosResponse } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { AbapConnection, AbapRequestOptions } from
|
|
1
|
+
import { type AxiosResponse } from 'axios';
|
|
2
|
+
import type { SapConfig } from '../config/sapConfig.js';
|
|
3
|
+
import type { ILogger } from '../logger.js';
|
|
4
|
+
import type { AbapConnection, AbapRequestOptions } from './AbapConnection.js';
|
|
5
5
|
declare abstract class AbstractAbapConnection implements AbapConnection {
|
|
6
6
|
private readonly config;
|
|
7
7
|
protected readonly logger: ILogger | null;
|
|
@@ -19,7 +19,7 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
|
|
|
19
19
|
* - stateful: SAP maintains session state between requests (locks, transactions)
|
|
20
20
|
* - stateless: Each request is independent
|
|
21
21
|
*/
|
|
22
|
-
setSessionType(type:
|
|
22
|
+
setSessionType(type: 'stateful' | 'stateless'): void;
|
|
23
23
|
/**
|
|
24
24
|
* Enable stateful session mode (tells SAP to maintain stateful session)
|
|
25
25
|
* This controls whether x-sap-adt-sessiontype: stateful header is used
|
|
@@ -34,7 +34,7 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
|
|
|
34
34
|
/**
|
|
35
35
|
* Get current session mode
|
|
36
36
|
*/
|
|
37
|
-
getSessionMode():
|
|
37
|
+
getSessionMode(): 'stateless' | 'stateful';
|
|
38
38
|
/**
|
|
39
39
|
* Set session ID
|
|
40
40
|
* @deprecated Session ID is auto-generated, use setSessionType() to control session mode
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"AAGA,OAAc,EAIZ,KAAK,aAAa,EACnB,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9E,uBAAe,sBAAuB,YAAW,cAAc;IAU3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAV3C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAyC;IAE5D,SAAS,aACU,MAAM,EAAE,SAAS,EACf,MAAM,EAAE,OAAO,GAAG,IAAI,EACzC,SAAS,CAAC,EAAE,MAAM;IAoBpB;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAOpD;;;;OAIG;IACH,qBAAqB,IAAI,IAAI;IAI7B;;;OAGG;IACH,sBAAsB,IAAI,IAAI;IAU9B;;OAEG;IACH,cAAc,IAAI,WAAW,GAAG,UAAU;IAI1C;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKrC;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,SAAS,IAAI,SAAS;IAItB,KAAK,IAAI,IAAI;IAYP,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAevD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IA4PzE,SAAS,CAAC,QAAQ,CAAC,wBAAwB,IAAI,MAAM;IAErD;;;OAGG;cACa,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAgC,EAC5C,UAAU,GAAE,MAAgC,GAC3C,OAAO,CAAC,MAAM,CAAC;IAgLlB;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIlD;;OAEG;IACH,SAAS,CAAC,UAAU,IAAI,MAAM,GAAG,IAAI;IAIrC,OAAO,CAAC,yBAAyB;IAuDjC,OAAO,CAAC,gBAAgB;YAqBV,oBAAoB;IAiClC,OAAO,CAAC,eAAe;CA+BxB;AAGD,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -34,10 +34,10 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.AbstractAbapConnection = void 0;
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const crypto_1 = require("crypto");
|
|
37
|
+
const node_crypto_1 = require("node:crypto");
|
|
38
|
+
const node_https_1 = require("node:https");
|
|
40
39
|
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
40
|
+
const axios_1 = __importStar(require("axios"));
|
|
41
41
|
const timeouts_js_1 = require("../utils/timeouts.js");
|
|
42
42
|
const csrfConfig_js_1 = require("./csrfConfig.js");
|
|
43
43
|
class AbstractAbapConnection {
|
|
@@ -49,12 +49,12 @@ class AbstractAbapConnection {
|
|
|
49
49
|
cookieStore = new Map();
|
|
50
50
|
baseUrl;
|
|
51
51
|
sessionId = null;
|
|
52
|
-
sessionMode =
|
|
52
|
+
sessionMode = 'stateless';
|
|
53
53
|
constructor(config, logger, sessionId) {
|
|
54
54
|
this.config = config;
|
|
55
55
|
this.logger = logger;
|
|
56
56
|
// Generate sessionId (used for sap-adt-connection-id header)
|
|
57
|
-
this.sessionId = sessionId || (0,
|
|
57
|
+
this.sessionId = sessionId || (0, node_crypto_1.randomUUID)();
|
|
58
58
|
// Initialize baseUrl from config (required, will throw if invalid)
|
|
59
59
|
try {
|
|
60
60
|
const urlObj = new URL(config.url);
|
|
@@ -74,7 +74,7 @@ class AbstractAbapConnection {
|
|
|
74
74
|
setSessionType(type) {
|
|
75
75
|
this.sessionMode = type;
|
|
76
76
|
this.logger?.debug(`Session type set to: ${type}`, {
|
|
77
|
-
sessionId: this.sessionId?.substring(0, 8)
|
|
77
|
+
sessionId: this.sessionId?.substring(0, 8),
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
@@ -83,18 +83,18 @@ class AbstractAbapConnection {
|
|
|
83
83
|
* @deprecated Use setSessionType("stateful") instead
|
|
84
84
|
*/
|
|
85
85
|
enableStatefulSession() {
|
|
86
|
-
this.setSessionType(
|
|
86
|
+
this.setSessionType('stateful');
|
|
87
87
|
}
|
|
88
88
|
/**
|
|
89
89
|
* Disable stateful session mode (switch to stateless)
|
|
90
90
|
* @deprecated Use setSessionType("stateless") instead
|
|
91
91
|
*/
|
|
92
92
|
disableStatefulSession() {
|
|
93
|
-
if (this.sessionMode ===
|
|
93
|
+
if (this.sessionMode === 'stateless') {
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
|
-
this.sessionMode =
|
|
97
|
-
this.logger?.debug(
|
|
96
|
+
this.sessionMode = 'stateless';
|
|
97
|
+
this.logger?.debug('Stateful session mode disabled');
|
|
98
98
|
}
|
|
99
99
|
/**
|
|
100
100
|
* Get current session mode
|
|
@@ -136,22 +136,24 @@ class AbstractAbapConnection {
|
|
|
136
136
|
async getAuthHeaders() {
|
|
137
137
|
const headers = {};
|
|
138
138
|
if (this.config.client) {
|
|
139
|
-
headers[
|
|
139
|
+
headers['X-SAP-Client'] = this.config.client;
|
|
140
140
|
}
|
|
141
141
|
const authorization = this.buildAuthorizationHeader();
|
|
142
142
|
if (authorization) {
|
|
143
|
-
headers
|
|
143
|
+
headers.Authorization = authorization;
|
|
144
144
|
}
|
|
145
145
|
return headers;
|
|
146
146
|
}
|
|
147
147
|
async makeAdtRequest(options) {
|
|
148
|
-
const { url: endpoint, method, timeout, data, params, headers: customHeaders } = options;
|
|
148
|
+
const { url: endpoint, method, timeout, data, params, headers: customHeaders, } = options;
|
|
149
149
|
const normalizedMethod = method.toUpperCase();
|
|
150
150
|
// Build full URL: baseUrl + endpoint
|
|
151
151
|
const requestUrl = `${this.baseUrl}${endpoint}`;
|
|
152
152
|
// Try to ensure CSRF token is available for POST/PUT/DELETE, but don't fail if it can't be fetched
|
|
153
153
|
// The retry logic will handle CSRF token errors automatically
|
|
154
|
-
if (normalizedMethod ===
|
|
154
|
+
if (normalizedMethod === 'POST' ||
|
|
155
|
+
normalizedMethod === 'PUT' ||
|
|
156
|
+
normalizedMethod === 'DELETE') {
|
|
155
157
|
if (!this.csrfToken) {
|
|
156
158
|
try {
|
|
157
159
|
await this.ensureFreshCsrfToken(requestUrl);
|
|
@@ -165,8 +167,9 @@ class AbstractAbapConnection {
|
|
|
165
167
|
}
|
|
166
168
|
// Start with default Accept header
|
|
167
169
|
const requestHeaders = {};
|
|
168
|
-
if (!customHeaders || !customHeaders
|
|
169
|
-
requestHeaders
|
|
170
|
+
if (!customHeaders || !customHeaders.Accept) {
|
|
171
|
+
requestHeaders.Accept =
|
|
172
|
+
'application/xml, application/json, text/plain, */*';
|
|
170
173
|
}
|
|
171
174
|
// Add custom headers (but they won't override auth/cookies)
|
|
172
175
|
if (customHeaders) {
|
|
@@ -174,35 +177,41 @@ class AbstractAbapConnection {
|
|
|
174
177
|
}
|
|
175
178
|
// ALWAYS add sap-adt-connection-id header (connectionId is sent for ALL session types)
|
|
176
179
|
if (this.sessionId) {
|
|
177
|
-
requestHeaders[
|
|
180
|
+
requestHeaders['sap-adt-connection-id'] = this.sessionId;
|
|
178
181
|
}
|
|
179
182
|
// Add stateful session headers if stateful mode enabled via enableStatefulSession()
|
|
180
|
-
if (this.sessionMode ===
|
|
181
|
-
requestHeaders[
|
|
182
|
-
requestHeaders[
|
|
183
|
-
requestHeaders[
|
|
183
|
+
if (this.sessionMode === 'stateful') {
|
|
184
|
+
requestHeaders['x-sap-adt-sessiontype'] = 'stateful';
|
|
185
|
+
requestHeaders['sap-adt-request-id'] = (0, node_crypto_1.randomUUID)().replace(/-/g, '');
|
|
186
|
+
requestHeaders['X-sap-adt-profiling'] = 'server-time';
|
|
184
187
|
}
|
|
185
188
|
// Add auth headers (these MUST NOT be overridden)
|
|
186
189
|
Object.assign(requestHeaders, await this.getAuthHeaders());
|
|
187
|
-
if ((normalizedMethod ===
|
|
188
|
-
|
|
190
|
+
if ((normalizedMethod === 'POST' ||
|
|
191
|
+
normalizedMethod === 'PUT' ||
|
|
192
|
+
normalizedMethod === 'DELETE') &&
|
|
193
|
+
this.csrfToken) {
|
|
194
|
+
requestHeaders['x-csrf-token'] = this.csrfToken;
|
|
189
195
|
}
|
|
190
196
|
// Add cookies LAST (MUST NOT be overridden by custom headers)
|
|
191
197
|
if (this.cookies) {
|
|
192
|
-
requestHeaders
|
|
198
|
+
requestHeaders.Cookie = this.cookies;
|
|
193
199
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - Adding cookies to request (first 100 chars): ${this.cookies.substring(0, 100)}...`);
|
|
194
200
|
}
|
|
195
201
|
else {
|
|
196
202
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - NO COOKIES available for this request to ${requestUrl}`);
|
|
197
203
|
}
|
|
198
|
-
if ((normalizedMethod ===
|
|
199
|
-
if (typeof data ===
|
|
200
|
-
if (requestUrl.includes(
|
|
201
|
-
|
|
202
|
-
requestHeaders[
|
|
204
|
+
if ((normalizedMethod === 'POST' || normalizedMethod === 'PUT') && data) {
|
|
205
|
+
if (typeof data === 'string' && !requestHeaders['Content-Type']) {
|
|
206
|
+
if (requestUrl.includes('/usageReferences') &&
|
|
207
|
+
data.includes('usageReferenceRequest')) {
|
|
208
|
+
requestHeaders['Content-Type'] =
|
|
209
|
+
'application/vnd.sap.adt.repository.usagereferences.request.v1+xml';
|
|
210
|
+
requestHeaders.Accept =
|
|
211
|
+
'application/vnd.sap.adt.repository.usagereferences.result.v1+xml';
|
|
203
212
|
}
|
|
204
213
|
else {
|
|
205
|
-
requestHeaders[
|
|
214
|
+
requestHeaders['Content-Type'] = 'text/plain; charset=utf-8';
|
|
206
215
|
}
|
|
207
216
|
}
|
|
208
217
|
}
|
|
@@ -211,39 +220,39 @@ class AbstractAbapConnection {
|
|
|
211
220
|
url: requestUrl,
|
|
212
221
|
headers: requestHeaders,
|
|
213
222
|
timeout,
|
|
214
|
-
params
|
|
223
|
+
params,
|
|
215
224
|
};
|
|
216
225
|
if (data !== undefined) {
|
|
217
226
|
requestConfig.data = data;
|
|
218
227
|
}
|
|
219
228
|
this.logger?.debug(`Executing ${normalizedMethod} request to: ${requestUrl}`, {
|
|
220
|
-
type:
|
|
229
|
+
type: 'REQUEST_INFO',
|
|
221
230
|
url: requestUrl,
|
|
222
|
-
method: normalizedMethod
|
|
231
|
+
method: normalizedMethod,
|
|
223
232
|
});
|
|
224
233
|
try {
|
|
225
234
|
const response = await this.getAxiosInstance()(requestConfig);
|
|
226
235
|
this.updateCookiesFromResponse(response.headers);
|
|
227
236
|
this.logger?.debug(`Request succeeded with status ${response.status}`, {
|
|
228
|
-
type:
|
|
237
|
+
type: 'REQUEST_SUCCESS',
|
|
229
238
|
status: response.status,
|
|
230
239
|
url: requestUrl,
|
|
231
|
-
method: normalizedMethod
|
|
240
|
+
method: normalizedMethod,
|
|
232
241
|
});
|
|
233
242
|
return response;
|
|
234
243
|
}
|
|
235
244
|
catch (error) {
|
|
236
245
|
const errorDetails = {
|
|
237
|
-
type:
|
|
246
|
+
type: 'REQUEST_ERROR',
|
|
238
247
|
message: error instanceof Error ? error.message : String(error),
|
|
239
248
|
url: requestUrl,
|
|
240
249
|
method: normalizedMethod,
|
|
241
250
|
status: error instanceof axios_1.AxiosError ? error.response?.status : undefined,
|
|
242
|
-
data: undefined
|
|
251
|
+
data: undefined,
|
|
243
252
|
};
|
|
244
253
|
if (error instanceof axios_1.AxiosError && error.response) {
|
|
245
254
|
errorDetails.data =
|
|
246
|
-
typeof error.response.data ===
|
|
255
|
+
typeof error.response.data === 'string'
|
|
247
256
|
? error.response.data.slice(0, 200)
|
|
248
257
|
: JSON.stringify(error.response.data).slice(0, 200);
|
|
249
258
|
this.updateCookiesFromResponse(error.response.headers);
|
|
@@ -264,16 +273,16 @@ class AbstractAbapConnection {
|
|
|
264
273
|
}
|
|
265
274
|
// Retry logic for CSRF token errors (403 with CSRF message)
|
|
266
275
|
if (this.shouldRetryCsrf(error)) {
|
|
267
|
-
this.logger?.debug(
|
|
276
|
+
this.logger?.debug('CSRF token validation failed, fetching new token and retrying request', {
|
|
268
277
|
url: requestUrl,
|
|
269
|
-
method: normalizedMethod
|
|
278
|
+
method: normalizedMethod,
|
|
270
279
|
});
|
|
271
280
|
this.csrfToken = await this.fetchCsrfToken(requestUrl, 5, 2000);
|
|
272
281
|
if (this.csrfToken) {
|
|
273
|
-
requestHeaders[
|
|
282
|
+
requestHeaders['x-csrf-token'] = this.csrfToken;
|
|
274
283
|
}
|
|
275
284
|
if (this.cookies) {
|
|
276
|
-
requestHeaders
|
|
285
|
+
requestHeaders.Cookie = this.cookies;
|
|
277
286
|
}
|
|
278
287
|
const retryResponse = await this.getAxiosInstance()(requestConfig);
|
|
279
288
|
this.updateCookiesFromResponse(retryResponse.headers);
|
|
@@ -283,13 +292,13 @@ class AbstractAbapConnection {
|
|
|
283
292
|
// Only for basic auth - JWT auth will be handled by refresh logic below
|
|
284
293
|
if (error instanceof axios_1.AxiosError &&
|
|
285
294
|
error.response?.status === 401 &&
|
|
286
|
-
normalizedMethod ===
|
|
295
|
+
normalizedMethod === 'GET' &&
|
|
287
296
|
this.config.authType === 'basic' // Only for basic auth
|
|
288
297
|
) {
|
|
289
298
|
// If we already have cookies from error response, retry immediately
|
|
290
299
|
if (this.cookies) {
|
|
291
300
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - 401 on GET request, retrying with cookies from error response`);
|
|
292
|
-
requestHeaders
|
|
301
|
+
requestHeaders.Cookie = this.cookies;
|
|
293
302
|
const retryResponse = await this.getAxiosInstance()(requestConfig);
|
|
294
303
|
this.updateCookiesFromResponse(retryResponse.headers);
|
|
295
304
|
return retryResponse;
|
|
@@ -300,7 +309,7 @@ class AbstractAbapConnection {
|
|
|
300
309
|
// Try to get CSRF token (this will also get cookies)
|
|
301
310
|
this.csrfToken = await this.fetchCsrfToken(requestUrl, 3, 1000);
|
|
302
311
|
if (this.cookies) {
|
|
303
|
-
requestHeaders
|
|
312
|
+
requestHeaders.Cookie = this.cookies;
|
|
304
313
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - Retrying GET request with cookies from CSRF fetch`);
|
|
305
314
|
const retryResponse = await this.getAxiosInstance()(requestConfig);
|
|
306
315
|
this.updateCookiesFromResponse(retryResponse.headers);
|
|
@@ -322,13 +331,15 @@ class AbstractAbapConnection {
|
|
|
322
331
|
async fetchCsrfToken(url, retryCount = csrfConfig_js_1.CSRF_CONFIG.RETRY_COUNT, retryDelay = csrfConfig_js_1.CSRF_CONFIG.RETRY_DELAY) {
|
|
323
332
|
let csrfUrl = url;
|
|
324
333
|
// Build CSRF endpoint URL from base URL
|
|
325
|
-
if (!url.includes(
|
|
334
|
+
if (!url.includes('/sap/bc/adt/')) {
|
|
326
335
|
// If URL doesn't contain ADT path, append endpoint
|
|
327
|
-
csrfUrl = url.endsWith(
|
|
336
|
+
csrfUrl = url.endsWith('/')
|
|
337
|
+
? `${url}${csrfConfig_js_1.CSRF_CONFIG.ENDPOINT.slice(1)}`
|
|
338
|
+
: `${url}${csrfConfig_js_1.CSRF_CONFIG.ENDPOINT}`;
|
|
328
339
|
}
|
|
329
340
|
else if (!url.includes(csrfConfig_js_1.CSRF_CONFIG.ENDPOINT)) {
|
|
330
341
|
// If URL contains ADT path but not our endpoint, extract base and append endpoint
|
|
331
|
-
const base = url.split(
|
|
342
|
+
const base = url.split('/sap/bc/adt')[0];
|
|
332
343
|
csrfUrl = `${base}${csrfConfig_js_1.CSRF_CONFIG.ENDPOINT}`;
|
|
333
344
|
}
|
|
334
345
|
// If URL already contains the endpoint, use it as is
|
|
@@ -341,31 +352,31 @@ class AbstractAbapConnection {
|
|
|
341
352
|
const authHeaders = await this.getAuthHeaders();
|
|
342
353
|
const headers = {
|
|
343
354
|
...authHeaders,
|
|
344
|
-
...csrfConfig_js_1.CSRF_CONFIG.REQUIRED_HEADERS
|
|
355
|
+
...csrfConfig_js_1.CSRF_CONFIG.REQUIRED_HEADERS,
|
|
345
356
|
};
|
|
346
357
|
// Always add cookies if available - they are needed for session continuity
|
|
347
358
|
// Even on first attempt, if we have cookies from previous session or error response, use them
|
|
348
359
|
if (this.cookies) {
|
|
349
|
-
headers
|
|
360
|
+
headers.Cookie = this.cookies;
|
|
350
361
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - Adding cookies to CSRF token request (attempt ${attempt + 1}, first 100 chars): ${this.cookies.substring(0, 100)}...`);
|
|
351
362
|
}
|
|
352
363
|
else {
|
|
353
364
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - No cookies available for CSRF token request (will get fresh cookies from response)`);
|
|
354
365
|
}
|
|
355
366
|
// Log request details for debugging (only if debug logging is enabled)
|
|
356
|
-
this.logger?.debug(`[DEBUG] CSRF Token Request: url=${csrfUrl}, method=GET, hasAuth=${!!authHeaders.Authorization}, hasClient=${!!authHeaders[
|
|
367
|
+
this.logger?.debug(`[DEBUG] CSRF Token Request: url=${csrfUrl}, method=GET, hasAuth=${!!authHeaders.Authorization}, hasClient=${!!authHeaders['X-SAP-Client']}, hasCookies=${!!headers.Cookie}, attempt=${attempt + 1}`);
|
|
357
368
|
const response = await this.getAxiosInstance()({
|
|
358
|
-
method:
|
|
369
|
+
method: 'GET',
|
|
359
370
|
url: csrfUrl,
|
|
360
371
|
headers,
|
|
361
|
-
timeout: (0, timeouts_js_1.getTimeout)(
|
|
372
|
+
timeout: (0, timeouts_js_1.getTimeout)('csrf'),
|
|
362
373
|
});
|
|
363
374
|
this.updateCookiesFromResponse(response.headers);
|
|
364
|
-
const token = response.headers[
|
|
375
|
+
const token = response.headers['x-csrf-token'];
|
|
365
376
|
if (!token) {
|
|
366
|
-
this.logger?.error(
|
|
377
|
+
this.logger?.error('No CSRF token in response headers', {
|
|
367
378
|
headers: response.headers,
|
|
368
|
-
status: response.status
|
|
379
|
+
status: response.status,
|
|
369
380
|
});
|
|
370
381
|
if (attempt < retryCount) {
|
|
371
382
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
@@ -373,16 +384,16 @@ class AbstractAbapConnection {
|
|
|
373
384
|
}
|
|
374
385
|
throw new Error(csrfConfig_js_1.CSRF_ERROR_MESSAGES.NOT_IN_HEADERS);
|
|
375
386
|
}
|
|
376
|
-
if (response.headers[
|
|
387
|
+
if (response.headers['set-cookie']) {
|
|
377
388
|
this.updateCookiesFromResponse(response.headers);
|
|
378
389
|
if (this.cookies) {
|
|
379
390
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - Cookies received from CSRF response (first 100 chars): ${this.cookies.substring(0, 100)}...`);
|
|
380
|
-
this.logger?.debug(
|
|
381
|
-
cookieLength: this.cookies.length
|
|
391
|
+
this.logger?.debug('Cookies extracted from response', {
|
|
392
|
+
cookieLength: this.cookies.length,
|
|
382
393
|
});
|
|
383
394
|
}
|
|
384
395
|
}
|
|
385
|
-
this.logger?.debug(
|
|
396
|
+
this.logger?.debug('CSRF token successfully obtained');
|
|
386
397
|
return token;
|
|
387
398
|
}
|
|
388
399
|
catch (error) {
|
|
@@ -392,9 +403,9 @@ class AbstractAbapConnection {
|
|
|
392
403
|
if (error.response?.headers) {
|
|
393
404
|
this.updateCookiesFromResponse(error.response.headers);
|
|
394
405
|
if (this.cookies) {
|
|
395
|
-
this.logger?.debug(
|
|
406
|
+
this.logger?.debug('Cookies extracted from error response', {
|
|
396
407
|
status: error.response.status,
|
|
397
|
-
cookieLength: this.cookies.length
|
|
408
|
+
cookieLength: this.cookies.length,
|
|
398
409
|
});
|
|
399
410
|
}
|
|
400
411
|
}
|
|
@@ -402,41 +413,42 @@ class AbstractAbapConnection {
|
|
|
402
413
|
url: csrfUrl,
|
|
403
414
|
status: error.response?.status,
|
|
404
415
|
attempt: attempt + 1,
|
|
405
|
-
maxAttempts: retryCount + 1
|
|
416
|
+
maxAttempts: retryCount + 1,
|
|
406
417
|
});
|
|
407
|
-
if (error.response?.status === 405 &&
|
|
408
|
-
|
|
409
|
-
|
|
418
|
+
if (error.response?.status === 405 &&
|
|
419
|
+
error.response?.headers['x-csrf-token']) {
|
|
420
|
+
this.logger?.debug('CSRF: SAP returned 405 (Method Not Allowed) — not critical, token found in header');
|
|
421
|
+
const token = error.response.headers['x-csrf-token'];
|
|
410
422
|
if (token) {
|
|
411
423
|
this.updateCookiesFromResponse(error.response.headers);
|
|
412
424
|
return token;
|
|
413
425
|
}
|
|
414
426
|
}
|
|
415
|
-
if (error.response?.headers[
|
|
427
|
+
if (error.response?.headers['x-csrf-token']) {
|
|
416
428
|
this.logger?.debug(`Got CSRF token despite error (status: ${error.response?.status})`);
|
|
417
|
-
const token = error.response.headers[
|
|
429
|
+
const token = error.response.headers['x-csrf-token'];
|
|
418
430
|
this.updateCookiesFromResponse(error.response.headers);
|
|
419
431
|
return token;
|
|
420
432
|
}
|
|
421
433
|
if (error.response) {
|
|
422
|
-
this.logger?.error(
|
|
434
|
+
this.logger?.error('CSRF error details', {
|
|
423
435
|
status: error.response.status,
|
|
424
436
|
statusText: error.response.statusText,
|
|
425
437
|
headers: Object.keys(error.response.headers),
|
|
426
|
-
data: typeof error.response.data ===
|
|
438
|
+
data: typeof error.response.data === 'string'
|
|
427
439
|
? error.response.data.slice(0, 200)
|
|
428
|
-
: JSON.stringify(error.response.data).slice(0, 200)
|
|
440
|
+
: JSON.stringify(error.response.data).slice(0, 200),
|
|
429
441
|
});
|
|
430
442
|
}
|
|
431
443
|
else if (error.request) {
|
|
432
|
-
this.logger?.error(
|
|
433
|
-
request: error.request.path
|
|
444
|
+
this.logger?.error('CSRF request error - no response received', {
|
|
445
|
+
request: error.request.path,
|
|
434
446
|
});
|
|
435
447
|
}
|
|
436
448
|
}
|
|
437
449
|
else {
|
|
438
|
-
this.logger?.error(
|
|
439
|
-
error: error instanceof Error ? error.message : String(error)
|
|
450
|
+
this.logger?.error('CSRF non-axios error', {
|
|
451
|
+
error: error instanceof Error ? error.message : String(error),
|
|
440
452
|
});
|
|
441
453
|
}
|
|
442
454
|
if (attempt < retryCount) {
|
|
@@ -451,7 +463,7 @@ class AbstractAbapConnection {
|
|
|
451
463
|
throw new Error(csrfConfig_js_1.CSRF_ERROR_MESSAGES.FETCH_FAILED(retryCount + 1, error instanceof Error ? error.message : String(error)));
|
|
452
464
|
}
|
|
453
465
|
}
|
|
454
|
-
throw new Error(
|
|
466
|
+
throw new Error('CSRF token fetch failed unexpectedly');
|
|
455
467
|
}
|
|
456
468
|
/**
|
|
457
469
|
* Get CSRF token (protected for use by subclasses)
|
|
@@ -475,25 +487,25 @@ class AbstractAbapConnection {
|
|
|
475
487
|
if (!headers) {
|
|
476
488
|
return;
|
|
477
489
|
}
|
|
478
|
-
const setCookie = headers[
|
|
490
|
+
const setCookie = headers['set-cookie'];
|
|
479
491
|
if (!setCookie) {
|
|
480
492
|
return;
|
|
481
493
|
}
|
|
482
494
|
const cookiesArray = Array.isArray(setCookie) ? setCookie : [setCookie];
|
|
483
495
|
for (const entry of cookiesArray) {
|
|
484
|
-
if (typeof entry !==
|
|
496
|
+
if (typeof entry !== 'string') {
|
|
485
497
|
continue;
|
|
486
498
|
}
|
|
487
|
-
const [nameValue] = entry.split(
|
|
499
|
+
const [nameValue] = entry.split(';');
|
|
488
500
|
if (!nameValue) {
|
|
489
501
|
continue;
|
|
490
502
|
}
|
|
491
|
-
const [name, ...rest] = nameValue.split(
|
|
503
|
+
const [name, ...rest] = nameValue.split('=');
|
|
492
504
|
if (!name) {
|
|
493
505
|
continue;
|
|
494
506
|
}
|
|
495
507
|
const trimmedName = name.trim();
|
|
496
|
-
const trimmedValue = rest.join(
|
|
508
|
+
const trimmedValue = rest.join('=').trim();
|
|
497
509
|
if (!trimmedName) {
|
|
498
510
|
continue;
|
|
499
511
|
}
|
|
@@ -504,7 +516,7 @@ class AbstractAbapConnection {
|
|
|
504
516
|
}
|
|
505
517
|
const combined = Array.from(this.cookieStore.entries())
|
|
506
518
|
.map(([name, value]) => (value ? `${name}=${value}` : name))
|
|
507
|
-
.join(
|
|
519
|
+
.join('; ');
|
|
508
520
|
if (!combined) {
|
|
509
521
|
return;
|
|
510
522
|
}
|
|
@@ -513,14 +525,14 @@ class AbstractAbapConnection {
|
|
|
513
525
|
}
|
|
514
526
|
getAxiosInstance() {
|
|
515
527
|
if (!this.axiosInstance) {
|
|
516
|
-
const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED ===
|
|
517
|
-
(process.env.TLS_REJECT_UNAUTHORIZED ===
|
|
518
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED !==
|
|
528
|
+
const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '1' ||
|
|
529
|
+
(process.env.TLS_REJECT_UNAUTHORIZED === '1' &&
|
|
530
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0');
|
|
519
531
|
this.logger?.debug(`TLS configuration: rejectUnauthorized=${rejectUnauthorized}`);
|
|
520
532
|
this.axiosInstance = axios_1.default.create({
|
|
521
|
-
httpsAgent: new
|
|
522
|
-
rejectUnauthorized
|
|
523
|
-
})
|
|
533
|
+
httpsAgent: new node_https_1.Agent({
|
|
534
|
+
rejectUnauthorized,
|
|
535
|
+
}),
|
|
524
536
|
});
|
|
525
537
|
}
|
|
526
538
|
return this.axiosInstance;
|
|
@@ -539,7 +551,9 @@ class AbstractAbapConnection {
|
|
|
539
551
|
catch (error) {
|
|
540
552
|
// fetchCsrfToken handles auth errors
|
|
541
553
|
// Just re-throw the error with minimal logging to avoid duplicate error messages
|
|
542
|
-
const errorMsg = error instanceof Error
|
|
554
|
+
const errorMsg = error instanceof Error
|
|
555
|
+
? error.message
|
|
556
|
+
: csrfConfig_js_1.CSRF_ERROR_MESSAGES.REQUIRED_FOR_MUTATION;
|
|
543
557
|
// Only log at DEBUG level to avoid duplicate error messages
|
|
544
558
|
// (fetchCsrfToken already logged the error at ERROR level if auth failed)
|
|
545
559
|
this.logger?.debug(`[DEBUG] BaseAbapConnection - ensureFreshCsrfToken failed: ${errorMsg}`);
|
|
@@ -551,7 +565,9 @@ class AbstractAbapConnection {
|
|
|
551
565
|
return false;
|
|
552
566
|
}
|
|
553
567
|
const responseData = error.response?.data;
|
|
554
|
-
const responseText = typeof responseData ===
|
|
568
|
+
const responseText = typeof responseData === 'string'
|
|
569
|
+
? responseData
|
|
570
|
+
: JSON.stringify(responseData || '');
|
|
555
571
|
// Don't retry for JWT auth - refresh logic will handle it
|
|
556
572
|
if (this.config.authType === 'jwt') {
|
|
557
573
|
return false;
|
|
@@ -559,10 +575,12 @@ class AbstractAbapConnection {
|
|
|
559
575
|
// Retry on 403 with CSRF message, or if response mentions CSRF token
|
|
560
576
|
// Also retry on 401 for POST/PUT/DELETE if we don't have CSRF token yet (might need to get cookies first)
|
|
561
577
|
const method = error.config?.method?.toUpperCase();
|
|
562
|
-
const isPostPutDelete = method && [
|
|
578
|
+
const isPostPutDelete = method && ['POST', 'PUT', 'DELETE'].includes(method);
|
|
563
579
|
const needsCsrfToken = !!isPostPutDelete && !this.csrfToken;
|
|
564
|
-
return ((!!error.response &&
|
|
565
|
-
|
|
580
|
+
return ((!!error.response &&
|
|
581
|
+
error.response.status === 403 &&
|
|
582
|
+
responseText.includes('CSRF')) ||
|
|
583
|
+
responseText.includes('CSRF token') ||
|
|
566
584
|
(needsCsrfToken && error.response?.status === 401));
|
|
567
585
|
}
|
|
568
586
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { SapConfig } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import type { SapConfig } from '../config/sapConfig.js';
|
|
2
|
+
import type { ILogger } from '../logger.js';
|
|
3
|
+
import { AbstractAbapConnection } from './AbstractAbapConnection.js';
|
|
4
4
|
/**
|
|
5
5
|
* Basic Authentication connection for on-premise SAP systems
|
|
6
6
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/BaseAbapConnection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BaseAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/BaseAbapConnection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,sBAAsB;gBAChD,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;IAK1E;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAsC9B,SAAS,CAAC,wBAAwB,IAAI,MAAM;IAU5C,OAAO,CAAC,MAAM,CAAC,cAAc;CAmB9B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseAbapConnection = void 0;
|
|
4
|
-
const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
|
|
5
4
|
const axios_1 = require("axios");
|
|
5
|
+
const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
|
|
6
6
|
/**
|
|
7
7
|
* Basic Authentication connection for on-premise SAP systems
|
|
8
8
|
*/
|
|
@@ -23,10 +23,10 @@ class BaseAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnect
|
|
|
23
23
|
// Try to get CSRF token (this will also get cookies)
|
|
24
24
|
const token = await this.fetchCsrfToken(discoveryUrl, 3, 1000);
|
|
25
25
|
this.setCsrfToken(token);
|
|
26
|
-
this.logger?.debug(
|
|
26
|
+
this.logger?.debug('Successfully connected to SAP system', {
|
|
27
27
|
hasCsrfToken: !!this.getCsrfToken(),
|
|
28
28
|
hasCookies: !!this.getCookies(),
|
|
29
|
-
cookieLength: this.getCookies()?.length || 0
|
|
29
|
+
cookieLength: this.getCookies()?.length || 0,
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
catch (error) {
|
|
@@ -38,27 +38,27 @@ class BaseAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnect
|
|
|
38
38
|
if (error instanceof axios_1.AxiosError && error.response?.headers) {
|
|
39
39
|
// updateCookiesFromResponse is private, but cookies are extracted in fetchCsrfToken
|
|
40
40
|
if (this.getCookies()) {
|
|
41
|
-
this.logger?.debug(`[DEBUG] BaseAbapConnection - Cookies extracted from error response during connect (first 100 chars): ${this.getCookies()
|
|
41
|
+
this.logger?.debug(`[DEBUG] BaseAbapConnection - Cookies extracted from error response during connect (first 100 chars): ${this.getCookies()?.substring(0, 100)}...`);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
buildAuthorizationHeader() {
|
|
47
47
|
const { username, password } = this.getConfig();
|
|
48
|
-
const safeUsername = username ??
|
|
49
|
-
const safePassword = password ??
|
|
50
|
-
const token = Buffer.from(`${safeUsername}:${safePassword}`).toString(
|
|
48
|
+
const safeUsername = username ?? '';
|
|
49
|
+
const safePassword = password ?? '';
|
|
50
|
+
const token = Buffer.from(`${safeUsername}:${safePassword}`).toString('base64');
|
|
51
51
|
return `Basic ${token}`;
|
|
52
52
|
}
|
|
53
53
|
static validateConfig(config) {
|
|
54
|
-
if (config.authType !==
|
|
54
|
+
if (config.authType !== 'basic') {
|
|
55
55
|
throw new Error(`Basic authentication connection expects authType "basic", got "${config.authType}"`);
|
|
56
56
|
}
|
|
57
57
|
if (!config.username || !config.password) {
|
|
58
|
-
throw new Error(
|
|
58
|
+
throw new Error('Basic authentication requires both username and password');
|
|
59
59
|
}
|
|
60
60
|
if (!config.client) {
|
|
61
|
-
throw new Error(
|
|
61
|
+
throw new Error('Basic authentication requires SAP_CLIENT to be provided');
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { ILogger } from
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
1
|
+
import type { ITokenRefresher } from '@mcp-abap-adt/interfaces';
|
|
2
|
+
import { type AxiosResponse } from 'axios';
|
|
3
|
+
import type { SapConfig } from '../config/sapConfig.js';
|
|
4
|
+
import type { ILogger } from '../logger.js';
|
|
5
|
+
import type { AbapRequestOptions } from './AbapConnection.js';
|
|
6
|
+
import { AbstractAbapConnection } from './AbstractAbapConnection.js';
|
|
7
7
|
/**
|
|
8
8
|
* JWT Authentication connection for SAP BTP Cloud systems
|
|
9
9
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JwtAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/JwtAbapConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"JwtAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/JwtAbapConnection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,sBAAsB;IAC3D,OAAO,CAAC,cAAc,CAAC,CAAkB;IACzC,OAAO,CAAC,YAAY,CAAS;gBAG3B,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,eAAe;IAWlC,SAAS,CAAC,wBAAwB,IAAI,MAAM;IAW5C;;;OAGG;YACW,eAAe;IA0B7B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAyD9B;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAyDzE;;OAEG;cACa,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,UAAU,SAAI,EACd,UAAU,SAAO,GAChB,OAAO,CAAC,MAAM,CAAC;IA2ClB,OAAO,CAAC,MAAM,CAAC,cAAc;CAa9B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.JwtAbapConnection = void 0;
|
|
4
|
-
const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
|
|
5
4
|
const axios_1 = require("axios");
|
|
5
|
+
const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
|
|
6
6
|
/**
|
|
7
7
|
* JWT Authentication connection for SAP BTP Cloud systems
|
|
8
8
|
*
|
|
@@ -17,11 +17,16 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
17
17
|
JwtAbapConnection.validateConfig(config);
|
|
18
18
|
super(config, logger || null, sessionId);
|
|
19
19
|
this.tokenRefresher = tokenRefresher;
|
|
20
|
+
if (!config.jwtToken) {
|
|
21
|
+
throw new Error('jwtToken is required for JwtAbapConnection');
|
|
22
|
+
}
|
|
20
23
|
this.currentToken = config.jwtToken;
|
|
21
24
|
}
|
|
22
25
|
buildAuthorizationHeader() {
|
|
23
26
|
// Use currentToken which may have been refreshed
|
|
24
|
-
const tokenPreview = this.currentToken
|
|
27
|
+
const tokenPreview = this.currentToken
|
|
28
|
+
? `${this.currentToken.substring(0, 10)}...${this.currentToken.substring(Math.max(0, this.currentToken.length - 4))}`
|
|
29
|
+
: 'null';
|
|
25
30
|
this.logger?.debug(`[DEBUG] JwtAbapConnection.buildAuthorizationHeader - Using token: ${tokenPreview}`);
|
|
26
31
|
return `Bearer ${this.currentToken}`;
|
|
27
32
|
}
|
|
@@ -57,10 +62,10 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
57
62
|
// Try to get CSRF token (this will also get cookies)
|
|
58
63
|
const token = await this.fetchCsrfToken(discoveryUrl, 3, 1000);
|
|
59
64
|
this.setCsrfToken(token);
|
|
60
|
-
this.logger?.debug(
|
|
65
|
+
this.logger?.debug('Successfully connected to SAP system', {
|
|
61
66
|
hasCsrfToken: !!this.getCsrfToken(),
|
|
62
67
|
hasCookies: !!this.getCookies(),
|
|
63
|
-
cookieLength: this.getCookies()?.length || 0
|
|
68
|
+
cookieLength: this.getCookies()?.length || 0,
|
|
64
69
|
});
|
|
65
70
|
}
|
|
66
71
|
catch (error) {
|
|
@@ -69,11 +74,13 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
69
74
|
(error.response?.status === 401 || error.response?.status === 403)) {
|
|
70
75
|
// Check if this is really an auth error, not a permissions error
|
|
71
76
|
const responseData = error.response?.data;
|
|
72
|
-
const responseText = typeof responseData ===
|
|
77
|
+
const responseText = typeof responseData === 'string'
|
|
78
|
+
? responseData
|
|
79
|
+
: JSON.stringify(responseData || '');
|
|
73
80
|
// Don't retry on "No Access" errors
|
|
74
|
-
if (responseText.includes(
|
|
75
|
-
responseText.includes(
|
|
76
|
-
responseText.includes(
|
|
81
|
+
if (responseText.includes('ExceptionResourceNoAccess') ||
|
|
82
|
+
responseText.includes('No authorization') ||
|
|
83
|
+
responseText.includes('Missing authorization')) {
|
|
77
84
|
throw error;
|
|
78
85
|
}
|
|
79
86
|
// Try to refresh token if tokenRefresher is available
|
|
@@ -82,7 +89,7 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
82
89
|
this.logger?.debug(`[DEBUG] JwtAbapConnection - Retrying connect after token refresh...`);
|
|
83
90
|
return this.connect();
|
|
84
91
|
}
|
|
85
|
-
throw new Error(
|
|
92
|
+
throw new Error('JWT token has expired. Please re-authenticate.');
|
|
86
93
|
}
|
|
87
94
|
// Re-throw other errors
|
|
88
95
|
throw error;
|
|
@@ -106,11 +113,13 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
106
113
|
this.logger?.debug(`[DEBUG] JwtAbapConnection.makeAdtRequest - Got ${error.response.status}, attempting token refresh...`);
|
|
107
114
|
// Check if this is really an auth error, not a permissions error
|
|
108
115
|
const responseData = error.response?.data;
|
|
109
|
-
const responseText = typeof responseData ===
|
|
116
|
+
const responseText = typeof responseData === 'string'
|
|
117
|
+
? responseData
|
|
118
|
+
: JSON.stringify(responseData || '');
|
|
110
119
|
// Don't retry on "No Access" errors - these are permission issues, not auth issues
|
|
111
|
-
if (responseText.includes(
|
|
112
|
-
responseText.includes(
|
|
113
|
-
responseText.includes(
|
|
120
|
+
if (responseText.includes('ExceptionResourceNoAccess') ||
|
|
121
|
+
responseText.includes('No authorization') ||
|
|
122
|
+
responseText.includes('Missing authorization')) {
|
|
114
123
|
throw error;
|
|
115
124
|
}
|
|
116
125
|
// Try to refresh token if tokenRefresher is available
|
|
@@ -120,7 +129,7 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
120
129
|
this.reset();
|
|
121
130
|
return super.makeAdtRequest(options);
|
|
122
131
|
}
|
|
123
|
-
throw new Error(
|
|
132
|
+
throw new Error('JWT token has expired. Please re-authenticate.');
|
|
124
133
|
}
|
|
125
134
|
throw error;
|
|
126
135
|
}
|
|
@@ -139,11 +148,13 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
139
148
|
(error.response?.status === 401 || error.response?.status === 403)) {
|
|
140
149
|
// Check if this is really an auth error, not a permissions error
|
|
141
150
|
const responseData = error.response?.data;
|
|
142
|
-
const responseText = typeof responseData ===
|
|
151
|
+
const responseText = typeof responseData === 'string'
|
|
152
|
+
? responseData
|
|
153
|
+
: JSON.stringify(responseData || '');
|
|
143
154
|
// Don't retry on "No Access" errors
|
|
144
|
-
if (responseText.includes(
|
|
145
|
-
responseText.includes(
|
|
146
|
-
responseText.includes(
|
|
155
|
+
if (responseText.includes('ExceptionResourceNoAccess') ||
|
|
156
|
+
responseText.includes('No authorization') ||
|
|
157
|
+
responseText.includes('Missing authorization')) {
|
|
147
158
|
throw error;
|
|
148
159
|
}
|
|
149
160
|
// Try to refresh token if tokenRefresher is available
|
|
@@ -152,18 +163,18 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
|
|
|
152
163
|
this.logger?.debug(`[DEBUG] JwtAbapConnection.fetchCsrfToken - Retrying after token refresh...`);
|
|
153
164
|
return super.fetchCsrfToken(url, retryCount, retryDelay);
|
|
154
165
|
}
|
|
155
|
-
throw new Error(
|
|
166
|
+
throw new Error('JWT token has expired. Please re-authenticate.');
|
|
156
167
|
}
|
|
157
168
|
// Re-throw other errors
|
|
158
169
|
throw error;
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
static validateConfig(config) {
|
|
162
|
-
if (config.authType !==
|
|
173
|
+
if (config.authType !== 'jwt') {
|
|
163
174
|
throw new Error(`JWT connection expects authType "jwt", got "${config.authType}"`);
|
|
164
175
|
}
|
|
165
176
|
if (!config.jwtToken) {
|
|
166
|
-
throw new Error(
|
|
177
|
+
throw new Error('JWT authentication requires SAP_JWT_TOKEN to be provided');
|
|
167
178
|
}
|
|
168
179
|
}
|
|
169
180
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { ILogger } from
|
|
4
|
-
import type {
|
|
1
|
+
import type { ITokenRefresher } from '@mcp-abap-adt/interfaces';
|
|
2
|
+
import type { SapConfig } from '../config/sapConfig.js';
|
|
3
|
+
import type { ILogger } from '../logger.js';
|
|
4
|
+
import type { AbapConnection } from './AbapConnection.js';
|
|
5
5
|
export declare function createAbapConnection(config: SapConfig, logger?: ILogger | null, sessionId?: string, tokenRefresher?: ITokenRefresher): AbapConnection;
|
|
6
6
|
//# sourceMappingURL=connectionFactory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connectionFactory.d.ts","sourceRoot":"","sources":["../../src/connection/connectionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"connectionFactory.d.ts","sourceRoot":"","sources":["../../src/connection/connectionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,eAAe,GAC/B,cAAc,CAWhB"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createAbapConnection = createAbapConnection;
|
|
4
|
-
const JwtAbapConnection_js_1 = require("./JwtAbapConnection.js");
|
|
5
4
|
const BaseAbapConnection_js_1 = require("./BaseAbapConnection.js");
|
|
5
|
+
const JwtAbapConnection_js_1 = require("./JwtAbapConnection.js");
|
|
6
6
|
function createAbapConnection(config, logger, sessionId, tokenRefresher) {
|
|
7
7
|
switch (config.authType) {
|
|
8
|
-
case
|
|
8
|
+
case 'basic':
|
|
9
9
|
return new BaseAbapConnection_js_1.BaseAbapConnection(config, logger, sessionId);
|
|
10
|
-
case
|
|
10
|
+
case 'jwt':
|
|
11
11
|
return new JwtAbapConnection_js_1.JwtAbapConnection(config, logger, sessionId, tokenRefresher);
|
|
12
12
|
default:
|
|
13
13
|
throw new Error(`Unsupported SAP authentication type: ${config.authType}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csrfConfig.d.ts","sourceRoot":"","sources":["../../src/connection/csrfConfig.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW;IACtB;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;OAEG;;;;;CAKK,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB;sCACL,MAAM,SAAS,MAAM;;;
|
|
1
|
+
{"version":3,"file":"csrfConfig.d.ts","sourceRoot":"","sources":["../../src/connection/csrfConfig.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW;IACtB;;;OAGG;;IAGH;;;OAGG;;IAGH;;;OAGG;;IAGH;;OAEG;;;;;CAKK,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,mBAAmB;sCACL,MAAM,SAAS,MAAM;;;CAOtC,CAAC"}
|
|
@@ -28,8 +28,8 @@ exports.CSRF_CONFIG = {
|
|
|
28
28
|
*/
|
|
29
29
|
REQUIRED_HEADERS: {
|
|
30
30
|
'x-csrf-token': 'fetch',
|
|
31
|
-
|
|
32
|
-
}
|
|
31
|
+
Accept: 'application/atomsvc+xml',
|
|
32
|
+
},
|
|
33
33
|
};
|
|
34
34
|
/**
|
|
35
35
|
* CSRF token error messages
|
|
@@ -38,5 +38,5 @@ exports.CSRF_CONFIG = {
|
|
|
38
38
|
exports.CSRF_ERROR_MESSAGES = {
|
|
39
39
|
FETCH_FAILED: (attempts, cause) => `Failed to fetch CSRF token after ${attempts} attempts: ${cause}`,
|
|
40
40
|
NOT_IN_HEADERS: 'No CSRF token in response headers',
|
|
41
|
-
REQUIRED_FOR_MUTATION: 'CSRF token is required for POST/PUT requests but could not be fetched'
|
|
41
|
+
REQUIRED_FOR_MUTATION: 'CSRF token is required for POST/PUT requests but could not be fetched',
|
|
42
42
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
export
|
|
3
|
-
export {
|
|
4
|
-
export
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export { sapConfigSignature } from "./config/sapConfig.js";
|
|
11
|
-
export { getTimeout, getTimeoutConfig, type TimeoutConfig } from "./utils/timeouts.js";
|
|
12
|
-
export { CSRF_CONFIG, CSRF_ERROR_MESSAGES } from "./connection/csrfConfig.js";
|
|
1
|
+
export type { SapAuthType, SapConfig, } from './config/sapConfig.js';
|
|
2
|
+
export { sapConfigSignature } from './config/sapConfig.js';
|
|
3
|
+
export type { AbapConnection, AbapRequestOptions, } from './connection/AbapConnection.js';
|
|
4
|
+
export { BaseAbapConnection, BaseAbapConnection as OnPremAbapConnection, } from './connection/BaseAbapConnection.js';
|
|
5
|
+
export { createAbapConnection } from './connection/connectionFactory.js';
|
|
6
|
+
export { CSRF_CONFIG, CSRF_ERROR_MESSAGES } from './connection/csrfConfig.js';
|
|
7
|
+
export { JwtAbapConnection, JwtAbapConnection as CloudAbapConnection, } from './connection/JwtAbapConnection.js';
|
|
8
|
+
export type { ILogger } from './logger.js';
|
|
9
|
+
export { getTimeout, getTimeoutConfig, type TimeoutConfig, } from './utils/timeouts.js';
|
|
13
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,WAAW,EACX,SAAS,GACV,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,IAAI,oBAAoB,GAC3C,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,iBAAiB,EACjB,iBAAiB,IAAI,mBAAmB,GACzC,MAAM,mCAAmC,CAAC;AAC3C,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getTimeoutConfig = exports.getTimeout = exports.CloudAbapConnection = exports.JwtAbapConnection = exports.CSRF_ERROR_MESSAGES = exports.CSRF_CONFIG = exports.createAbapConnection = exports.OnPremAbapConnection = exports.BaseAbapConnection = exports.sapConfigSignature = void 0;
|
|
4
|
+
// Config utilities
|
|
5
|
+
var sapConfig_js_1 = require("./config/sapConfig.js");
|
|
6
|
+
Object.defineProperty(exports, "sapConfigSignature", { enumerable: true, get: function () { return sapConfig_js_1.sapConfigSignature; } });
|
|
4
7
|
// Connection classes - only final implementations
|
|
8
|
+
// Deprecated aliases for backward compatibility
|
|
5
9
|
var BaseAbapConnection_js_1 = require("./connection/BaseAbapConnection.js");
|
|
6
10
|
Object.defineProperty(exports, "BaseAbapConnection", { enumerable: true, get: function () { return BaseAbapConnection_js_1.BaseAbapConnection; } });
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(exports, "JwtAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_1.JwtAbapConnection; } });
|
|
9
|
-
// Deprecated aliases for backward compatibility
|
|
10
|
-
var BaseAbapConnection_js_2 = require("./connection/BaseAbapConnection.js");
|
|
11
|
-
Object.defineProperty(exports, "OnPremAbapConnection", { enumerable: true, get: function () { return BaseAbapConnection_js_2.BaseAbapConnection; } });
|
|
12
|
-
var JwtAbapConnection_js_2 = require("./connection/JwtAbapConnection.js");
|
|
13
|
-
Object.defineProperty(exports, "CloudAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_2.JwtAbapConnection; } });
|
|
11
|
+
Object.defineProperty(exports, "OnPremAbapConnection", { enumerable: true, get: function () { return BaseAbapConnection_js_1.BaseAbapConnection; } });
|
|
14
12
|
// Factory
|
|
15
13
|
var connectionFactory_js_1 = require("./connection/connectionFactory.js");
|
|
16
14
|
Object.defineProperty(exports, "createAbapConnection", { enumerable: true, get: function () { return connectionFactory_js_1.createAbapConnection; } });
|
|
17
|
-
// Config utilities
|
|
18
|
-
var sapConfig_js_1 = require("./config/sapConfig.js");
|
|
19
|
-
Object.defineProperty(exports, "sapConfigSignature", { enumerable: true, get: function () { return sapConfig_js_1.sapConfigSignature; } });
|
|
20
|
-
// Timeouts
|
|
21
|
-
var timeouts_js_1 = require("./utils/timeouts.js");
|
|
22
|
-
Object.defineProperty(exports, "getTimeout", { enumerable: true, get: function () { return timeouts_js_1.getTimeout; } });
|
|
23
|
-
Object.defineProperty(exports, "getTimeoutConfig", { enumerable: true, get: function () { return timeouts_js_1.getTimeoutConfig; } });
|
|
24
15
|
// CSRF configuration
|
|
25
16
|
var csrfConfig_js_1 = require("./connection/csrfConfig.js");
|
|
26
17
|
Object.defineProperty(exports, "CSRF_CONFIG", { enumerable: true, get: function () { return csrfConfig_js_1.CSRF_CONFIG; } });
|
|
27
18
|
Object.defineProperty(exports, "CSRF_ERROR_MESSAGES", { enumerable: true, get: function () { return csrfConfig_js_1.CSRF_ERROR_MESSAGES; } });
|
|
19
|
+
var JwtAbapConnection_js_1 = require("./connection/JwtAbapConnection.js");
|
|
20
|
+
Object.defineProperty(exports, "JwtAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_1.JwtAbapConnection; } });
|
|
21
|
+
Object.defineProperty(exports, "CloudAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_1.JwtAbapConnection; } });
|
|
22
|
+
// Timeouts
|
|
23
|
+
var timeouts_js_1 = require("./utils/timeouts.js");
|
|
24
|
+
Object.defineProperty(exports, "getTimeout", { enumerable: true, get: function () { return timeouts_js_1.getTimeout; } });
|
|
25
|
+
Object.defineProperty(exports, "getTimeoutConfig", { enumerable: true, get: function () { return timeouts_js_1.getTimeoutConfig; } });
|
package/dist/utils/timeouts.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ITimeoutConfig } from '@mcp-abap-adt/interfaces';
|
|
2
2
|
export type TimeoutConfig = ITimeoutConfig;
|
|
3
3
|
export declare function getTimeoutConfig(): ITimeoutConfig;
|
|
4
|
-
export declare function getTimeout(type?:
|
|
4
|
+
export declare function getTimeout(type?: 'default' | 'csrf' | 'long' | number): number;
|
|
5
5
|
//# sourceMappingURL=timeouts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,wBAAgB,gBAAgB,IAAI,cAAc,
|
|
1
|
+
{"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,wBAAgB,gBAAgB,IAAI,cAAc,CAajD;AAED,wBAAgB,UAAU,CACxB,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GACrD,MAAM,CAOR"}
|
package/dist/utils/timeouts.js
CHANGED
|
@@ -3,17 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getTimeoutConfig = getTimeoutConfig;
|
|
4
4
|
exports.getTimeout = getTimeout;
|
|
5
5
|
function getTimeoutConfig() {
|
|
6
|
-
const defaultTimeout = parseInt(process.env.SAP_TIMEOUT_DEFAULT ||
|
|
7
|
-
const csrfTimeout = parseInt(process.env.SAP_TIMEOUT_CSRF ||
|
|
8
|
-
const longTimeout = parseInt(process.env.SAP_TIMEOUT_LONG ||
|
|
6
|
+
const defaultTimeout = parseInt(process.env.SAP_TIMEOUT_DEFAULT || '45000', 10);
|
|
7
|
+
const csrfTimeout = parseInt(process.env.SAP_TIMEOUT_CSRF || '15000', 10);
|
|
8
|
+
const longTimeout = parseInt(process.env.SAP_TIMEOUT_LONG || '60000', 10);
|
|
9
9
|
return {
|
|
10
10
|
default: defaultTimeout,
|
|
11
11
|
csrf: csrfTimeout,
|
|
12
|
-
long: longTimeout
|
|
12
|
+
long: longTimeout,
|
|
13
13
|
};
|
|
14
14
|
}
|
|
15
|
-
function getTimeout(type =
|
|
16
|
-
if (typeof type ===
|
|
15
|
+
function getTimeout(type = 'default') {
|
|
16
|
+
if (typeof type === 'number') {
|
|
17
17
|
return type;
|
|
18
18
|
}
|
|
19
19
|
const config = getTimeoutConfig();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../../src/utils/tokenRefresh.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../../src/utils/tokenRefresh.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpE,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,mBAAmB,CAAC,CA0C9B"}
|
|
@@ -32,7 +32,7 @@ async function refreshJwtToken(refreshToken, uaaUrl, clientId, clientSecret) {
|
|
|
32
32
|
},
|
|
33
33
|
data: params.toString(),
|
|
34
34
|
});
|
|
35
|
-
if (response.data
|
|
35
|
+
if (response.data?.access_token) {
|
|
36
36
|
return {
|
|
37
37
|
accessToken: response.data.access_token,
|
|
38
38
|
refreshToken: response.data.refresh_token || refreshToken, // Use new refresh token if provided, otherwise keep old one
|
|
@@ -43,11 +43,13 @@ async function refreshJwtToken(refreshToken, uaaUrl, clientId, clientSecret) {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
catch (error) {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
const err = error;
|
|
47
|
+
if (err.response) {
|
|
48
|
+
throw new Error(`Token refresh failed (${err.response.status}): ${JSON.stringify(err.response.data)}`);
|
|
48
49
|
}
|
|
49
50
|
else {
|
|
50
|
-
|
|
51
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
52
|
+
throw new Error(`Token refresh failed: ${errorMessage}`);
|
|
51
53
|
}
|
|
52
54
|
}
|
|
53
55
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/connection",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "ABAP connection layer for MCP ABAP ADT server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -32,8 +32,12 @@
|
|
|
32
32
|
"url": "git+https://github.com/fr0ster/mcp-abap-connection.git"
|
|
33
33
|
},
|
|
34
34
|
"scripts": {
|
|
35
|
+
"chrono": "./tools/version-stats.sh",
|
|
35
36
|
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
36
|
-
"
|
|
37
|
+
"lint": "npx biome check --write src",
|
|
38
|
+
"lint:check": "npx biome check src",
|
|
39
|
+
"format": "npx biome format --write src",
|
|
40
|
+
"build": "npm run --silent clean && npx biome check src --diagnostic-level=error && npx tsc -p tsconfig.json",
|
|
37
41
|
"build:fast": "npx tsc -p tsconfig.json",
|
|
38
42
|
"test": "jest",
|
|
39
43
|
"prepublishOnly": "npm run build"
|
|
@@ -49,6 +53,7 @@
|
|
|
49
53
|
"open": "^11.0.0"
|
|
50
54
|
},
|
|
51
55
|
"devDependencies": {
|
|
56
|
+
"@biomejs/biome": "^2.3.10",
|
|
52
57
|
"@types/jest": "^30.0.0",
|
|
53
58
|
"@types/node": "^24.2.1",
|
|
54
59
|
"jest": "^30.2.0",
|