@mcp-abap-adt/connection 5.0.0 → 6.0.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/CHANGELOG.md +134 -1
- package/README.md +204 -48
- package/dist/auth/providers.d.ts +38 -9
- package/dist/auth/providers.d.ts.map +1 -1
- package/dist/auth/providers.js +46 -3
- package/dist/connection/AbstractAbapConnection.d.ts +83 -136
- package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.js +241 -763
- package/dist/connection/AdtCloudConnector.d.ts +12 -7
- package/dist/connection/AdtCloudConnector.d.ts.map +1 -1
- package/dist/connection/AdtCloudConnector.js +2 -6
- package/dist/connection/AdtOnPremConnector.d.ts +20 -7
- package/dist/connection/AdtOnPremConnector.d.ts.map +1 -1
- package/dist/connection/AdtOnPremConnector.js +2 -6
- package/dist/connection/CloudHttpTransport.d.ts +37 -0
- package/dist/connection/CloudHttpTransport.d.ts.map +1 -0
- package/dist/{session/CloudSecuritySessionStrategy.js → connection/CloudHttpTransport.js} +59 -49
- package/dist/connection/CredentialAbapConnection.d.ts +8 -41
- package/dist/connection/CredentialAbapConnection.d.ts.map +1 -1
- package/dist/connection/CredentialAbapConnection.js +44 -111
- package/dist/connection/HttpTransport.d.ts +178 -0
- package/dist/connection/HttpTransport.d.ts.map +1 -0
- package/dist/connection/HttpTransport.js +402 -0
- package/dist/connection/IAdtTransport.d.ts +232 -0
- package/dist/connection/IAdtTransport.d.ts.map +1 -0
- package/dist/connection/IAdtTransport.js +28 -0
- package/dist/connection/LegacyOnPremHttpTransport.d.ts +40 -0
- package/dist/connection/LegacyOnPremHttpTransport.d.ts.map +1 -0
- package/dist/connection/LegacyOnPremHttpTransport.js +57 -0
- package/dist/connection/OnPremHttpTransport.d.ts +45 -0
- package/dist/connection/OnPremHttpTransport.d.ts.map +1 -0
- package/dist/connection/OnPremHttpTransport.js +91 -0
- package/dist/connection/RfcTransport.d.ts +89 -0
- package/dist/connection/RfcTransport.d.ts.map +1 -0
- package/dist/connection/RfcTransport.js +256 -0
- package/dist/connection/rfcConversation.d.ts +44 -0
- package/dist/connection/rfcConversation.d.ts.map +1 -0
- package/dist/connection/rfcConversation.js +71 -0
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -18
- package/dist/utils/timeouts.d.ts +6 -19
- package/dist/utils/timeouts.d.ts.map +1 -1
- package/dist/utils/timeouts.js +6 -22
- package/docs/INDEX.md +5 -2
- package/docs/INSTALLATION.md +28 -10
- package/docs/JWT_AUTH_TOOLS.md +20 -4
- package/docs/MIGRATION-6.0.md +359 -0
- package/docs/SCOPE.md +1 -1
- package/docs/STATEFUL_SESSION_GUIDE.md +86 -17
- package/docs/USAGE.md +260 -119
- package/examples/basic-connection.js +15 -3
- package/examples/jwt-with-token-refresh.js +15 -7
- package/examples/saml-connection.js +15 -2
- package/package.json +12 -10
- package/dist/__tests__/helpers/session.d.ts +0 -15
- package/dist/__tests__/helpers/session.d.ts.map +0 -1
- package/dist/__tests__/helpers/session.js +0 -19
- package/dist/auth/IAuthProvider.d.ts +0 -84
- package/dist/auth/IAuthProvider.d.ts.map +0 -1
- package/dist/auth/IAuthProvider.js +0 -21
- package/dist/connection/BaseAbapConnection.d.ts +0 -29
- package/dist/connection/BaseAbapConnection.d.ts.map +0 -1
- package/dist/connection/BaseAbapConnection.js +0 -81
- package/dist/connection/CertificateAbapConnection.d.ts +0 -35
- package/dist/connection/CertificateAbapConnection.d.ts.map +0 -1
- package/dist/connection/CertificateAbapConnection.js +0 -91
- package/dist/connection/JwtAbapConnection.d.ts +0 -131
- package/dist/connection/JwtAbapConnection.d.ts.map +0 -1
- package/dist/connection/JwtAbapConnection.js +0 -376
- package/dist/connection/KerberosAbapConnection.d.ts +0 -32
- package/dist/connection/KerberosAbapConnection.d.ts.map +0 -1
- package/dist/connection/KerberosAbapConnection.js +0 -128
- package/dist/connection/RfcAbapConnection.d.ts +0 -44
- package/dist/connection/RfcAbapConnection.d.ts.map +0 -1
- package/dist/connection/RfcAbapConnection.js +0 -324
- package/dist/connection/SamlAbapConnection.d.ts +0 -31
- package/dist/connection/SamlAbapConnection.d.ts.map +0 -1
- package/dist/connection/SamlAbapConnection.js +0 -81
- package/dist/connection/connectionFactory.d.ts +0 -25
- package/dist/connection/connectionFactory.d.ts.map +0 -1
- package/dist/connection/connectionFactory.js +0 -84
- package/dist/session/CloudSecuritySessionStrategy.d.ts +0 -32
- package/dist/session/CloudSecuritySessionStrategy.d.ts.map +0 -1
- package/dist/session/IcfSessionStrategy.d.ts +0 -27
- package/dist/session/IcfSessionStrategy.d.ts.map +0 -1
- package/dist/session/IcfSessionStrategy.js +0 -62
- package/dist/session/SessionStrategy.d.ts +0 -86
- package/dist/session/SessionStrategy.d.ts.map +0 -1
- package/dist/session/SessionStrategy.js +0 -33
- package/docs/superpowers/specs/2026-08-21-platform-connectors.md +0 -108
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/auth/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/auth/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,0BAA0B,EAC1B,oBAAoB,EACpB,UAAU,EACV,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,2DAA2D;AAC3D,qBAAa,iBAAkB,YAAW,aAAa;IAiBnD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAjB3B,QAAQ,CAAC,IAAI,WAAW;IAExB,wEAAwE;IAClE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,yDAAyD;IACzD,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB,gFAAgF;IAChF,iBAAiB,IAAI,oBAAoB;gBAKtB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;IAG7B,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAGpD;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,YAAW,oBAAoB;IAyB1D,OAAO,CAAC,QAAQ,CAAC,MAAM;IAxBzB,QAAQ,CAAC,IAAI,WAAW;IAExB,wEAAwE;IAClE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,yDAAyD;IACzD,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB,gFAAgF;IAChF,iBAAiB,IAAI,oBAAoB;IAIzC;;;;;;;OAOG;gBAEgB,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAG7E;;;;;;;OAOG;IACG,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUnD;;;;;;;;;;;;;;;;;;OAkBG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,aAAa;IAWxC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAV3C,QAAQ,CAAC,IAAI,UAAU;IAEvB,wEAAwE;IAClE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE9B,gFAAgF;IAChF,iBAAiB,IAAI,oBAAoB;gBAIZ,cAAc,EAAE,MAAM;IAEnD,0EAA0E;IACpE,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAInD,8BAA8B;IAC9B,OAAO,IAAI,MAAM;CAGlB;AAED;;;;;;GAMG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IAWzD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAXzB,QAAQ,CAAC,IAAI,iBAAiB;IAE9B,yDAAyD;IACzD,OAAO,IAAI,MAAM,GAAG,IAAI;IAIxB,OAAO,CAAC,QAAQ,CAAqC;gBAGlC,MAAM,EAAE,0BAA0B,EAClC,MAAM,EAAE,UAAU;IAG/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B,+DAA+D;IACzD,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAInD,iBAAiB,IAAI,oBAAoB;CAS1C"}
|
package/dist/auth/providers.js
CHANGED
|
@@ -15,6 +15,16 @@ class BasicAuthProvider {
|
|
|
15
15
|
username;
|
|
16
16
|
password;
|
|
17
17
|
kind = 'basic';
|
|
18
|
+
/** Nothing to get ready: this credential is complete as constructed. */
|
|
19
|
+
async prepare() { }
|
|
20
|
+
/** Not cookies. This one authenticates with a header. */
|
|
21
|
+
cookies() {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/** No TLS material: this credential lives in a header, not in the transport. */
|
|
25
|
+
transportMaterial() {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
18
28
|
constructor(username, password) {
|
|
19
29
|
this.username = username;
|
|
20
30
|
this.password = password;
|
|
@@ -39,6 +49,16 @@ exports.BasicAuthProvider = BasicAuthProvider;
|
|
|
39
49
|
class TokenAuthProvider {
|
|
40
50
|
source;
|
|
41
51
|
kind = 'token';
|
|
52
|
+
/** Nothing to get ready: this credential is complete as constructed. */
|
|
53
|
+
async prepare() { }
|
|
54
|
+
/** Not cookies. This one authenticates with a header. */
|
|
55
|
+
cookies() {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
/** No TLS material: this credential lives in a header, not in the transport. */
|
|
59
|
+
transportMaterial() {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
42
62
|
/**
|
|
43
63
|
* A token, or something that can produce one.
|
|
44
64
|
*
|
|
@@ -69,6 +89,17 @@ class TokenAuthProvider {
|
|
|
69
89
|
/**
|
|
70
90
|
* The token was refused, so force a new one.
|
|
71
91
|
*
|
|
92
|
+
* Declared through `IRenewableCredential` rather than as an optional member
|
|
93
|
+
* of every credential, so a consumer narrows to it: a password has nothing
|
|
94
|
+
* behind it to ask again, and a SAML session was negotiated elsewhere.
|
|
95
|
+
*
|
|
96
|
+
* **Nothing in this package calls it.** A token that EXPIRED is replaced
|
|
97
|
+
* inside `authorizationHeader()` above, which is asked per request — nobody
|
|
98
|
+
* decides anything. This is the other case, a token the source still believes
|
|
99
|
+
* in and the server refuses, and whether a refusal meant that is a judgement
|
|
100
|
+
* made with what the caller knows. So the refusal surfaces, and this is the
|
|
101
|
+
* seam the caller decides with.
|
|
102
|
+
*
|
|
72
103
|
* `getToken()` is documented to return the cached token while it believes it
|
|
73
104
|
* is still valid — which is exactly the situation after a 401 on a token the
|
|
74
105
|
* provider has not yet noticed is dead. `refreshToken()` is the contract's
|
|
@@ -93,11 +124,18 @@ exports.TokenAuthProvider = TokenAuthProvider;
|
|
|
93
124
|
class SamlAuthProvider {
|
|
94
125
|
sessionCookies;
|
|
95
126
|
kind = 'saml';
|
|
127
|
+
/** Nothing to get ready: this credential is complete as constructed. */
|
|
128
|
+
async prepare() { }
|
|
129
|
+
/** No TLS material: this credential lives in a header, not in the transport. */
|
|
130
|
+
transportMaterial() {
|
|
131
|
+
return {};
|
|
132
|
+
}
|
|
96
133
|
constructor(sessionCookies) {
|
|
97
134
|
this.sessionCookies = sessionCookies;
|
|
98
135
|
}
|
|
136
|
+
/** Not a header: this credential authenticates with the cookies below. */
|
|
99
137
|
async authorizationHeader() {
|
|
100
|
-
return
|
|
138
|
+
return null;
|
|
101
139
|
}
|
|
102
140
|
/** The cookies to present. */
|
|
103
141
|
cookies() {
|
|
@@ -116,6 +154,10 @@ class CertificateAuthProvider {
|
|
|
116
154
|
loader;
|
|
117
155
|
config;
|
|
118
156
|
kind = 'certificate';
|
|
157
|
+
/** Not cookies. This one authenticates with a header. */
|
|
158
|
+
cookies() {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
119
161
|
material = null;
|
|
120
162
|
constructor(loader, config) {
|
|
121
163
|
this.loader = loader;
|
|
@@ -126,10 +168,11 @@ class CertificateAuthProvider {
|
|
|
126
168
|
this.material = await this.loader.load(this.config);
|
|
127
169
|
}
|
|
128
170
|
}
|
|
171
|
+
/** Not a header: this credential authenticates through TLS. */
|
|
129
172
|
async authorizationHeader() {
|
|
130
|
-
return
|
|
173
|
+
return null;
|
|
131
174
|
}
|
|
132
|
-
|
|
175
|
+
transportMaterial() {
|
|
133
176
|
if (!this.material) {
|
|
134
177
|
throw new Error('CertificateAuthProvider: certificate material not loaded. connect() prepares it; a request before that has nothing to present.');
|
|
135
178
|
}
|
|
@@ -2,8 +2,8 @@ import { type IAdtResponse, type ISessionLifecycleAware } from '@mcp-abap-adt/in
|
|
|
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';
|
|
5
|
-
import type { SessionStrategy } from '../session/SessionStrategy.js';
|
|
6
5
|
import type { AbapConnection, AbapRequestOptions } from './AbapConnection.js';
|
|
6
|
+
import { type IAdtSessionContext, type IAdtTransport } from './IAdtTransport.js';
|
|
7
7
|
/**
|
|
8
8
|
* Declares the capabilities explicitly rather than satisfying them by accident.
|
|
9
9
|
* `AbapConnection` is the base contract every transport honours; these two are
|
|
@@ -28,6 +28,16 @@ import type { AbapConnection, AbapRequestOptions } from './AbapConnection.js';
|
|
|
28
28
|
*/
|
|
29
29
|
declare abstract class AbstractAbapConnection implements AbapConnection, ISessionLifecycleAware {
|
|
30
30
|
private readonly config;
|
|
31
|
+
/**
|
|
32
|
+
* Required, and constructed by the caller.
|
|
33
|
+
*
|
|
34
|
+
* There is no default to fall back to and nothing is worked out from the
|
|
35
|
+
* config: the wire is a fact about the deployment, and a library that
|
|
36
|
+
* picked one would be guessing at the thing this design exists to stop
|
|
37
|
+
* guessing at. It also carries what the caller alone can wire — the
|
|
38
|
+
* credential's TLS material, the client — which is why it arrives built.
|
|
39
|
+
*/
|
|
40
|
+
readonly transport: IAdtTransport;
|
|
31
41
|
protected readonly logger: ILogger | null;
|
|
32
42
|
/**
|
|
33
43
|
* Owns session state, admission and teardown ordering. Composed rather than
|
|
@@ -35,20 +45,17 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
35
45
|
* transports share this unit instead of a base class.
|
|
36
46
|
*/
|
|
37
47
|
protected readonly lifecycle: SessionLifecycle;
|
|
38
|
-
private axiosInstance;
|
|
39
|
-
private csrfToken;
|
|
40
|
-
private cookies;
|
|
41
|
-
private cookieStore;
|
|
42
48
|
private baseUrl;
|
|
43
49
|
private sessionId;
|
|
44
50
|
private sessionMode;
|
|
45
|
-
private skipSessionType;
|
|
46
51
|
/**
|
|
47
52
|
* When true, requests are treated as part of an uninterruptible critical
|
|
48
53
|
* section (e.g. a lock → modify → unlock chain). In this state a short
|
|
49
|
-
* per-request timeout must NOT abort the request mid-flight
|
|
50
|
-
*
|
|
51
|
-
*
|
|
54
|
+
* per-request timeout must NOT abort the request mid-flight: the abort ends
|
|
55
|
+
* nothing server-side — only the server ends an ABAP session, on an idle
|
|
56
|
+
* timeout this side cannot see — it ends what this side KNOWS. Whether the
|
|
57
|
+
* change was applied becomes unknowable, and the handle `unlock` needs is
|
|
58
|
+
* lost while the lock lives on in that session. While set, makeAdtRequest
|
|
52
59
|
* raises the effective timeout to CRITICAL_SECTION_TIMEOUT (a large ceiling)
|
|
53
60
|
* so the request runs to completion instead of being interrupted.
|
|
54
61
|
*/
|
|
@@ -56,7 +63,6 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
56
63
|
/** Reference count for nested beginCriticalSection()/endCriticalSection() pairs. */
|
|
57
64
|
private criticalSectionDepth;
|
|
58
65
|
/** The default release deadline, validated once at construction. */
|
|
59
|
-
private readonly releaseDeadlineMs;
|
|
60
66
|
/**
|
|
61
67
|
* The logoff for the session this connection last held, while it is on its
|
|
62
68
|
* way. One, because a connection holds one session: `connect()` opens it and
|
|
@@ -90,7 +96,6 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
90
96
|
* with the rest of the session state: it names a server for a session that no
|
|
91
97
|
* longer exists.
|
|
92
98
|
*/
|
|
93
|
-
private appServer;
|
|
94
99
|
/**
|
|
95
100
|
* Whether the preflight opened a session of its own.
|
|
96
101
|
*
|
|
@@ -99,23 +104,27 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
99
104
|
* Only a preflight answered with a session address opened one, and only that
|
|
100
105
|
* is worth saying goodbye to when establishment then fails.
|
|
101
106
|
*/
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
protected constructor(config: SapConfig,
|
|
108
|
+
/**
|
|
109
|
+
* Required, and constructed by the caller.
|
|
110
|
+
*
|
|
111
|
+
* There is no default to fall back to and nothing is worked out from the
|
|
112
|
+
* config: the wire is a fact about the deployment, and a library that
|
|
113
|
+
* picked one would be guessing at the thing this design exists to stop
|
|
114
|
+
* guessing at. It also carries what the caller alone can wire — the
|
|
115
|
+
* credential's TLS material, the client — which is why it arrives built.
|
|
116
|
+
*/
|
|
117
|
+
transport: IAdtTransport, logger: ILogger | null, sessionId?: string);
|
|
108
118
|
/**
|
|
109
119
|
* Set session type (stateful or stateless)
|
|
110
120
|
* Controls whether x-sap-adt-sessiontype: stateful header is added to requests
|
|
111
121
|
* - stateful: SAP maintains session state between requests (locks, transactions)
|
|
112
122
|
* - stateless: Each request is independent
|
|
113
123
|
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* resulting in HTTP 423 on subsequent PUT requests.
|
|
124
|
+
* Whether the header actually goes out is the WIRE's: a system where the
|
|
125
|
+
* stateful header makes the server store locks in session memory instead of
|
|
126
|
+
* the enqueue table — BASIS 7.40 — is a different deployment, and a
|
|
127
|
+
* deployment is a transport, not a flag on the connection.
|
|
119
128
|
*/
|
|
120
129
|
setSessionType(type: 'stateful' | 'stateless'): void;
|
|
121
130
|
/**
|
|
@@ -129,9 +138,11 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
129
138
|
* in a finally, AFTER unlocking). While in a critical section, a short
|
|
130
139
|
* per-request timeout is not applied — makeAdtRequest uses a large ceiling
|
|
131
140
|
* (CRITICAL_SECTION_TIMEOUT, env SAP_TIMEOUT_CRITICAL) instead — so a slow
|
|
132
|
-
* PUT/activate/unlock is not aborted mid-flight.
|
|
133
|
-
*
|
|
134
|
-
*
|
|
141
|
+
* PUT/activate/unlock is not aborted mid-flight. An abort does not end the
|
|
142
|
+
* ABAP session — that session is the server's, and only the server ends it,
|
|
143
|
+
* on an idle timeout this side cannot influence — it strands the caller:
|
|
144
|
+
* the outcome is unknown and the handle `unlock` needs is gone, while the
|
|
145
|
+
* lock survives inside the ABAP session that owns it.
|
|
135
146
|
*
|
|
136
147
|
* Nesting is reference-counted so nested begin/end pairs are safe.
|
|
137
148
|
*/
|
|
@@ -207,12 +218,12 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
207
218
|
* request cannot use, so the leak surfaces as a half-written object rather
|
|
208
219
|
* than as anything about sessions.
|
|
209
220
|
*
|
|
210
|
-
* **
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* is what made a teardown unbounded, which blocks every
|
|
215
|
-
* the serialized tail.
|
|
221
|
+
* **Nothing is waited for.** The logoff is dispatched, not awaited: this
|
|
222
|
+
* method notifies, and whether the server has acted on the notice is not
|
|
223
|
+
* something it reports. Finishing chains and releasing locks stay the
|
|
224
|
+
* caller's to do BEFORE calling — waiting here on a request that carries no
|
|
225
|
+
* timeout by design is what made a teardown unbounded, which blocks every
|
|
226
|
+
* later transition on the serialized tail.
|
|
216
227
|
*
|
|
217
228
|
* **Requests already in flight are not waited for, and the logoff ends the
|
|
218
229
|
* session they are running on** — so they will start failing against a
|
|
@@ -221,58 +232,13 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
221
232
|
* dropped the cookie. Generation fencing (see `SessionLifecycle.isCurrent`)
|
|
222
233
|
* keeps their results from reaching this connection either way.
|
|
223
234
|
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
* have chosen to take. Anything that is not a finite, non-negative number
|
|
232
|
-
* is reported and the default used instead — this method is called from a
|
|
233
|
-
* `finally`, where throwing would replace the error that sent the caller
|
|
234
|
-
* there. The configured default is checked once, at construction, so a
|
|
235
|
-
* misconfigured `SAP_RELEASE_DEADLINE_MS` fails before a connection exists
|
|
236
|
-
* rather than at every teardown.
|
|
237
|
-
*/
|
|
238
|
-
disconnect(options?: {
|
|
239
|
-
deadlineMs?: number;
|
|
240
|
-
}): Promise<void>;
|
|
241
|
-
/**
|
|
242
|
-
* Tells the server this session is no longer needed. Best effort, and the
|
|
243
|
-
* answer is not depended on: whether and when the server frees it is its own
|
|
244
|
-
* affair, and this connection does not check afterwards.
|
|
245
|
-
*
|
|
246
|
-
* ICF rather than ADT because ADT publishes no such endpoint: its discovery
|
|
247
|
-
* document lists none on any reachable system — on-prem, cloud, or legacy —
|
|
248
|
-
* and the ADT logon is the discovery call itself. `/sap/public/bc/icf/logoff`
|
|
249
|
-
* is the platform's own, and answers `200` while expiring the session cookie.
|
|
250
|
-
*
|
|
251
|
-
* **One session, this connection's own.** A repeat `connect()` opens a NEW
|
|
252
|
-
* one, with a new `SAP_SESSIONID`, so an earlier session is not this
|
|
253
|
-
* connection's business any more: its logoff is already on the wire, or the
|
|
254
|
-
* system will time it out. Nothing here retries, counts, or keeps a list —
|
|
255
|
-
* how many connections to run and how carefully stays with the caller.
|
|
256
|
-
*
|
|
257
|
-
* **It ends the session, not this object's use of it.** The cookies are the
|
|
258
|
-
* only thing tying anyone to a session, so a second connection given the same
|
|
259
|
-
* cookies works in the same ABAP session and can use the locks taken in it —
|
|
260
|
-
* and this logoff closes that session for all of them at once. Whoever hands
|
|
261
|
-
* the cookies around owns that decision; this method cannot see the copies.
|
|
262
|
-
*
|
|
263
|
-
* Never throws. `disconnect()` must always settle, and a session we could not
|
|
264
|
-
* close is better than a teardown that does not finish; the local state is
|
|
265
|
-
* cleared either way.
|
|
266
|
-
*/
|
|
267
|
-
private releaseServerSession;
|
|
268
|
-
/**
|
|
269
|
-
* Waits up to `budgetMs` for a release already on its way, then detaches.
|
|
270
|
-
*
|
|
271
|
-
* Detaching, not cancelling: when the budget runs out this stops waiting and
|
|
272
|
-
* the request carries on to the server. Each caller of `disconnect()` gets
|
|
273
|
-
* its own, so one caller's patience is never charged to another's.
|
|
274
|
-
*/
|
|
275
|
-
private awaitReleaseWithin;
|
|
235
|
+
* **Nothing to wait for.** This TELLS the server the session is finished and
|
|
236
|
+
* does not act on the answer — whether and when the session is freed is the
|
|
237
|
+
* server's affair. Waiting for a reply nobody reads was also the one thing
|
|
238
|
+
* that could make a teardown unbounded, since the goodbye carries no request
|
|
239
|
+
* timeout by design.
|
|
240
|
+
*/
|
|
241
|
+
disconnect(): Promise<void>;
|
|
276
242
|
isConnected(): boolean;
|
|
277
243
|
/**
|
|
278
244
|
* Fingerprint of the SAP-side session, or null when none is known.
|
|
@@ -317,7 +283,6 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
317
283
|
* the session is ours, and nothing else. It cannot reach session state, so a
|
|
318
284
|
* strategy can neither mark this connection connected nor tear it down.
|
|
319
285
|
*/
|
|
320
|
-
private sessionTransport;
|
|
321
286
|
/**
|
|
322
287
|
* Ask the server to open a session before the establishing call needs one.
|
|
323
288
|
*
|
|
@@ -329,22 +294,13 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
329
294
|
* is what happens when nobody asks.
|
|
330
295
|
*/
|
|
331
296
|
/**
|
|
332
|
-
*
|
|
333
|
-
* discovered by asking.
|
|
297
|
+
* What a wire needs from this connection to get a session, or give one back.
|
|
334
298
|
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
* it always has. Cloud opens a session resource and takes it back by DELETE on
|
|
339
|
-
* the address the server published.
|
|
340
|
-
*
|
|
341
|
-
* Probing was tried and is wrong: `/sap/bc/adt/core/http/sessions` answers on
|
|
342
|
-
* on-prem too — measured on S/4HANA, which publishes both the session resource
|
|
343
|
-
* and the ICF logoff in the same document — so a probe does not tell the two
|
|
344
|
-
* systems apart. It only tells whether an endpoint exists, and both have it.
|
|
299
|
+
* A snapshot in the sense that matters: a close is dispatched without being
|
|
300
|
+
* awaited, so nothing here may read the connection again once the teardown
|
|
301
|
+
* has cleared it.
|
|
345
302
|
*/
|
|
346
|
-
protected
|
|
347
|
-
private openServerSession;
|
|
303
|
+
protected sessionContext(): IAdtSessionContext;
|
|
348
304
|
private establishAndCommit;
|
|
349
305
|
/** The teardown epoch, for a recovery to capture before it starts. */
|
|
350
306
|
protected get teardownEpoch(): number;
|
|
@@ -387,7 +343,7 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
387
343
|
* turns "your lock is dead" back into "your request 403'd", which is the very
|
|
388
344
|
* information the caller needs and the only one it cannot recover itself.
|
|
389
345
|
*/
|
|
390
|
-
|
|
346
|
+
protected isSessionVerdict(error: unknown): boolean;
|
|
391
347
|
/**
|
|
392
348
|
* Folds a response into the session state AND acts on what it means, in one
|
|
393
349
|
* step.
|
|
@@ -397,23 +353,7 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
397
353
|
* later check reads `unchanged`. That is one call site forgetting, and it
|
|
398
354
|
* happened — on the error path and on every retry response.
|
|
399
355
|
*/
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Take the application server's name from a response, if it named one.
|
|
403
|
-
*
|
|
404
|
-
* Only ever set from the server's own answer — never guessed, and never kept
|
|
405
|
-
* across a teardown.
|
|
406
|
-
*/
|
|
407
|
-
private rememberAppServer;
|
|
408
|
-
/**
|
|
409
|
-
* Headers that keep this connection on the server its session lives on.
|
|
410
|
-
*
|
|
411
|
-
* `sap-adt-saplb: fetch` asks the server to name itself — it answers on every
|
|
412
|
-
* request, so the binding survives a restart that moves us. `saplb` is that
|
|
413
|
-
* name sent back. `REDISPATCH_ON_SHUTDOWN` is what Eclipse asks for: if the
|
|
414
|
-
* server is going down, send us elsewhere rather than fail.
|
|
415
|
-
*/
|
|
416
|
-
private affinityHeaders;
|
|
356
|
+
protected observeResponse(headers?: Record<string, unknown>, generation?: number): void;
|
|
417
357
|
/**
|
|
418
358
|
* Acts on what a response said about the session identity.
|
|
419
359
|
*
|
|
@@ -458,19 +398,17 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
458
398
|
private performRequest;
|
|
459
399
|
protected abstract buildAuthorizationHeader(): string;
|
|
460
400
|
/**
|
|
461
|
-
*
|
|
462
|
-
*
|
|
401
|
+
* Ask the wire to establish itself, and hand back what it earned.
|
|
402
|
+
*
|
|
403
|
+
* The exchange itself is the transport's — it is the HTTP wire that has a
|
|
404
|
+
* token to earn and an endpoint to earn it from, and the RFC wire that has
|
|
405
|
+
* neither. What stays here is the part that is about the SESSION rather than
|
|
406
|
+
* the wire: fencing the answer by generation, and letting the identity policy
|
|
407
|
+
* read what it means.
|
|
463
408
|
*/
|
|
464
|
-
protected fetchCsrfToken(
|
|
409
|
+
protected fetchCsrfToken(_url: string, retryCount?: number, retryDelay?: number,
|
|
465
410
|
/** Fences the response effects; omitted during connect(), which has no lease. */
|
|
466
411
|
generation?: number): Promise<string>;
|
|
467
|
-
/**
|
|
468
|
-
* Fetch CSRF token from a specific endpoint with retries
|
|
469
|
-
*/
|
|
470
|
-
private fetchCsrfTokenFromEndpoint;
|
|
471
|
-
/**
|
|
472
|
-
* Get CSRF token (protected for use by subclasses)
|
|
473
|
-
*/
|
|
474
412
|
protected getCsrfToken(): string | null;
|
|
475
413
|
/**
|
|
476
414
|
* Set CSRF token (protected for use by subclasses)
|
|
@@ -480,21 +418,30 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
|
|
|
480
418
|
* Get cookies (protected for use by subclasses)
|
|
481
419
|
*/
|
|
482
420
|
protected getCookies(): string | null;
|
|
483
|
-
protected setInitialCookies(cookies: string): void;
|
|
484
421
|
/**
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
422
|
+
* Seed the wire with cookies the caller already holds — a SAML session, which
|
|
423
|
+
* IS the credential rather than something a logon call earns.
|
|
424
|
+
*
|
|
425
|
+
* Handed over as a response would deliver them, because the wire owns the jar
|
|
426
|
+
* and how it stores them is its business, not this class's.
|
|
489
427
|
*/
|
|
490
|
-
|
|
428
|
+
protected setInitialCookies(cookies: string): void;
|
|
491
429
|
/**
|
|
492
|
-
* Subclasses override to inject extra https.Agent options (e.g. mTLS
|
|
493
|
-
* The returned options are merged with the base options
|
|
430
|
+
* Subclasses override to inject extra https.Agent options (e.g. mTLS
|
|
431
|
+
* cert/key/pfx). The returned options are merged with the base options
|
|
432
|
+
* (rejectUnauthorized).
|
|
494
433
|
*/
|
|
495
434
|
protected getHttpsAgentOptions(): import('node:https').AgentOptions;
|
|
496
|
-
|
|
497
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Make sure the wire is ready to carry a mutation.
|
|
437
|
+
*
|
|
438
|
+
* What ready MEANS is the wire's: HTTP holds a CSRF token and returns at once
|
|
439
|
+
* when it already has one; an RFC conversation has nothing to earn and does
|
|
440
|
+
* nothing. Demanding a token back was an HTTP assumption, and over RFC it
|
|
441
|
+
* raised `No CSRF token in response headers` before every write — swallowed
|
|
442
|
+
* by the caller, but logged as an error and repeated on the next one.
|
|
443
|
+
*/
|
|
444
|
+
private ensureWireReady;
|
|
498
445
|
/**
|
|
499
446
|
* Clear SAP-side session state when SAP rejects the cached CSRF token + session
|
|
500
447
|
* cookies (HTTP 401 on a mutation while a cached token exists). This forces the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAE5B,MAAM,0BAA0B,CAAC;AAMlC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"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"}
|