@mcp-abap-adt/connection 9.2.2 → 9.3.1

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/CHANGELOG.md CHANGED
@@ -7,6 +7,75 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [9.3.1] - 2026-09-27
11
+
12
+ ### Fixed
13
+
14
+ - **Over HTTP, a request that is not stateful no longer reaches the stateful
15
+ context.** `sap-contextid` names the context a `LOCK` opens, and SAP routes any
16
+ request that carries it into that context — session header or none. The wire
17
+ sent the whole jar on every request, so a "stateless" `PUT` after a `LOCK` ran
18
+ in the lock's context. Measured 2026-09-27 on one connection, a package locked,
19
+ written and unlocked twice:
20
+ - E19 (BASIS 816): the second `PUT` answered 400 PAK/058 "Package … is already
21
+ locked" — the first save was still in that context's `CL_PACKAGE` buffer, and
22
+ a delete on the same connection was refused the same way;
23
+ - E98 (BASIS 756): every `PUT` answered 423 "Resource … is not locked (invalid
24
+ lock handle)".
25
+
26
+ With the cookie kept to the stateful requests, both systems wrote twice and
27
+ deleted on the same connection. That is Eclipse ADT's split too: its trace
28
+ shows one stateful session for `LOCK` and `UNLOCK` alone and every other
29
+ request, the `PUT` included, outside it. The jar still holds the cookie, so
30
+ the `UNLOCK` that follows reaches the context holding the lock.
31
+
32
+ The cookie is filtered on the header as it finally goes out, not only on
33
+ the jar: the connection's CSRF and 401 retries write the whole jar into the
34
+ request's own `Cookie`, and a caller can spell that header in lower case.
35
+ Either way, a non-stateful request does not carry the context.
36
+
37
+ ### Changed
38
+
39
+ - **The session type is the wire's to express, not the connection's.**
40
+ `setSessionType('stateful')` now marks each request `stateful: true` on
41
+ `IAdtTransportRequest` instead of writing `x-sap-adt-sessiontype` into its
42
+ headers.
43
+ - `HttpTransport` adds `x-sap-adt-sessiontype: stateful` and `sap-contextid`
44
+ to stateful requests only (overridable through `sessionTypeHeaders()`;
45
+ `LegacyOnPremHttpTransport` sends no header, as before). The legacy wire
46
+ decides stateful before it drops a header the caller wrote, so a caller
47
+ that asks for the session that way — `AdtClassLegacy` — keeps its context
48
+ cookie, and its `UNLOCK` still reaches the lock.
49
+ - `RfcTransport` sends no session header, which is what Eclipse's JCo trace
50
+ shows.
51
+
52
+ A request whose caller wrote the header themselves is still treated as
53
+ stateful.
54
+
55
+ **What a consumer does:** nothing, through the connectors. A custom
56
+ `IAdtTransport` reads `request.stateful` where it used to find the header.
57
+
58
+ ## [9.3.0] - 2026-09-26
59
+
60
+ ### Changed
61
+
62
+ - **The connection contract comes from `@mcp-abap-adt/interfaces-adt-connection`
63
+ `^1.0.0`, and `@mcp-abap-adt/interfaces-adt` is no longer a dependency.** Every
64
+ name this package takes from the contract — `IAbapConnection`,
65
+ `IAbapRequestOptions`, `IAdtWireResponse`, `ISessionLifecycleAware`,
66
+ `ICriticalSection`, `IRequestProfiling`, `ITimeoutConfig`, `ADT_SESSION_ERROR`
67
+ — moved there unchanged in `interfaces-adt` 11.0.0. The object contracts that
68
+ stayed behind released three majors in one day, and each one used to reach
69
+ this package as a range to raise or a second copy of the contract in the tree;
70
+ none of them touched a name this package uses. (One import, `IAdtResponse`,
71
+ was never used and is gone.)
72
+
73
+ **What a consumer does:** import the connection types from
74
+ `@mcp-abap-adt/interfaces-adt-connection` instead of
75
+ `@mcp-abap-adt/interfaces-adt`. The names and shapes are the same, so code that
76
+ still imports them from `interfaces-adt` 10 keeps compiling against this
77
+ release; it just carries that package for nothing.
78
+
10
79
  ## [9.2.2] - 2026-09-26
11
80
 
12
81
  ### Changed
@@ -1762,7 +1831,9 @@ const connection = createAbapConnection(config, logger);
1762
1831
  - JWT token refresh now properly handles connection errors (401/403 during initial connect)
1763
1832
  - Permission errors (403 with "ExceptionResourceNoAccess") no longer trigger JWT refresh loops
1764
1833
  - Proper separation: base class handles HTTP/session, concrete classes handle auth-specific errors
1765
- [Unreleased]: https://github.com/fr0ster/mcp-abap-connection/compare/v9.2.2...HEAD
1834
+ [Unreleased]: https://github.com/fr0ster/mcp-abap-connection/compare/v9.3.1...HEAD
1835
+ [9.3.1]: https://github.com/fr0ster/mcp-abap-connection/compare/v9.3.0...v9.3.1
1836
+ [9.3.0]: https://github.com/fr0ster/mcp-abap-connection/compare/v9.2.2...v9.3.0
1766
1837
  [9.2.2]: https://github.com/fr0ster/mcp-abap-connection/compare/v9.2.1...v9.2.2
1767
1838
  [9.2.1]: https://github.com/fr0ster/mcp-abap-connection/compare/v9.2.0...v9.2.1
1768
1839
  [9.2.0]: https://github.com/fr0ster/mcp-abap-connection/compare/v9.1.0...v9.2.0
package/README.md CHANGED
@@ -136,7 +136,7 @@ dependencies of this one besides `axios`, `commander` and `open`:
136
136
 
137
137
  | Package | What this package takes from it |
138
138
  |---|---|
139
- | `@mcp-abap-adt/interfaces-adt` | `IAbapConnection`, `IAbapRequestOptions`, `IAdtResponse`, `IAdtWireResponse`, `ITimeoutConfig`, the capability atoms, `ADT_SESSION_ERROR` |
139
+ | `@mcp-abap-adt/interfaces-adt-connection` | `IAbapConnection`, `IAbapRequestOptions`, `IAdtWireResponse`, `ITimeoutConfig`, the capability atoms, `ADT_SESSION_ERROR` |
140
140
  | `@mcp-abap-adt/interfaces-auth` | `IAuthProvider`, `IRenewableCredential`, `ICertificateMaterial`, `ITokenRefresher`, `ITokenRefreshResult` |
141
141
  | `@mcp-abap-adt/interfaces-auth-sap` | `ISapConfig`, `SapAuthType`, `SapConnectionType`, `ICertificateMaterialLoader` |
142
142
  | `@mcp-abap-adt/interfaces-network` | `NETWORK_ERROR_CODES`, the WebSocket contracts |
@@ -180,7 +180,7 @@ config you build — install the package it lives in as well, because this one n
180
180
  longer brings them along:
181
181
 
182
182
  ```bash
183
- npm install @mcp-abap-adt/interfaces-adt @mcp-abap-adt/interfaces-auth @mcp-abap-adt/interfaces-auth-sap
183
+ npm install @mcp-abap-adt/interfaces-adt-connection @mcp-abap-adt/interfaces-auth @mcp-abap-adt/interfaces-auth-sap
184
184
  ```
185
185
 
186
186
  For detailed installation instructions, see [Installation Guide](./docs/INSTALLATION.md).
@@ -429,7 +429,11 @@ See [MIGRATION-4.0.md](./docs/MIGRATION-4.0.md).
429
429
 
430
430
  ### Stateful Sessions
431
431
 
432
- For operations that require session state (e.g., object modifications), you can enable stateful sessions:
432
+ Enable stateful mode for requests that need the ABAP context, such as LOCK and
433
+ UNLOCK. Switch to stateless for GET and PUT between them: a lock handle does
434
+ not require the write itself to run in the lock’s context. Authentication
435
+ cookies and the CSRF token remain available in both modes. See the
436
+ [Stateful Session Guide](./docs/STATEFUL_SESSION_GUIDE.md) for transport behavior.
433
437
 
