@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.
@@ -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;IAW3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAX3C,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;IAC5D,OAAO,CAAC,eAAe,CAAU;IAEjC,SAAS,aACU,MAAM,EAAE,SAAS,EACf,MAAM,EAAE,OAAO,GAAG,IAAI,EACzC,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE;IAqBzC;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAUpD;;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,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA+R9B,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;IA2ClB;;OAEG;YACW,0BAA0B;IAsKxC;;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,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlD,OAAO,CAAC,yBAAyB;IAkEjC;;;OAGG;IACH,SAAS,CAAC,oBAAoB,IAAI,OAAO,YAAY,EAAE,YAAY;IAInE,OAAO,CAAC,gBAAgB;YAsBV,oBAAoB;IAiClC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,eAAe;CA+BxB;AAGD,OAAO,EAAE,sBAAsB,EAAE,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) {
@@ -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"}
@@ -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.9.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.13.5",
50
+ "axios": "^1.16.0",
51
51
  "commander": "^14.0.3",
52
52
  "express": "^5.1.0",
53
53
  "open": "^11.0.0"