@mcp-abap-adt/connection 0.1.6 → 0.1.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/dist/connection/AbapConnection.d.ts +2 -0
- package/dist/connection/AbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.d.ts +10 -2
- package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.js +40 -28
- package/package.json +1 -1
|
@@ -13,6 +13,8 @@ export interface AbapConnection {
|
|
|
13
13
|
getConfig(): SapConfig;
|
|
14
14
|
getBaseUrl(): Promise<string>;
|
|
15
15
|
getAuthHeaders(): Promise<Record<string, string>>;
|
|
16
|
+
getSessionId(): string | null;
|
|
17
|
+
setSessionType(type: "stateful" | "stateless"): void;
|
|
16
18
|
makeAdtRequest(options: AbapRequestOptions): Promise<AxiosResponse>;
|
|
17
19
|
connect(): Promise<void>;
|
|
18
20
|
reset(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbapConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,IAAI,SAAS,CAAC;IACvB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,eAAe,IAAI,YAAY,GAAG,IAAI,CAAC;IACvC,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC5C"}
|
|
1
|
+
{"version":3,"file":"AbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbapConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,IAAI,SAAS,CAAC;IACvB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,YAAY,IAAI,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI,CAAC;IACrD,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,KAAK,IAAI,IAAI,CAAC;IACd,eAAe,IAAI,YAAY,GAAG,IAAI,CAAC;IACvC,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;CAC5C"}
|
|
@@ -9,20 +9,29 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
|
|
|
9
9
|
private csrfToken;
|
|
10
10
|
private cookies;
|
|
11
11
|
private cookieStore;
|
|
12
|
-
private
|
|
12
|
+
private baseUrl;
|
|
13
13
|
private sessionId;
|
|
14
14
|
private sessionStorage;
|
|
15
15
|
private sessionMode;
|
|
16
16
|
protected constructor(config: SapConfig, logger: ILogger, sessionStorage?: ISessionStorage, sessionId?: string);
|
|
17
|
+
/**
|
|
18
|
+
* Set session type (stateful or stateless)
|
|
19
|
+
* Controls whether x-sap-adt-sessiontype: stateful header is added to requests
|
|
20
|
+
* - stateful: SAP maintains session state between requests (locks, transactions)
|
|
21
|
+
* - stateless: Each request is independent
|
|
22
|
+
*/
|
|
23
|
+
setSessionType(type: "stateful" | "stateless"): void;
|
|
17
24
|
/**
|
|
18
25
|
* Enable stateful session mode (tells SAP to maintain stateful session)
|
|
19
26
|
* This controls whether x-sap-adt-sessiontype: stateful header is used
|
|
20
27
|
* Storage is controlled separately via setSessionStorage()
|
|
28
|
+
* @deprecated Use setSessionType("stateful") instead
|
|
21
29
|
*/
|
|
22
30
|
enableStatefulSession(): void;
|
|
23
31
|
/**
|
|
24
32
|
* Disable stateful session mode (switch to stateless)
|
|
25
33
|
* Optionally saves current state before switching
|
|
34
|
+
* @deprecated Use setSessionType("stateless") instead
|
|
26
35
|
*/
|
|
27
36
|
disableStatefulSession(saveBeforeDisable?: boolean): Promise<void>;
|
|
28
37
|
/**
|
|
@@ -108,7 +117,6 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
|
|
|
108
117
|
protected getCookies(): string | null;
|
|
109
118
|
private updateCookiesFromResponse;
|
|
110
119
|
private getAxiosInstance;
|
|
111
|
-
private normalizeRequestUrl;
|
|
112
120
|
private ensureFreshCsrfToken;
|
|
113
121
|
private shouldRetryCsrf;
|
|
114
122
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"AAAA,OAAc,EAAiD,aAAa,EAAE,MAAM,OAAO,CAAC;AAG5F,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzE,uBAAe,sBAAuB,YAAW,cAAc;IAW3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO;IAXpC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -45,7 +45,7 @@ class AbstractAbapConnection {
|
|
|
45
45
|
csrfToken = null;
|
|
46
46
|
cookies = null;
|
|
47
47
|
cookieStore = new Map();
|
|
48
|
-
|
|
48
|
+
baseUrl;
|
|
49
49
|
sessionId = null;
|
|
50
50
|
sessionStorage = null;
|
|
51
51
|
sessionMode = "stateless";
|
|
@@ -57,23 +57,42 @@ class AbstractAbapConnection {
|
|
|
57
57
|
this.sessionId = sessionId || (0, crypto_1.randomUUID)();
|
|
58
58
|
// Session mode depends only on storage availability (sessionId exists for both modes)
|
|
59
59
|
this.sessionMode = sessionStorage ? "stateful" : "stateless";
|
|
60
|
+
// Initialize baseUrl from config (required, will throw if invalid)
|
|
61
|
+
try {
|
|
62
|
+
const urlObj = new URL(config.url);
|
|
63
|
+
this.baseUrl = urlObj.origin;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
throw new Error(`Invalid URL in configuration: ${error instanceof Error ? error.message : error}`);
|
|
67
|
+
}
|
|
60
68
|
this.logger.debug(`AbstractAbapConnection - Session ID: ${this.sessionId.substring(0, 8)}..., mode: ${this.sessionMode}`);
|
|
61
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Set session type (stateful or stateless)
|
|
72
|
+
* Controls whether x-sap-adt-sessiontype: stateful header is added to requests
|
|
73
|
+
* - stateful: SAP maintains session state between requests (locks, transactions)
|
|
74
|
+
* - stateless: Each request is independent
|
|
75
|
+
*/
|
|
76
|
+
setSessionType(type) {
|
|
77
|
+
this.sessionMode = type;
|
|
78
|
+
this.logger.debug(`Session type set to: ${type}`, {
|
|
79
|
+
sessionId: this.sessionId?.substring(0, 8),
|
|
80
|
+
hasStorage: !!this.sessionStorage
|
|
81
|
+
});
|
|
82
|
+
}
|
|
62
83
|
/**
|
|
63
84
|
* Enable stateful session mode (tells SAP to maintain stateful session)
|
|
64
85
|
* This controls whether x-sap-adt-sessiontype: stateful header is used
|
|
65
86
|
* Storage is controlled separately via setSessionStorage()
|
|
87
|
+
* @deprecated Use setSessionType("stateful") instead
|
|
66
88
|
*/
|
|
67
89
|
enableStatefulSession() {
|
|
68
|
-
this.
|
|
69
|
-
this.logger.debug("Stateful session mode enabled", {
|
|
70
|
-
sessionId: this.sessionId?.substring(0, 8),
|
|
71
|
-
hasStorage: !!this.sessionStorage
|
|
72
|
-
});
|
|
90
|
+
this.setSessionType("stateful");
|
|
73
91
|
}
|
|
74
92
|
/**
|
|
75
93
|
* Disable stateful session mode (switch to stateless)
|
|
76
94
|
* Optionally saves current state before switching
|
|
95
|
+
* @deprecated Use setSessionType("stateless") instead
|
|
77
96
|
*/
|
|
78
97
|
async disableStatefulSession(saveBeforeDisable = false) {
|
|
79
98
|
if (this.sessionMode === "stateless") {
|
|
@@ -251,22 +270,10 @@ class AbstractAbapConnection {
|
|
|
251
270
|
this.csrfToken = null;
|
|
252
271
|
this.cookies = null;
|
|
253
272
|
this.cookieStore.clear();
|
|
254
|
-
|
|
273
|
+
// Note: baseUrl is not reset as it's derived from immutable config
|
|
255
274
|
}
|
|
256
275
|
async getBaseUrl() {
|
|
257
|
-
|
|
258
|
-
return this.cachedBaseUrl;
|
|
259
|
-
}
|
|
260
|
-
const { url } = this.config;
|
|
261
|
-
try {
|
|
262
|
-
const urlObj = new URL(url);
|
|
263
|
-
this.cachedBaseUrl = urlObj.origin;
|
|
264
|
-
return this.cachedBaseUrl;
|
|
265
|
-
}
|
|
266
|
-
catch (error) {
|
|
267
|
-
const errorMessage = `Invalid URL in configuration: ${error instanceof Error ? error.message : error}`;
|
|
268
|
-
throw new Error(errorMessage);
|
|
269
|
-
}
|
|
276
|
+
return this.baseUrl;
|
|
270
277
|
}
|
|
271
278
|
async getAuthHeaders() {
|
|
272
279
|
const headers = {};
|
|
@@ -280,9 +287,10 @@ class AbstractAbapConnection {
|
|
|
280
287
|
return headers;
|
|
281
288
|
}
|
|
282
289
|
async makeAdtRequest(options) {
|
|
283
|
-
const { url, method, timeout, data, params, headers: customHeaders } = options;
|
|
290
|
+
const { url: endpoint, method, timeout, data, params, headers: customHeaders } = options;
|
|
284
291
|
const normalizedMethod = method.toUpperCase();
|
|
285
|
-
|
|
292
|
+
// Build full URL: baseUrl + endpoint
|
|
293
|
+
const requestUrl = `${this.baseUrl}${endpoint}`;
|
|
286
294
|
// Try to ensure CSRF token is available for POST/PUT/DELETE, but don't fail if it can't be fetched
|
|
287
295
|
// The retry logic will handle CSRF token errors automatically
|
|
288
296
|
if (normalizedMethod === "POST" || normalizedMethod === "PUT" || normalizedMethod === "DELETE") {
|
|
@@ -306,6 +314,16 @@ class AbstractAbapConnection {
|
|
|
306
314
|
if (customHeaders) {
|
|
307
315
|
Object.assign(requestHeaders, customHeaders);
|
|
308
316
|
}
|
|
317
|
+
// ALWAYS add sap-adt-connection-id header (connectionId is sent for ALL session types)
|
|
318
|
+
if (this.sessionId) {
|
|
319
|
+
requestHeaders["sap-adt-connection-id"] = this.sessionId;
|
|
320
|
+
}
|
|
321
|
+
// Add stateful session headers if stateful mode enabled via enableStatefulSession()
|
|
322
|
+
if (this.sessionMode === "stateful") {
|
|
323
|
+
requestHeaders["x-sap-adt-sessiontype"] = "stateful";
|
|
324
|
+
requestHeaders["sap-adt-request-id"] = (0, crypto_1.randomUUID)().replace(/-/g, '');
|
|
325
|
+
requestHeaders["X-sap-adt-profiling"] = "server-time";
|
|
326
|
+
}
|
|
309
327
|
// Add auth headers (these MUST NOT be overridden)
|
|
310
328
|
Object.assign(requestHeaders, await this.getAuthHeaders());
|
|
311
329
|
if ((normalizedMethod === "POST" || normalizedMethod === "PUT" || normalizedMethod === "DELETE") && this.csrfToken) {
|
|
@@ -667,12 +685,6 @@ class AbstractAbapConnection {
|
|
|
667
685
|
}
|
|
668
686
|
return this.axiosInstance;
|
|
669
687
|
}
|
|
670
|
-
normalizeRequestUrl(url) {
|
|
671
|
-
if (!url.includes("/sap/bc/adt/") && !url.endsWith("/sap/bc/adt")) {
|
|
672
|
-
return url.endsWith("/") ? `${url}sap/bc/adt` : `${url}/sap/bc/adt`;
|
|
673
|
-
}
|
|
674
|
-
return url;
|
|
675
|
-
}
|
|
676
688
|
async ensureFreshCsrfToken(requestUrl) {
|
|
677
689
|
// If we already have a CSRF token, reuse it to keep the same SAP session
|
|
678
690
|
// SAP ties the lock handle to the HTTP session (SAP_SESSIONID cookie)
|