434
438
  ```typescript
435
439
  import {
@@ -450,18 +454,18 @@ const connection = new AdtOnPremConnector(
450
454
  );
451
455
  await connection.connect();
452
456
 
453
- // Enable stateful session mode (adds x-sap-adt-sessiontype: stateful header)
457
+ // Enable the stateful HTTP context (session header and context cookie)
454
458
  connection.setSessionType("stateful");
455
459
 
456
- // Make requests - SAP will maintain session state
457
- await connection.makeAdtRequest({
458
- method: "POST",
459
- url: "/sap/bc/adt/objects/domains",
460
- data: { /* domain data */ },
461
- timeout: getTimeout("default"),
462
- });
460
+ // Send the LOCK request here, then leave its context for GET / PUT.
461
+ connection.setSessionType("stateless");
462
+
463
+ // Send GET / PUT with the lock handle here.
464
+ // Return to the same context when sending UNLOCK.
465
+ connection.setSessionType("stateful");
466
+ // Send UNLOCK here, then restore the default mode.
467
+ connection.setSessionType("stateless");
463
468
 
464
- // Note: Session state persistence is handled by @mcp-abap-adt/auth-broker package
465
469
  ```
466
470
 
467
471
  ### Custom Logger
@@ -607,7 +611,7 @@ interface AbapConnection {
607
611
  ```
608
612
 
609
613
  The connectors carry the rest of the session lifecycle. It is on the shared
610
- contract as a **capability atom** in `@mcp-abap-adt/interfaces-adt` rather than as
614
+ contract as a **capability atom** in `@mcp-abap-adt/interfaces-adt-connection` rather than as
611
615
  methods on `IAbapConnection`, so a consumer that only carries requests is
612
616
  unaffected by its existence. Note that a connection over RFC has the whole of it
613
617
  — what an RFC conversation has none of is a session RESOURCE to open and close
@@ -620,7 +624,7 @@ isConnected(): boolean;
620
624
  getSessionIdentity(): string | null; // WHICH SAP session; null is not "disconnected"
621
625
  ```
622
626
 
623
- Import those names from `@mcp-abap-adt/interfaces-adt`, not from this package: a
627
+ Import those names from `@mcp-abap-adt/interfaces-adt-connection`, not from this package: a
624
628
  contract type re-exported under a second name is a contract type that can drift.
625
629
 
626
630
  **The connection does not track locks.** Deciding when to disconnect, and
@@ -1,4 +1,4 @@
1
- import type { IAbapConnection, IAbapRequestOptions } from '@mcp-abap-adt/interfaces-adt';
1
+ import type { IAbapConnection, IAbapRequestOptions } from '@mcp-abap-adt/interfaces-adt-connection';
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":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,MAAM,8BAA8B,CAAC;AAGtC,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"AbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbapConnection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACpB,MAAM,yCAAyC,CAAC;AAGjD,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { IAdtWireResponse, ICriticalSection, IRequestProfiling, ISessionLifecycleAware } from '@mcp-abap-adt/interfaces-adt';
1
+ import type { IAdtWireResponse, ICriticalSection, IRequestProfiling, ISessionLifecycleAware } from '@mcp-abap-adt/interfaces-adt-connection';
2
2
  import type { SapConfig } from '../config/sapConfig.js';
3
3
  import type { ILogger } from '../logger.js';
4
4
  import { SessionLifecycle } from '../session/SessionLifecycle.js';
@@ -126,7 +126,9 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
126
126
  transport: IAdtTransport, logger: ILogger | null, sessionId?: string);
127
127
  /**
128
128
  * Set session type (stateful or stateless)
129
- * Controls whether x-sap-adt-sessiontype: stateful header is added to requests
129
+ * Marks the requests that follow as `stateful` for the transport, which says
130
+ * so on its own wire (over HTTP: `x-sap-adt-sessiontype: stateful` and the
131
+ * context cookie; over RFC: nothing)
130
132
  * - stateful: SAP maintains session state between requests (locks, transactions)
131
133
  * - stateless: Each request is independent
132
134
  *
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AbstractAbapConnection = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
- const interfaces_adt_1 = require("@mcp-abap-adt/interfaces-adt");
5
+ const interfaces_adt_connection_1 = require("@mcp-abap-adt/interfaces-adt-connection");
6
6
  const SessionLifecycle_js_1 = require("../session/SessionLifecycle.js");
7
7
  const networkErrors_js_1 = require("../utils/networkErrors.js");
8
8
  const timeouts_js_1 = require("../utils/timeouts.js");
@@ -170,7 +170,9 @@ class AbstractAbapConnection {
170
170
  }
171
171
  /**
172
172
  * Set session type (stateful or stateless)
173
- * Controls whether x-sap-adt-sessiontype: stateful header is added to requests
173
+ * Marks the requests that follow as `stateful` for the transport, which says
174
+ * so on its own wire (over HTTP: `x-sap-adt-sessiontype: stateful` and the
175
+ * context cookie; over RFC: nothing)
174
176
  * - stateful: SAP maintains session state between requests (locks, transactions)
175
177
  * - stateless: Each request is independent
176
178
  *
@@ -481,7 +483,7 @@ class AbstractAbapConnection {
481
483
  }
482
484
  async establishAndCommit(baselineEpoch) {
483
485
  if (this.lifecycle.teardownEpoch !== baselineEpoch) {
484
- throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_1.ADT_SESSION_ERROR.NOT_CONNECTED, 'Establishment abandoned: a teardown was requested for this connection');
486
+ throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_connection_1.ADT_SESSION_ERROR.NOT_CONNECTED, 'Establishment abandoned: a teardown was requested for this connection');
485
487
  }
486
488
  // Whatever session arrives from here is one we are deliberately
487
489
  // establishing, so it must not read as a replacement: the identity policy
@@ -556,7 +558,7 @@ class AbstractAbapConnection {
556
558
  // the CSRF token and the axios instance out from under a request that is
557
559
  // still in flight — breaking the guarantee this whole change rests on,
558
560
  // from inside the guard meant to protect it.
559
- throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_1.ADT_SESSION_ERROR.NOT_CONNECTED, 'Establishment abandoned: a teardown was requested while it was in flight');
561
+ throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_connection_1.ADT_SESSION_ERROR.NOT_CONNECTED, 'Establishment abandoned: a teardown was requested while it was in flight');
560
562
  }
561
563
  // The session IS the SAP_SESSIONID the server issued; our own session id is
562
564
  // a conversation label we generate and says nothing about what exists on
@@ -607,7 +609,7 @@ class AbstractAbapConnection {
607
609
  this.invalidateSession();
608
610
  this.lifecycle.forgetIdentity();
609
611
  this.lifecycle.markDisconnected();
610
- throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_1.ADT_SESSION_ERROR.NOT_CONNECTED, `The server authenticated the request but opened no ABAP session: the ${this.transport.kind} wire reports it is on none, so there is nothing for a lock to be bound to. The wire itself is fine — the request was carried and answered — which is why this is not a transport failure and does not look like one. ` +
612
+ throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_connection_1.ADT_SESSION_ERROR.NOT_CONNECTED, `The server authenticated the request but opened no ABAP session: the ${this.transport.kind} wire reports it is on none, so there is nothing for a lock to be bound to. The wire itself is fine — the request was carried and answered — which is why this is not a transport failure and does not look like one. ` +
611
613
  'Stateless reads would still work over it, but a lock, and any write under that lock, is dead the moment it is issued. ' +
612
614
  'The usual cause is the system declining to open another session for this user: they are limited per user, shared with every other tool logged on as them, and released either by disconnecting or by their own idle timeout. ' +
613
615
  'Whether to wait, retry, or release sessions this user still holds is yours to decide — this library does not retry on your behalf.');
@@ -670,9 +672,9 @@ class AbstractAbapConnection {
670
672
  */
