@mcp-abap-adt/connection 1.9.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/connection/AbstractAbapConnection.d.ts +36 -0
- package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.js +59 -1
- package/dist/utils/timeouts.d.ts +10 -0
- package/dist/utils/timeouts.d.ts.map +1 -1
- package/dist/utils/timeouts.js +13 -0
- package/package.json +2 -2
|
@@ -13,6 +13,18 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
|
|
|
13
13
|
private sessionId;
|
|
14
14
|
private sessionMode;
|
|
15
15
|
private skipSessionType;
|
|
16
|
+
/**
|
|
17
|
+
* When true, requests are treated as part of an uninterruptible critical
|
|
18
|
+
* section (e.g. a lock → modify → unlock chain). In this state a short
|
|
19
|
+
* per-request timeout must NOT abort the request mid-flight, because
|
|
20
|
+
* aborting the socket drops the stateful ADT session and orphans the lock
|
|
21
|
+
* handle (leaving the object locked and inactive). While set, makeAdtRequest
|
|
22
|
+
* raises the effective timeout to CRITICAL_SECTION_TIMEOUT (a large ceiling)
|
|
23
|
+
* so the request runs to completion instead of being interrupted.
|
|
24
|
+
*/
|
|
25
|
+
private inCriticalSection;
|
|
26
|
+
/** Reference count for nested beginCriticalSection()/endCriticalSection() pairs. */
|
|
27
|
+
private criticalSectionDepth;
|
|
16
28
|
protected constructor(config: SapConfig, logger: ILogger | null, sessionId?: string, options?: {
|
|
17
29
|
skipSessionType?: boolean;
|
|
18
30
|
});
|
|
@@ -33,6 +45,30 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
|
|
|
33
45
|
* Get current session mode
|
|
34
46
|
*/
|
|
35
47
|
getSessionMode(): 'stateless' | 'stateful';
|
|
48
|
+
/**
|
|
49
|
+
* Enter an uninterruptible critical section.
|
|
50
|
+
*
|
|
51
|
+
* Call this BEFORE acquiring a lock (and pair it with endCriticalSection()
|
|
52
|
+
* in a finally, AFTER unlocking). While in a critical section, a short
|
|
53
|
+
* per-request timeout is not applied — makeAdtRequest uses a large ceiling
|
|
54
|
+
* (CRITICAL_SECTION_TIMEOUT, env SAP_TIMEOUT_CRITICAL) instead — so a slow
|
|
55
|
+
* PUT/activate/unlock is not aborted mid-flight. Aborting mid-flight tears
|
|
56
|
+
* down the socket, which drops the stateful ADT session and orphans the
|
|
57
|
+
* lock handle, leaving the object locked and inactive.
|
|
58
|
+
*
|
|
59
|
+
* Nesting is reference-counted so nested begin/end pairs are safe.
|
|
60
|
+
*/
|
|
61
|
+
beginCriticalSection(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Leave the uninterruptible critical section. See beginCriticalSection().
|
|
64
|
+
* Safe to call more times than begin (clamped at 0). Normal per-request
|
|
65
|
+
* timeouts resume once the outermost section ends.
|
|
66
|
+
*/
|
|
67
|
+
endCriticalSection(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Whether requests are currently in an uninterruptible critical section.
|
|
70
|
+
*/
|
|
71
|
+
isInCriticalSection(): boolean;
|
|
36
72
|
/**
|
|
37
73
|
* Set session ID
|
|
38
74
|
* @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":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,0BAA0B,CAAC;AAM7E,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;
|
|
1
|
+
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -51,6 +51,18 @@ class AbstractAbapConnection {
|
|
|
51
51
|
sessionId = null;
|
|
52
52
|
sessionMode = 'stateless';
|
|
53
53
|
skipSessionType;
|
|
54
|
+
/**
|
|
55
|
+
* When true, requests are treated as part of an uninterruptible critical
|
|
56
|
+
* section (e.g. a lock → modify → unlock chain). In this state a short
|
|
57
|
+
* per-request timeout must NOT abort the request mid-flight, because
|
|
58
|
+
* aborting the socket drops the stateful ADT session and orphans the lock
|
|
59
|
+
* handle (leaving the object locked and inactive). While set, makeAdtRequest
|
|
60
|
+
* raises the effective timeout to CRITICAL_SECTION_TIMEOUT (a large ceiling)
|
|
61
|
+
* so the request runs to completion instead of being interrupted.
|
|
62
|
+
*/
|
|
63
|
+
inCriticalSection = false;
|
|
64
|
+
/** Reference count for nested beginCriticalSection()/endCriticalSection() pairs. */
|
|
65
|
+
criticalSectionDepth = 0;
|
|
54
66
|
constructor(config, logger, sessionId, options) {
|
|
55
67
|
this.config = config;
|
|
56
68
|
this.logger = logger;
|
|
@@ -94,6 +106,44 @@ class AbstractAbapConnection {
|
|
|
94
106
|
getSessionMode() {
|
|
95
107
|
return this.sessionMode;
|
|
96
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Enter an uninterruptible critical section.
|
|
111
|
+
*
|
|
112
|
+
* Call this BEFORE acquiring a lock (and pair it with endCriticalSection()
|
|
113
|
+
* in a finally, AFTER unlocking). While in a critical section, a short
|
|
114
|
+
* per-request timeout is not applied — makeAdtRequest uses a large ceiling
|
|
115
|
+
* (CRITICAL_SECTION_TIMEOUT, env SAP_TIMEOUT_CRITICAL) instead — so a slow
|
|
116
|
+
* PUT/activate/unlock is not aborted mid-flight. Aborting mid-flight tears
|
|
117
|
+
* down the socket, which drops the stateful ADT session and orphans the
|
|
118
|
+
* lock handle, leaving the object locked and inactive.
|
|
119
|
+
*
|
|
120
|
+
* Nesting is reference-counted so nested begin/end pairs are safe.
|
|
121
|
+
*/
|
|
122
|
+
beginCriticalSection() {
|
|
123
|
+
this.criticalSectionDepth++;
|
|
124
|
+
this.inCriticalSection = true;
|
|
125
|
+
this.logger?.debug(`Entered critical section (depth ${this.criticalSectionDepth})`);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Leave the uninterruptible critical section. See beginCriticalSection().
|
|
129
|
+
* Safe to call more times than begin (clamped at 0). Normal per-request
|
|
130
|
+
* timeouts resume once the outermost section ends.
|
|
131
|
+
*/
|
|
132
|
+
endCriticalSection() {
|
|
133
|
+
if (this.criticalSectionDepth > 0) {
|
|
134
|
+
this.criticalSectionDepth--;
|
|
135
|
+
}
|
|
136
|
+
if (this.criticalSectionDepth === 0) {
|
|
137
|
+
this.inCriticalSection = false;
|
|
138
|
+
}
|
|
139
|
+
this.logger?.debug(`Left critical section (depth ${this.criticalSectionDepth})`);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Whether requests are currently in an uninterruptible critical section.
|
|
143
|
+
*/
|
|
144
|
+
isInCriticalSection() {
|
|
145
|
+
return this.inCriticalSection;
|
|
146
|
+
}
|
|
97
147
|
/**
|
|
98
148
|
* Set session ID
|
|
99
149
|
* @deprecated Session ID is auto-generated, use setSessionType() to control session mode
|
|
@@ -207,11 +257,19 @@ class AbstractAbapConnection {
|
|
|
207
257
|
}
|
|
208
258
|
}
|
|
209
259
|
}
|
|
260
|
+
// Inside an uninterruptible critical section (lock → modify → unlock), a
|
|
261
|
+
// short per-request timeout must not abort the request mid-flight — that
|
|
262
|
+
// would drop the stateful session and orphan the lock. Raise the effective
|
|
263
|
+
// timeout to the large critical-section ceiling for the whole request
|
|
264
|
+
// (also honoured on the retry paths below, which reuse requestConfig).
|
|
265
|
+
const effectiveTimeout = this.inCriticalSection
|
|
266
|
+
? Math.max(timeout ?? 0, (0, timeouts_js_1.getCriticalSectionTimeout)())
|
|
267
|
+
: timeout;
|
|
210
268
|
const requestConfig = {
|
|
211
269
|
method: normalizedMethod,
|
|
212
270
|
url: requestUrl,
|
|
213
271
|
headers: requestHeaders,
|
|
214
|
-
timeout,
|
|
272
|
+
timeout: effectiveTimeout,
|
|
215
273
|
params,
|
|
216
274
|
};
|
|
217
275
|
if (data !== undefined) {
|
package/dist/utils/timeouts.d.ts
CHANGED
|
@@ -2,4 +2,14 @@ import type { ITimeoutConfig } from '@mcp-abap-adt/interfaces';
|
|
|
2
2
|
export type TimeoutConfig = ITimeoutConfig;
|
|
3
3
|
export declare function getTimeoutConfig(): ITimeoutConfig;
|
|
4
4
|
export declare function getTimeout(type?: 'default' | 'csrf' | 'long' | number): number;
|
|
5
|
+
/**
|
|
6
|
+
* Large ceiling timeout applied while a connection is inside an uninterruptible
|
|
7
|
+
* critical section (lock → modify → unlock). A short per-request timeout must
|
|
8
|
+
* not abort such a request mid-flight: aborting tears down the socket, which
|
|
9
|
+
* drops the stateful ADT session and orphans the lock handle (object left
|
|
10
|
+
* locked and inactive). Configurable via SAP_TIMEOUT_CRITICAL (ms).
|
|
11
|
+
* Default 600000 (10 minutes) — a generous ceiling that still guards against a
|
|
12
|
+
* permanently dead socket hanging forever.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getCriticalSectionTimeout(): number;
|
|
5
15
|
//# 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,CAajD;AAED,wBAAgB,UAAU,CACxB,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GACrD,MAAM,CAOR"}
|
|
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;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD"}
|
package/dist/utils/timeouts.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getTimeoutConfig = getTimeoutConfig;
|
|
4
4
|
exports.getTimeout = getTimeout;
|
|
5
|
+
exports.getCriticalSectionTimeout = getCriticalSectionTimeout;
|
|
5
6
|
function getTimeoutConfig() {
|
|
6
7
|
const defaultTimeout = parseInt(process.env.SAP_TIMEOUT_DEFAULT || '45000', 10);
|
|
7
8
|
const csrfTimeout = parseInt(process.env.SAP_TIMEOUT_CSRF || '15000', 10);
|
|
@@ -19,3 +20,15 @@ function getTimeout(type = 'default') {
|
|
|
19
20
|
const config = getTimeoutConfig();
|
|
20
21
|
return config[type];
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Large ceiling timeout applied while a connection is inside an uninterruptible
|
|
25
|
+
* critical section (lock → modify → unlock). A short per-request timeout must
|
|
26
|
+
* not abort such a request mid-flight: aborting tears down the socket, which
|
|
27
|
+
* drops the stateful ADT session and orphans the lock handle (object left
|
|
28
|
+
* locked and inactive). Configurable via SAP_TIMEOUT_CRITICAL (ms).
|
|
29
|
+
* Default 600000 (10 minutes) — a generous ceiling that still guards against a
|
|
30
|
+
* permanently dead socket hanging forever.
|
|
31
|
+
*/
|
|
32
|
+
function getCriticalSectionTimeout() {
|
|
33
|
+
return parseInt(process.env.SAP_TIMEOUT_CRITICAL || '600000', 10);
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/connection",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "ABAP connection layer for MCP ABAP ADT server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@mcp-abap-adt/interfaces": "^7.2.0",
|
|
50
|
-
"axios": "^1.
|
|
50
|
+
"axios": "^1.16.0",
|
|
51
51
|
"commander": "^14.0.3",
|
|
52
52
|
"express": "^5.1.0",
|
|
53
53
|
"open": "^11.0.0"
|