671
673
  isSessionVerdict(error) {
672
674
  const code = error?.code;
673
- return (code === interfaces_adt_1.ADT_SESSION_ERROR.SESSION_REPLACED ||
674
- code === interfaces_adt_1.ADT_SESSION_ERROR.NOT_CONNECTED ||
675
- code === interfaces_adt_1.ADT_SESSION_ERROR.RELEASE_PENDING);
675
+ return (code === interfaces_adt_connection_1.ADT_SESSION_ERROR.SESSION_REPLACED ||
676
+ code === interfaces_adt_connection_1.ADT_SESSION_ERROR.NOT_CONNECTED ||
677
+ code === interfaces_adt_connection_1.ADT_SESSION_ERROR.RELEASE_PENDING);
676
678
  }
677
679
  /**
678
680
  * Folds a response into the session state AND acts on what it means, in one
@@ -723,7 +725,7 @@ class AbstractAbapConnection {
723
725
  if (classification !== 'replaced')
724
726
  return;
725
727
  this.raiseSessionLost('the session cookie changed under us');
726
- throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_1.ADT_SESSION_ERROR.SESSION_REPLACED, 'The SAP session this connection was using is gone and the requests are now on a different one; anything held against the old session — a lock and any write under it — is dead. ' +
728
+ throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_connection_1.ADT_SESSION_ERROR.SESSION_REPLACED, 'The SAP session this connection was using is gone and the requests are now on a different one; anything held against the old session — a lock and any write under it — is dead. ' +
727
729
  'The server does not swap sessions on a whim: the usual causes are the session idling out (the timeout is idle-based, so a quiet connection loses it while a busy one does not) or a request landing on a different application server. ' +
728
730
  'What to do about it is yours: re-establish and redo the work, or fail the operation. Nothing is retried here.');
729
731
  }
@@ -857,11 +859,11 @@ class AbstractAbapConnection {
857
859
  !hasHeader(requestHeaders, 'x-sap-adt-profiling')) {
858
860
  requestHeaders['X-sap-adt-profiling'] = this.profilingRequest;
859
861
  }
860
- // And this one really is the session's: it says how the server should treat
861
- // the request, which is the only one of the three that varies with the mode.
862
- if (this.sessionMode === 'stateful') {
863
- requestHeaders['x-sap-adt-sessiontype'] = 'stateful';
864
- }
862
+ // The session mode is NOT a header here. How a request asks for the
863
+ // stateful session is the wire's: HTTP says it with
864
+ // `x-sap-adt-sessiontype: stateful` and the context cookie, RFC with
865
+ // neither. It travels as `stateful` on the request below, and each
866
+ // transport expresses it its own way.
865
867
  // Add auth headers (these MUST NOT be overridden)
866
868
  Object.assign(requestHeaders, await this.getAuthHeaders());
867
869
  // Read once: the wire is asked what it holds, and the same value is what
@@ -909,6 +911,7 @@ class AbstractAbapConnection {
909
911
  // `unknown` on the caller's options, a record on the seam: the two
910
912
  // transports serialise a query differently and both need the pairs.
911
913
  params: params,
914
+ stateful: this.sessionMode === 'stateful',
912
915
  };
913
916
  if (data !== undefined) {
914
917
  requestConfig.data = data;
@@ -970,7 +973,7 @@ class AbstractAbapConnection {
970
973
  this.raiseSessionLost('the server reports the session no longer exists');
971
974
  // No internal retry: a blind retry here is what produced further locks
972
975
  // in the field. The caller decides.
973
- throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_1.ADT_SESSION_ERROR.SESSION_REPLACED, 'The SAP session no longer exists; any lock handle from it is dead');
976
+ throw (0, SessionLifecycle_js_1.sessionError)(interfaces_adt_connection_1.ADT_SESSION_ERROR.SESSION_REPLACED, 'The SAP session no longer exists; any lock handle from it is dead');
974
977
  }
975
978
  // Check if this is a network error (connection refused, timeout, DNS, etc.)
976
979
  // Don't retry for network errors - these indicate infrastructure/VPN issues
@@ -1 +1 @@
1
- {"version":3,"file":"CredentialAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/CredentialAbapConnection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,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;AACrE,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,oBAAoB,CAAC;AASnE,8BAAsB,wBAAwB,CAC5C,WAAW,SAAS,aAAa,GAAG,aAAa,CACjD,SAAQ,sBAAsB;IAG5B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,EAAE,WAAW;gBAFhC,MAAM,EAAE,SAAS;IACjB,6EAA6E;IACpE,UAAU,EAAE,WAAW,EAChC,SAAS,EAAE,aAAa,EACxB,MAAM,GAAE,OAAO,GAAG,IAAW,EAC7B,SAAS,CAAC,EAAE,MAAM;IAKpB;;;;;;;OAOG;IACH,gEAAgE;cAEvC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3D;;;;;OAKG;IACH,SAAS,CAAC,wBAAwB,IAAI,MAAM;IAI5C;;;;;;OAMG;IACY,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;cAiB7C,oBAAoB,IAAI,YAAY;cAIvC,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;CAoDlD"}
1
+ {"version":3,"file":"CredentialAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/CredentialAbapConnection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,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;AACrE,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,oBAAoB,CAAC;AASnE,8BAAsB,wBAAwB,CAC5C,WAAW,SAAS,aAAa,GAAG,aAAa,CACjD,SAAQ,sBAAsB;IAG5B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,EAAE,WAAW;gBAFhC,MAAM,EAAE,SAAS;IACjB,6EAA6E;IACpE,UAAU,EAAE,WAAW,EAChC,SAAS,EAAE,aAAa,EACxB,MAAM,GAAE,OAAO,GAAG,IAAW,EAC7B,SAAS,CAAC,EAAE,MAAM;IAKpB;;;;;;;OAOG;IACH,gEAAgE;cAEvC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3D;;;;;OAKG;IACH,SAAS,CAAC,wBAAwB,IAAI,MAAM;IAI5C;;;;;;OAMG;IACY,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;cAiB7C,oBAAoB,IAAI,YAAY;cAIvC,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;CAoDlD"}
@@ -21,6 +21,15 @@
21
21
  import { type AgentOptions } from 'node:https';
22
22
  import type { ILogger } from '../logger.js';
23
23
  import type { IAdtEstablishContext, IAdtSessionContext, IAdtTransport, IAdtTransportRequest, IAdtTransportResponse } from './IAdtTransport.js';
24
+ /**
25
+ * Whether the request runs in the stateful session: marked so by the
26
+ * connection, or — kept for callers that still write it themselves — carrying
27
+ * the session header already.
28
+ *
29
+ * Exported for the wires that rewrite a request before sending it: the
30
+ * verdict has to be taken on the request as the caller made it.
31
+ */
32
+ export declare function isStatefulRequest(request: IAdtTransportRequest): boolean;
24
33
  export declare class HttpTransport implements IAdtTransport {
25
34
  private readonly agentOptions;
26
35
  protected readonly logger: ILogger | null;
@@ -141,6 +150,13 @@ export declare class HttpTransport implements IAdtTransport {
141
150
  * looking like it carried a session.
142
151
  */
143
152
  private dress;
153
+ /**
154
+ * How this wire asks for the stateful session: `x-sap-adt-sessiontype:
155
+ * stateful`, as Eclipse sends on its `LOCK` and `UNLOCK` over HTTP. A wire
156
+ * whose system is hurt by the header overrides this (BASIS 7.40, see
157
+ * `LegacyOnPremHttpTransport`).
158
+ */
159
+ protected sessionTypeHeaders(): Record<string, string>;
144
160
  /** A path becomes an address; anything already absolute is left alone. */
145
161
  private address;
146
162
  private client;
@@ -1 +1 @@
1
- {"version":3,"file":"HttpTransport.d.ts","sourceRoot":"","sources":["../../src/connection/HttpTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAS5B,qBAAa,aAAc,YAAW,aAAa;IA6B/C,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IACzC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO;IApC1B;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAU;IAE/B,OAAO,CAAC,QAAQ,CAA8B;IAE9C;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,KAAK,CAAuB;gBAGjB,YAAY,GAAE,MAAM,YAAyB,EAC3C,MAAM,GAAE,OAAO,GAAG,IAAW;IAChD;;;;;OAKG;IACc,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAGtE;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAgC/C,0EAA0E;IAC1E,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB;;;;;;OAMG;IACH,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAQzC;;;;;OAKG;IACH,kBAAkB,IAAI,OAAO;IAI7B;;;;;;;OAOG;IACH,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IASzC;;;;;;;OAOG;IACH,SAAS,CAAC,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIlD;;;;;;;OAOG;IACG,SAAS,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmG7D,uEAAuE;IACvE,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAI1C,4EAA4E;IAC5E,aAAa,IAAI,IAAI;IASrB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,KAAK;IAcb,0EAA0E;IAC1E,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,MAAM;IAuBd;;;;;;OAMG;IACG,IAAI,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAEvD;;;;;;OAMG;IACG,KAAK,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAExD;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAIzE;;;;;;;;;;;;;OAaG;cACa,YAAY,CAC1B,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;YAInB,QAAQ;CA4BvB"}
1
+ {"version":3,"file":"HttpTransport.d.ts","sourceRoot":"","sources":["../../src/connection/HttpTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,KAAK,EACV,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAwC5B;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAaxE;AASD,qBAAa,aAAc,YAAW,aAAa;IA6B/C,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IACzC;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO;IApC1B;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAU;IAE/B,OAAO,CAAC,QAAQ,CAA8B;IAE9C;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,KAAK,CAAuB;gBAGjB,YAAY,GAAE,MAAM,YAAyB,EAC3C,MAAM,GAAE,OAAO,GAAG,IAAW;IAChD;;;;;OAKG;IACc,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAO;IAGtE;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAgC/C,0EAA0E;IAC1E,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB;;;;;;OAMG;IACH,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAQzC;;;;;OAKG;IACH,kBAAkB,IAAI,OAAO;IAI7B;;;;;;;OAOG;IACH,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IASzC;;;;;;;OAOG;IACH,SAAS,CAAC,cAAc,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIlD;;;;;;;OAOG;IACG,SAAS,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmG7D,uEAAuE;IACvE,SAAS,IAAI,MAAM,GAAG,IAAI;IAI1B,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAI1C,4EAA4E;IAC5E,aAAa,IAAI,IAAI;IASrB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAWzB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,KAAK;IA6Bb;;;;;OAKG;IACH,SAAS,CAAC,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAItD,0EAA0E;IAC1E,OAAO,CAAC,OAAO;IAQf,OAAO,CAAC,MAAM;IAuBd;;;;;;OAMG;IACG,IAAI,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAEvD;;;;;;OAMG;IACG,KAAK,CAAC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAExD;;;;;OAKG;IACG,IAAI,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAIzE;;;;;;;;;;;;;OAaG;cACa,YAAY,CAC1B,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;YAInB,QAAQ;CA4BvB"}
@@ -24,10 +24,69 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.HttpTransport = void 0;
27
+ exports.isStatefulRequest = isStatefulRequest;
27
28
  const node_https_1 = require("node:https");
28
29
  const axios_1 = __importDefault(require("axios"));
29
30
  const cookies_js_1 = require("../utils/cookies.js");
30
31
  const csrfConfig_js_1 = require("./csrfConfig.js");
32
+ /** The cookie that names a stateful ABAP context. */
33
+ const CONTEXT_COOKIE = 'sap-contextid';
34
+ /**
35
+ * A `Cookie` header without `sap-contextid`, for a request that is not stateful.
36
+ *
37
+ * `sap-contextid` names the stateful ABAP context a `LOCK` opened, and SAP
38
+ * routes any request carrying it into that context — header or no header. So
39
+ * a "stateless" request that carried it was not stateless: it ran in the
40
+ * lock's context. Measured on E19 (BASIS 816) and E98 (BASIS 756), 2026-09-27,
41
+ * one connection, a package locked, written and unlocked twice:
42
+ *
43
+ * - E19: the second `PUT` answered 400 PAK/058 "Package … is already locked" —
44
+ * the first write's save was still in that context's `CL_PACKAGE` buffer;
45
+ * - E98: every `PUT` answered 423 "Resource … is not locked (invalid lock
46
+ * handle)".
47
+ *
48
+ * Without the cookie on the non-stateful requests, both systems wrote twice
49
+ * and deleted on the same connection. Eclipse ADT does the same thing its
50
+ * own way: its trace shows one stateful session for `LOCK` and `UNLOCK`
51
+ * alone, and every other request — the `PUT` included — in a session of its
52
+ * own.
53
+ *
54
+ * The jar itself keeps the cookie: the stateful requests that follow, the
55
+ * `UNLOCK` above all, still need to reach the context that holds the lock.
56
+ */
57
+ function withoutContextCookie(cookie) {
58
+ if (!cookie)
59
+ return cookie;
60
+ const kept = cookie
61
+ .split(/;\s*/)
62
+ .filter((pair) => pair && pair.split('=')[0]?.trim() !== CONTEXT_COOKIE)
63
+ .join('; ');
64
+ return kept || undefined;
65
+ }
66
+ /** The header that asks SAP for the stateful session over HTTP. */
67
+ const SESSION_TYPE_HEADER = 'x-sap-adt-sessiontype';
68
+ /**
69
+ * Whether the request runs in the stateful session: marked so by the
70
+ * connection, or — kept for callers that still write it themselves — carrying
71
+ * the session header already.
72
+ *
73
+ * Exported for the wires that rewrite a request before sending it: the
74
+ * verdict has to be taken on the request as the caller made it.
75
+ */
76
+ function isStatefulRequest(request) {
77
+ if (request.stateful)
78
+ return true;
79
+ const headers = request.headers;
80
+ if (!headers)
81
+ return false;
82
+ for (const [name, value] of Object.entries(headers)) {
83
+ if (name.toLowerCase() === SESSION_TYPE_HEADER &&
84
+ String(value).toLowerCase() === 'stateful') {
85
+ return true;
86
+ }
87
+ }
88
+ return false;
89
+ }
31
90
  /** A 404 there means the system has no such endpoint, not that it is unwell. */
32
91
  function absentEndpoint(error) {
33
92
  const status = error
@@ -307,17 +366,46 @@ class HttpTransport {
307
366
  * cookie, and replacing it would send the request out unauthenticated while
308
367
  * looking like it carried a session.
309
368
  */
310
- dress(headers) {
369
+ dress(request) {
370
+ const headers = request.headers;
371
+ const stateful = isStatefulRequest(request);
311
372
  const dressed = {
312
373
  ...this.affinityHeaders(),
313
374
  ...this.sessionHeaders(),
375
+ ...(stateful ? this.sessionTypeHeaders() : {}),
314
376
  ...headers,
315
377
  };
316
- const merged = (0, cookies_js_1.mergeCookieHeaders)(headers?.Cookie, this.combined ?? undefined);
317
- if (merged)
318
- dressed.Cookie = merged;
378
+ // Whatever the caller spelled the header as: HTTP does not tell `Cookie`
379
+ // from `cookie`, and a lowercase one spread in above would bypass the
380
+ // filter below.
381
+ let callerCookie;
382
+ for (const name of Object.keys(dressed)) {
383
+ if (name.toLowerCase() !== 'cookie')
384
+ continue;
385
+ callerCookie = (0, cookies_js_1.mergeCookieHeaders)(callerCookie, dressed[name]);
386
+ delete dressed[name];
387
+ }
388
+ const merged = (0, cookies_js_1.mergeCookieHeaders)(callerCookie, this.combined ?? undefined);
389
+ // Filtered on the merged header, not only on the jar: a caller's own
390
+ // `Cookie` can carry the context too — the connection's CSRF and 401
391
+ // retries put the whole jar there — and a non-stateful request must not
392
+ // reach the context whichever way the cookie arrived.
393
+ const cookie = stateful ? merged : withoutContextCookie(merged);
394
+ if (cookie)
395
+ dressed.Cookie = cookie;
396
+ else
397
+ delete dressed.Cookie;
319
398
  return dressed;
320
399
  }
400
+ /**
401
+ * How this wire asks for the stateful session: `x-sap-adt-sessiontype:
402
+ * stateful`, as Eclipse sends on its `LOCK` and `UNLOCK` over HTTP. A wire
403
+ * whose system is hurt by the header overrides this (BASIS 7.40, see
404
+ * `LegacyOnPremHttpTransport`).
405
+ */
406
+ sessionTypeHeaders() {
407
+ return { [SESSION_TYPE_HEADER]: 'stateful' };
408
+ }
321
409
  /** A path becomes an address; anything already absolute is left alone. */
322
410
  address(url) {
323
411
  if (!url.startsWith('/') || !this.options.baseUrl)
@@ -367,7 +455,7 @@ class HttpTransport {
367
455
  * to add it.
368
456
  */
369
457
  async send(request) {
370
- return this.dispatch(request, this.dress(request.headers));
458
+ return this.dispatch(request, this.dress(request));
371
459
  }
372
460
  /**
373
461
  * Send EXACTLY these headers, with nothing of the wire's live state mixed in.
@@ -37,6 +37,18 @@ export interface IAdtTransportRequest {
37
37
  * this seam is written against.
38
38
  */
39
39
  validateStatus?: (status: number) => boolean;
40
+ /**
41
+ * Whether the request runs in the stateful session — the one a lock is
42
+ * taken and released in.
43
+ *
44
+ * A flag rather than a header, because how a session is asked for is the
45
+ * wire's own business. Over HTTP it is `x-sap-adt-sessiontype: stateful`
46
+ * plus the `sap-contextid` cookie that addresses the context; over RFC it is
47
+ * neither — Eclipse's JCo trace carries no session header at all. A request
48
+ * that is not stateful must not reach that context: see
49
+ * `HttpTransport.dress`.
50
+ */
51
+ stateful?: boolean;
40
52
  }
41
53
  /** What came back, in the terms every transport can produce. */
42
54
  export interface IAdtTransportResponse {
@@ -1 +1 @@
1
- {"version":3,"file":"IAdtTransport.d.ts","sourceRoot":"","sources":["../../src/connection/IAdtTransport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"IAdtTransport.d.ts","sourceRoot":"","sources":["../../src/connection/IAdtTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;CACf;AAED,6DAA6D;AAC7D;;;;;;;GAOG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,2EAA2E;IAC3E,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,oBAAqB,SAAQ,kBAAkB;IAC9D;;;;OAIG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACpC,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,OAAO,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAEtB;;;;;;;;;;;;;;;;OAgBG;IACH,IAAI,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD;;;;;;;OAOG;IACH,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;;;;;;OAOG;IACH,IAAI,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAEpE;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAEhD,8EAA8E;IAC9E,OAAO,IAAI,MAAM,GAAG,IAAI,CAAC;IAEzB;;;;;;OAMG;IACH,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE1C;;;;;;;;;;;;OAYG;IACH,kBAAkB,IAAI,OAAO,CAAC;IAE9B,4EAA4E;IAC5E,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE1C;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExD,0EAA0E;IAC1E,SAAS,IAAI,MAAM,GAAG,IAAI,CAAC;IAE3B;;;;;;OAMG;IACH,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAE3C,oDAAoD;IACpD,aAAa,IAAI,IAAI,CAAC;CACvB;AAED;;;;;;;;;;;;GAYG;AACH;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAiB,SAAQ,aAAa;IACrD,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC;CAC3B;AAED,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,qBAAqB,GAAG,IAAI,CAQtE"}
@@ -16,8 +16,11 @@ import type { IAdtTransportRequest, IAdtTransportResponse } from './IAdtTranspor
16
16
  import { OnPremHttpTransport } from './OnPremHttpTransport.js';
17
17
  export declare class LegacyOnPremHttpTransport extends OnPremHttpTransport {
18
18
  readonly kind = "onprem-http-legacy";
19
+ /** The stateful header is the one this system is hurt by, so it asks with none. */
20
+ protected sessionTypeHeaders(): Record<string, string>;
19
21
  /**
20
- * The header never goes out, whatever the caller set the session type to.
22
+ * The header never goes out, whatever the caller set the session type to —
23
+ * not even when a caller wrote it into the request themselves.
21
24
  *
22
25
  * Dropped here rather than refused above: `setSessionType()` records what the
23
26
  * caller wants, and what actually travels is the wire's business. A caller
@@ -1 +1 @@
1
- {"version":3,"file":"LegacyOnPremHttpTransport.d.ts","sourceRoot":"","sources":["../../src/connection/LegacyOnPremHttpTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAI/D,qBAAa,yBAA0B,SAAQ,mBAAmB;IAChE,SAAkB,IAAI,wBAAwB;IAE9C;;;;;;;;OAQG;IACY,IAAI,CACjB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAIjC,OAAO,CAAC,kBAAkB;IAY1B;;;;;;;OAOG;IACM,kBAAkB,IAAI,OAAO;CAGvC"}
1
+ {"version":3,"file":"LegacyOnPremHttpTransport.d.ts","sourceRoot":"","sources":["../../src/connection/LegacyOnPremHttpTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAI/D,qBAAa,yBAA0B,SAAQ,mBAAmB;IAChE,SAAkB,IAAI,wBAAwB;IAE9C,mFAAmF;cAChE,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAI/D;;;;;;;;;OASG;IACY,IAAI,CACjB,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAUjC,OAAO,CAAC,kBAAkB;IAY1B;;;;;;;OAOG;IACM,kBAAkB,IAAI,OAAO;CAGvC"}
@@ -15,12 +15,18 @@
15
15
  */
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.LegacyOnPremHttpTransport = void 0;
18
+ const HttpTransport_js_1 = require("./HttpTransport.js");
18
19
  const OnPremHttpTransport_js_1 = require("./OnPremHttpTransport.js");
19
20
  const SESSION_TYPE_HEADER = 'x-sap-adt-sessiontype';
20
21
  class LegacyOnPremHttpTransport extends OnPremHttpTransport_js_1.OnPremHttpTransport {
21
22
  kind = 'onprem-http-legacy';
23
+ /** The stateful header is the one this system is hurt by, so it asks with none. */
24
+ sessionTypeHeaders() {
25
+ return {};
26
+ }
22
27
  /**
23
- * The header never goes out, whatever the caller set the session type to.
28
+ * The header never goes out, whatever the caller set the session type to —
29
+ * not even when a caller wrote it into the request themselves.
24
30
  *
25
31
  * Dropped here rather than refused above: `setSessionType()` records what the
26
32
  * caller wants, and what actually travels is the wire's business. A caller
@@ -29,7 +35,13 @@ class LegacyOnPremHttpTransport extends OnPremHttpTransport_js_1.OnPremHttpTrans
29
35
  * would move its locks somewhere they cannot be released.
30
36
  */
31
37
  async send(request) {
32
- return super.send(this.withoutSessionType(request));
38
+ // Decided before the header is dropped: a caller that asked for the
39
+ // stateful session by writing the header itself still gets its context
40
+ // cookie, only without the header this system is hurt by.
41
+ return super.send({
42
+ ...this.withoutSessionType(request),
43
+ stateful: (0, HttpTransport_js_1.isStatefulRequest)(request),
44
+ });
33
45
  }
34
46
  withoutSessionType(request) {
35
47
  if (!request.headers)
@@ -11,9 +11,12 @@
11
11
  * "invalid lock handle" on legacy systems (BASIS < 7.50) where stateful HTTP
12
12
  * sessions are not usable.
13
13
  *
14
- * **What is NOT here.** Cookies, the CSRF token, `x-sap-adt-sessiontype` and
15
- * the session lifecycle belong to the connection above this seam and are
16
- * already in `request.headers` by the time `send()` is called. A transport that
14
+ * **What is NOT here.** Cookies, the CSRF token and the session lifecycle
15
+ * belong to the connection above this seam and are already in
16
+ * `request.headers` by the time `send()` is called. `x-sap-adt-sessiontype` is
17
+ * not sent at all: `request.stateful` is HTTP's to express, and over RFC
18
+ * Eclipse's JCo trace carries no session header — its stateful session is a
19
+ * connection of its own, not a header on the request. A transport that
17
20
  * also captured cookies would be doing that work twice, and the two copies
18
21
  * would disagree the first time one of them was cleared.
19
22
  */
@@ -1 +1 @@
1
- {"version":3,"file":"RfcTransport.d.ts","sourceRoot":"","sources":["../../src/connection/RfcTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EACV,oBAAoB,EAEpB,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CACF,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAgFD,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD,qBAAa,YAAa,YAAW,gBAAgB;IA+EjD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhF1B,QAAQ,CAAC,IAAI,SAAS;IAEtB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAG,QAAQ,CAAU;IAEpC,OAAO,CAAC,YAAY,CAAiC;IACrD,kEAAkE;IAClE,OAAO,CAAC,cAAc,CAAM;IAE5B;;;;;;OAMG;IACH,MAAM,IAAI,IAAI;IAEd,2CAA2C;IAC3C,OAAO,IAAI,IAAI;IAIf;;;;;OAKG;IACG,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE9D,+BAA+B;IAC/B,SAAS,IAAI,IAAI;IAIjB;;;OAGG;IACH,cAAc,IAAI,IAAI;IAEtB;;;OAGG;IACH,6EAA6E;IAC7E,kBAAkB,IAAI,OAAO;IAI7B,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAQzC,2EAA2E;IAC3E,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIzC;;;;OAIG;IACH,aAAa,IAAI,IAAI;IAErB;;;;;OAKG;gBAEgB,OAAO,EAAE,MAAM,gBAAgB,EAC/B,MAAM,GAAE,OAAO,GAAG,IAAW,EAC7B,OAAO,GAAE,oBAAyB;IAGrD,oDAAoD;IACpD,OAAO,CAAC,OAAO;IAcT,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB3B,0DAA0D;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAatB,IAAI,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAkK1E"}
1
+ {"version":3,"file":"RfcTransport.d.ts","sourceRoot":"","sources":["../../src/connection/RfcTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EACV,oBAAoB,EAEpB,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,oBAAoB,CAAC;AAE5B,sFAAsF;AACtF,MAAM,WAAW,gBAAgB;IAC/B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,CACF,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAgFD,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAKD,qBAAa,YAAa,YAAW,gBAAgB;IA+EjD,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAhF1B,QAAQ,CAAC,IAAI,SAAS;IAEtB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAG,QAAQ,CAAU;IAEpC,OAAO,CAAC,YAAY,CAAiC;IACrD,kEAAkE;IAClE,OAAO,CAAC,cAAc,CAAM;IAE5B;;;;;;OAMG;IACH,MAAM,IAAI,IAAI;IAEd,2CAA2C;IAC3C,OAAO,IAAI,IAAI;IAIf;;;;;OAKG;IACG,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAE9D,+BAA+B;IAC/B,SAAS,IAAI,IAAI;IAIjB;;;OAGG;IACH,cAAc,IAAI,IAAI;IAEtB;;;OAGG;IACH,6EAA6E;IAC7E,kBAAkB,IAAI,OAAO;IAI7B,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAQzC,2EAA2E;IAC3E,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAIzC;;;;OAIG;IACH,aAAa,IAAI,IAAI;IAErB;;;;;OAKG;gBAEgB,OAAO,EAAE,MAAM,gBAAgB,EAC/B,MAAM,GAAE,OAAO,GAAG,IAAW,EAC7B,OAAO,GAAE,oBAAyB;IAGrD,oDAAoD;IACpD,OAAO,CAAC,OAAO;IAcT,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB3B,0DAA0D;IACpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAatB,IAAI,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAkK1E"}
@@ -12,9 +12,12 @@
12
12
  * "invalid lock handle" on legacy systems (BASIS < 7.50) where stateful HTTP
13
13
  * sessions are not usable.
14
14
  *
15
- * **What is NOT here.** Cookies, the CSRF token, `x-sap-adt-sessiontype` and
16
- * the session lifecycle belong to the connection above this seam and are
17
- * already in `request.headers` by the time `send()` is called. A transport that
15
+ * **What is NOT here.** Cookies, the CSRF token and the session lifecycle
16
+ * belong to the connection above this seam and are already in
17
+ * `request.headers` by the time `send()` is called. `x-sap-adt-sessiontype` is
18
+ * not sent at all: `request.stateful` is HTTP's to express, and over RFC
19
+ * Eclipse's JCo trace carries no session header — its stateful session is a
20
+ * connection of its own, not a header on the request. A transport that
18
21
  * also captured cookies would be doing that work twice, and the two copies
19
22
  * would disagree the first time one of them was cleared.
20
23
  */
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ Object.defineProperty(exports, "AdtOnPremConnector", { enumerable: true, get: fu
24
24
  var CloudHttpTransport_js_1 = require("./connection/CloudHttpTransport.js");
25
25
  Object.defineProperty(exports, "CloudHttpTransport", { enumerable: true, get: function () { return CloudHttpTransport_js_1.CloudHttpTransport; } });
26
26
  // The session lifecycle vocabulary — ISessionLifecycleAware, ADT_SESSION_ERROR —
27
- // is deliberately NOT exported here. It lives in @mcp-abap-adt/interfaces-adt, and a
27
+ // is deliberately NOT exported here. It lives in @mcp-abap-adt/interfaces-adt-connection, and a
28
28
  // consumer imports it from there: re-exporting a contract type gives it two
29
29
  // names and lets the two drift.
30
30
  // Connection classes - only final implementations
@@ -25,7 +25,7 @@
25
25
  * anything held against the old one;
26
26
  * an unchanged one says nothing about whether the server still has it.
27
27
  */
28
- import type { AdtSessionErrorCode } from '@mcp-abap-adt/interfaces-adt';
28
+ import type { AdtSessionErrorCode } from '@mcp-abap-adt/interfaces-adt-connection';
29
29
  export type TransitionKind = 'connect' | 'disconnect' | 'recover' | 'cleanup';
30
30
  export interface RequestLease {
31
31
  /** Teardown epoch at admission — the baseline a recovery compares against. */
@@ -1 +1 @@
1
- {"version":3,"file":"SessionLifecycle.d.ts","sourceRoot":"","sources":["../../src/session/SessionLifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGxE,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,0FAA0F;IAC1F,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC9B,iFAAiF;IACjF,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,mBAAmB,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,KAAK,GAAG;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAMvC;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAgD;IAC7D,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAK;IAErB,OAAO,CAAC,IAAI,CAAuC;IACnD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,WAAW,CAAiC;IAIpD,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAM5B;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;;;;;;;;;;;;OAaG;IACH,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED;;;;;OAKG;IACH,aAAa,CAAC,WAAW,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GAAG,IAAI;IAOzE;;;;;;OAMG;IACH,cAAc,IAAI,IAAI;IAItB,gBAAgB,IAAI,IAAI;IAKxB;;;;;;OAMG;IACH,OAAO,CACL,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACvC,WAAW,GAAG,aAAa,GAAG,UAAU;IAiB3C,YAAY,IAAI,IAAI;IAMpB,4EAA4E;IAC5E,YAAY,IAAI,YAAY;IAiB5B,2FAA2F;IAC3F,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED;;;;;;;OAOG;IACH,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,GAAG,OAAO;IAM3D,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,oBAAoB,GAAG,IAAI;IAelE,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAID;;;;;;;;;OASG;IACH,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAuBvE"}
1
+ {"version":3,"file":"SessionLifecycle.d.ts","sourceRoot":"","sources":["../../src/session/SessionLifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AAGnF,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC;AAE9E,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yDAAyD;IACzD,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,0FAA0F;IAC1F,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC9B,iFAAiF;IACjF,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,mBAAmB,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,KAAK,GAAG;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,CAMvC;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,KAAK,CAAgD;IAC7D,OAAO,CAAC,WAAW,CAA6B;IAChD,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,UAAU,CAAK;IAEvB,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAK;IAErB,OAAO,CAAC,IAAI,CAAuC;IACnD,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,WAAW,CAAiC;IAIpD,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAM5B;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;;;;;;;;;;;;OAaG;IACH,IAAI,iBAAiB,IAAI,MAAM,CAE9B;IAED;;;;;OAKG;IACH,aAAa,CAAC,WAAW,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GAAG,IAAI;IAOzE;;;;;;OAMG;IACH,cAAc,IAAI,IAAI;IAItB,gBAAgB,IAAI,IAAI;IAKxB;;;;;;OAMG;IACH,OAAO,CACL,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GACvC,WAAW,GAAG,aAAa,GAAG,UAAU;IAiB3C,YAAY,IAAI,IAAI;IAMpB,4EAA4E;IAC5E,YAAY,IAAI,YAAY;IAiB5B,2FAA2F;IAC3F,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED;;;;;;;OAOG;IACH,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,YAAY,CAAC,GAAG,OAAO;IAM3D,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,oBAAoB,GAAG,IAAI;IAelE,IAAI,iBAAiB,IAAI,OAAO,CAE/B;IAID;;;;;;;;;OASG;IACH,UAAU,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAuBvE"}
@@ -29,7 +29,7 @@
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
30
  exports.SessionLifecycle = void 0;
31
31
  exports.sessionError = sessionError;
32
- const interfaces_adt_1 = require("@mcp-abap-adt/interfaces-adt");
32
+ const interfaces_adt_connection_1 = require("@mcp-abap-adt/interfaces-adt-connection");
33
33
  function sessionError(code, message) {
34
34
  const error = new Error(message ?? code);
35
35
  error.code = code;
@@ -130,7 +130,7 @@ class SessionLifecycle {
130
130
  // ------------------------------------------------------------ admission ---
131
131
  assertUsable() {
132
132
  if (this.state !== 'connected' || this.teardownPending) {
133
- throw sessionError(interfaces_adt_1.ADT_SESSION_ERROR.NOT_CONNECTED);
133
+ throw sessionError(interfaces_adt_connection_1.ADT_SESSION_ERROR.NOT_CONNECTED);
134
134
  }
135
135
  }
136
136
  /** Asserts usability and counts the request in, in one synchronous step. */
@@ -1,4 +1,4 @@
1
- import type { ITimeoutConfig } from '@mcp-abap-adt/interfaces-adt';
1
+ import type { ITimeoutConfig } from '@mcp-abap-adt/interfaces-adt-connection';
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;
@@ -1 +1 @@
1
- {"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAGnE,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;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD"}
1
+ {"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAG9E,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;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD"}
package/docs/INDEX.md CHANGED
@@ -69,7 +69,7 @@ mcp-abap-connection/
69
69
 
70
70
  ### Session Management
71
71
  - **Overview**: [USAGE.md - Session Management](./USAGE.md#session-management)
72
- - **Stateful Mode**: Use `setSessionType('stateful')` for session headers
72
+ - **Stateful Mode**: Use `setSessionType('stateful')` for the LOCK / UNLOCK context; use stateless mode for GET / PUT
73
73
  - **Session State Persistence**: Handled by `@mcp-abap-adt/auth-broker` package
74
74
  - **API Methods**:
75
75
  - `getSessionId()` - Get current session ID (auto-generated UUID)
@@ -205,7 +205,7 @@ anything your own signatures name has to be installed. Install the ones you
205
205
  actually use:
206
206
 
207
207
  ```bash
208
- npm install @mcp-abap-adt/interfaces-adt # IAbapConnection, ITimeoutConfig, ADT_SESSION_ERROR
208
+ npm install @mcp-abap-adt/interfaces-adt-connection # IAbapConnection, ITimeoutConfig, ADT_SESSION_ERROR
209
209
  npm install @mcp-abap-adt/interfaces-auth # IAuthProvider, IRenewableCredential, ITokenRefresher
210
210
  npm install @mcp-abap-adt/interfaces-auth-sap # ISapConfig, SapAuthType, ICertificateMaterialLoader
211
211
  npm install @mcp-abap-adt/interfaces-network # NETWORK_ERROR_CODES, the WebSocket contracts
package/docs/SCOPE.md CHANGED
@@ -41,4 +41,4 @@ It forwards every `(name, value)` pair from the JS `Client(params)` object to `R
41
41
 
42
42
  - [`INSTALLATION.md`](./INSTALLATION.md)
43
43
  - [`USAGE.md`](./USAGE.md)
44
- - Sibling packages: `@mcp-abap-adt/interfaces-adt`, `@mcp-abap-adt/interfaces-auth`, `@mcp-abap-adt/interfaces-auth-sap`, `@mcp-abap-adt/interfaces-network`, `@mcp-abap-adt/interfaces-utils`, `@mcp-abap-adt/sap-rfc-lite`, `@mcp-abap-adt/auth-broker`
44
+ - Sibling packages: `@mcp-abap-adt/interfaces-adt-connection`, `@mcp-abap-adt/interfaces-auth`, `@mcp-abap-adt/interfaces-auth-sap`, `@mcp-abap-adt/interfaces-network`, `@mcp-abap-adt/interfaces-utils`, `@mcp-abap-adt/sap-rfc-lite`, `@mcp-abap-adt/auth-broker`
@@ -1,8 +1,8 @@
1
1
  # Stateful Session Guide (Connection Layer)
2
2
 
3
3
  > **The header is not what tracks your lock.** `setSessionType('stateful')` sets
4
- > a per-request header and nothing more — another handler can flip it back while
5
- > your lock is genuinely open, and a batch never sets it at all. Nothing in this
4
+ > the mode for subsequent requests; HTTP uses it to select the session header
5
+ > and context cookie. Another handler can flip it back while your lock is open. Nothing in this
6
6
  > layer tracks locks: that belongs to `@mcp-abap-adt/adt-clients`, which holds the
7
7
  > handles and pairs each LOCK with its UNLOCK per object. What the connection
8
8
  > offers is `beginCriticalSection()` / `endCriticalSection()`, so a short timeout
@@ -24,6 +24,39 @@ The connection layer **does not** decide when to lock/unlock objects—that logi
24
24
 
25
25
  ---
26
26
 
27
+ ## HTTP context isolation (9.3.1)
28
+
29
+ The authentication session (`SAP_SESSIONID`) and the stateful ABAP context
30
+ (`sap-contextid`) have different roles. HTTP requests keep authentication
31
+ cookies and the CSRF token in either mode. Only stateful requests carry
32
+ `sap-contextid`; the jar retains it while stateless requests run so that a
33
+ later UNLOCK can still reach the context holding the lock.
34
+
35
+ Use stateful mode for LOCK and UNLOCK, and stateless mode for the intervening
36
+ GET and PUT. Passing a lock handle to PUT does not require the context cookie.
37
+ The ADT clients choose these modes; callers using connectors directly must
38
+ choose the mode for each operation themselves.
39
+
40
+ Before 9.3.1, the whole jar went out even in stateless mode. After LOCK, a PUT
41
+ therefore ran in the lock's context. Package updates on E19 (BASIS 816) failed
42
+ on the second write with **400 PAK/058, “Package … is already locked”**, and a
43
+ subsequent delete failed too. On E98 (BASIS 756), PUT failed with **423,
44
+ “Resource … is not locked (invalid lock handle)”**. Filtering the context
45
+ cookie allowed two updates and a delete on one connection on both systems.
46
+ The filter also applies to cookies supplied in request headers and retries.
47
+
48
+ | Transport | Stateful request |
49
+ | --- | --- |
50
+ | HTTP (on-prem / cloud) | Sends `x-sap-adt-sessiontype: stateful` and the context cookie, when held |
51
+ | Legacy on-prem HTTP | Keeps the context cookie but omits the session-type header |
52
+ | RFC | Uses its RFC conversation; `setSessionType()` adds no HTTP session-type header |
53
+
54
+ A caller-supplied stateful header is also recognized, including by the legacy
55
+ HTTP transport before it removes that header. Custom `IAdtTransport`
56
+ implementations must read `request.stateful`; the connection no longer adds
57
+ `x-sap-adt-sessiontype` itself. The cookie fix was verified on E19 and E98;
58
+ BASIS 7.40 and ABAP Cloud were not integration-tested for this change.
59
+
27
60
  ## Enabling Stateful Sessions
28
61
 
29
62
  Every example below builds on this much, so it is stated once:
@@ -62,10 +95,10 @@ const connection = new AdtOnPremConnector(
62
95
  );
63
96
  await connection.connect(); // required before any request
64
97
 
65
- // Enable stateful session mode (adds x-sap-adt-sessiontype: stateful header)
98
+ // Enable the stateful HTTP context (session header and context cookie)
66
99
  connection.setSessionType('stateful');
67
100
 
68
- // Now all requests share the same session (cookies, CSRF token)
101
+ // Following requests use the stateful context until the mode is changed
69
102
  await connection.makeAdtRequest({ method: 'GET', url: '/sap/bc/adt/discovery' , timeout: getTimeout('default') });
70
103
 
71
104
  // Switch back to stateless
@@ -178,7 +211,7 @@ wrong fixes:
178
211
  |---|---|---|
179
212
  | **the connection** | one TCP socket | you, the network, a timeout firing. **Ends nothing on the server** |
180
213
  | **the HTTP session** | the ICF conversation, held by the cookie jar on this side | dropping the cookies, or a logoff. Nothing about it is in doubt |
181
- | **the ABAP session** | the server-side context named by `SAP_SESSIONID_<SID>_<CLIENT>` — the roll area a stateful request runs in | **only the server.** Its own idle timeout, which this side can neither read nor influence, or an explicit logoff |
214
+ | **the ABAP session** | the authentication session named by `SAP_SESSIONID_<SID>_<CLIENT>`; `sap-contextid` separately addresses the stateful ABAP context | **only the server.** Its own idle timeout, which this side can neither read nor influence, or an explicit logoff |
182
215
  | **the lock** | an enqueue entry, **owned by the ABAP session** | that ABAP session ending. **When it goes, its locks go with it** |
183
216
 
184
217
  The two middle rows are the ones worth keeping apart, because everything that
@@ -206,7 +239,7 @@ lock is still very much held. That — not any teardown — is why
206
239
  duration of a `lock → modify → unlock` chain.
207
240
 
208
241
  Since 8.0.0 you reach it through the contract rather than the class: it is
209
- `ICriticalSection` in `@mcp-abap-adt/interfaces-adt`, which this connection declares.
242
+ `ICriticalSection` in `@mcp-abap-adt/interfaces-adt-connection`, which this connection declares.
210
243
  What it promises is narrow and worth stating exactly — inside a section the
211
244
  *ordinary* per-request deadline does not apply. Not that no request can be cut
212
245
  short: the ceiling is `SAP_TIMEOUT_CRITICAL`, ten minutes by default, and a
package/docs/USAGE.md CHANGED
@@ -250,9 +250,13 @@ await connection.connect();
250
250
  await connection.makeAdtRequest({ method: 'GET', url: '/sap/bc/adt/discovery' , timeout: getTimeout('default') });
251
251
  ```
252
252
 
253
- ### Stateful Mode (Session Headers)
253
+ ### Stateful Mode (HTTP Context)
254
254
 
255
- Enable stateful session mode for operations requiring consistent session state:
255
+ Use stateful mode for LOCK and UNLOCK, then stateless mode for GET and PUT
256
+ between them. Since 9.3.1, stateless HTTP requests omit `sap-contextid` while
257
+ retaining authentication cookies and the CSRF token. This prevents package
258
+ writes from running in the lock context and failing with PAK/058 or an invalid
259
+ lock handle. See [HTTP context isolation](./STATEFUL_SESSION_GUIDE.md#http-context-isolation-931).
256
260
 
257
261
  ```typescript
258
262
  import { AdtOnPremConnector, BasicAuthProvider, OnPremHttpTransport, getTimeout } from '@mcp-abap-adt/connection';
@@ -267,10 +271,10 @@ const connection = new AdtOnPremConnector(
267
271
  );
268
272
  await connection.connect();
269
273
 
270
- // Enable stateful session mode (adds x-sap-adt-sessiontype: stateful header)
274
+ // Enable the stateful HTTP context (session header and context cookie)
271
275
  connection.setSessionType('stateful');
272
276
 
273
- // Now all requests share the same session (cookies, CSRF token)
277
+ // Following requests use the stateful context until the mode is changed
274
278
  await connection.makeAdtRequest({ method: 'GET', url: '/sap/bc/adt/discovery' , timeout: getTimeout('default') });
275
279
 
276
280
  // Check session mode
@@ -292,7 +296,7 @@ implied. Four things follow from it.
292
296
 
293
297
  > **Availability.** `connect()` is on the shared `IAbapConnection` contract and
294
298
  > works on every connection. The rest of this section is on the contract too, but
295
- > as a **capability atom** in `@mcp-abap-adt/interfaces-adt` rather than as methods on
299
+ > as a **capability atom** in `@mcp-abap-adt/interfaces-adt-connection` rather than as methods on
296
300
  > `IAbapConnection`: `ISessionLifecycleAware` — `disconnect()`, `isConnected()`,
297
301
  > `getSessionIdentity()`.
298
302
  >
@@ -397,10 +401,10 @@ establishment rather than opening a session each.
397
401
 
398
402
  Match on the code rather than the message, so a rename is a compile error on your
399
403
  side instead of a condition that silently stops matching. The codes live in
400
- `@mcp-abap-adt/interfaces-adt` — import them from there, not from this package:
404
+ `@mcp-abap-adt/interfaces-adt-connection` — import them from there, not from this package:
401
405
 
402
406
  ```typescript
403
- import { ADT_SESSION_ERROR } from '@mcp-abap-adt/interfaces-adt';
407
+ import { ADT_SESSION_ERROR } from '@mcp-abap-adt/interfaces-adt-connection';
404
408
  import { getTimeout } from '@mcp-abap-adt/connection';
405
409
 
406
410
  const options = {
@@ -972,7 +976,7 @@ See [examples/](../examples/) for complete working examples:
972
976
  ## Best Practices
973
977
 
974
978
  1. **State the three axes**: the connector says which system, the provider says which credential, the transport says which wire. Nothing is detected, and a connection that had to guess would guess wrong on the case that matters
975
- 2. **Enable Stateful Mode**: Use `setSessionType('stateful')` for multi-request operations (locks, transactions)
979
+ 2. **Enable Stateful Mode**: Use `setSessionType('stateful')` for LOCK / UNLOCK and stateless mode for intervening GET / PUT
976
980
  3. **Token Refresh**: For cloud systems, use `@mcp-abap-adt/auth-broker` for token refresh functionality
977
981
  4. **Session State Persistence**: Use `@mcp-abap-adt/auth-broker` for session state persistence
978
982
  5. **Handle Errors Gracefully**: Wrap requests in try-catch blocks and check `failure.response` for HTTP errors
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/connection",
3
- "version": "9.2.2",
3
+ "version": "9.3.1",
4
4
  "description": "ABAP connection layer for MCP ABAP ADT server",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -53,7 +53,7 @@
53
53
  "node": ">=18.0.0"
54
54
  },
55
55
  "dependencies": {
56
- "@mcp-abap-adt/interfaces-adt": "^10.0.0",
56
+ "@mcp-abap-adt/interfaces-adt-connection": "^1.0.0",
57
57
  "@mcp-abap-adt/interfaces-auth": "^2.0.1",
58
58
  "@mcp-abap-adt/interfaces-auth-sap": "^1.0.1",
59
59
  "@mcp-abap-adt/interfaces-network": "^2.0.0",