@mcp-abap-adt/connection 3.0.0 → 5.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +337 -1
  2. package/README.md +74 -31
  3. package/dist/auth/IAuthProvider.d.ts +84 -0
  4. package/dist/auth/IAuthProvider.d.ts.map +1 -0
  5. package/dist/auth/IAuthProvider.js +21 -0
  6. package/dist/auth/providers.d.ts +95 -0
  7. package/dist/auth/providers.d.ts.map +1 -0
  8. package/dist/auth/providers.js +140 -0
  9. package/dist/connection/AbstractAbapConnection.d.ts +222 -17
  10. package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
  11. package/dist/connection/AbstractAbapConnection.js +555 -31
  12. package/dist/connection/AdtCloudConnector.d.ts +29 -0
  13. package/dist/connection/AdtCloudConnector.d.ts.map +1 -0
  14. package/dist/connection/AdtCloudConnector.js +31 -0
  15. package/dist/connection/AdtOnPremConnector.d.ts +30 -0
  16. package/dist/connection/AdtOnPremConnector.d.ts.map +1 -0
  17. package/dist/connection/AdtOnPremConnector.js +32 -0
  18. package/dist/connection/BaseAbapConnection.d.ts +7 -1
  19. package/dist/connection/BaseAbapConnection.d.ts.map +1 -1
  20. package/dist/connection/BaseAbapConnection.js +8 -2
  21. package/dist/connection/CertificateAbapConnection.d.ts +11 -1
  22. package/dist/connection/CertificateAbapConnection.d.ts.map +1 -1
  23. package/dist/connection/CertificateAbapConnection.js +14 -2
  24. package/dist/connection/CredentialAbapConnection.d.ts +88 -0
  25. package/dist/connection/CredentialAbapConnection.d.ts.map +1 -0
  26. package/dist/connection/CredentialAbapConnection.js +195 -0
  27. package/dist/connection/JwtAbapConnection.d.ts +102 -9
  28. package/dist/connection/JwtAbapConnection.d.ts.map +1 -1
  29. package/dist/connection/JwtAbapConnection.js +266 -82
  30. package/dist/connection/KerberosAbapConnection.d.ts +9 -1
  31. package/dist/connection/KerberosAbapConnection.d.ts.map +1 -1
  32. package/dist/connection/KerberosAbapConnection.js +9 -1
  33. package/dist/connection/RfcAbapConnection.d.ts +0 -5
  34. package/dist/connection/RfcAbapConnection.d.ts.map +1 -1
  35. package/dist/connection/RfcAbapConnection.js +0 -7
  36. package/dist/connection/SamlAbapConnection.d.ts +7 -1
  37. package/dist/connection/SamlAbapConnection.d.ts.map +1 -1
  38. package/dist/connection/SamlAbapConnection.js +8 -2
  39. package/dist/connection/connectionFactory.d.ts +16 -0
  40. package/dist/connection/connectionFactory.d.ts.map +1 -1
  41. package/dist/connection/connectionFactory.js +52 -0
  42. package/dist/index.d.ts +4 -0
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +10 -1
  45. package/dist/session/CloudSecuritySessionStrategy.d.ts +32 -0
  46. package/dist/session/CloudSecuritySessionStrategy.d.ts.map +1 -0
  47. package/dist/session/CloudSecuritySessionStrategy.js +135 -0
  48. package/dist/session/IcfSessionStrategy.d.ts +27 -0
  49. package/dist/session/IcfSessionStrategy.d.ts.map +1 -0
  50. package/dist/session/IcfSessionStrategy.js +62 -0
  51. package/dist/session/SessionLifecycle.d.ts +17 -2
  52. package/dist/session/SessionLifecycle.d.ts.map +1 -1
  53. package/dist/session/SessionLifecycle.js +17 -2
  54. package/dist/session/SessionStrategy.d.ts +86 -0
  55. package/dist/session/SessionStrategy.d.ts.map +1 -0
  56. package/dist/session/SessionStrategy.js +33 -0
  57. package/dist/utils/cookies.d.ts +12 -0
  58. package/dist/utils/cookies.d.ts.map +1 -0
  59. package/dist/utils/cookies.js +24 -0
  60. package/dist/utils/timeouts.d.ts +16 -0
  61. package/dist/utils/timeouts.d.ts.map +1 -1
  62. package/dist/utils/timeouts.js +19 -0
  63. package/docs/INDEX.md +5 -0
  64. package/docs/INSTALLATION.md +6 -2
  65. package/docs/MIGRATION-2.0.md +1 -1
  66. package/docs/MIGRATION-4.0.md +95 -0
  67. package/docs/MIGRATION-5.0.md +116 -0
  68. package/docs/STATEFUL_SESSION_GUIDE.md +83 -11
  69. package/docs/USAGE.md +115 -24
  70. package/docs/superpowers/specs/2026-08-21-platform-connectors.md +108 -0
  71. package/examples/README.md +2 -1
  72. package/examples/jwt-with-token-refresh.js +11 -3
  73. package/package.json +1 -1
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The credentials a connector can be handed.
3
+ *
4
+ * Each is the authentication half of one of the connection classes that used to
5
+ * carry both halves, lifted out unchanged — the header a `BasicAuthProvider`
6
+ * builds is byte for byte what `BaseAbapConnection` built, and the TLS options a
7
+ * `CertificateAuthProvider` returns are what `CertificateAbapConnection`
8
+ * returned. Nothing about how a credential works changed; only who owns it.
9
+ */
10
+ import type { AgentOptions } from 'node:https';
11
+ import type { ICertificateMaterialLoader, ISapConfig, ITokenRefresher } from '@mcp-abap-adt/interfaces';
12
+ import type { IAuthProvider } from './IAuthProvider.js';
13
+ /** Username and password, as `Basic base64(user:pass)`. */
14
+ export declare class BasicAuthProvider implements IAuthProvider {
15
+ private readonly username;
16
+ private readonly password;
17
+ readonly kind = "basic";
18
+ constructor(username: string, password: string);
19
+ authorizationHeader(): Promise<string>;
20
+ }
21
+ /**
22
+ * A bearer token, kept current by whoever issued it.
23
+ *
24
+ * Takes an `ITokenRefresher` — the contract `@mcp-abap-adt/auth-broker` already
25
+ * produces and `@mcp-abap-adt/auth-providers` already implements twelve ways.
26
+ * This package depends on neither: it speaks the contract, and the consumer
27
+ * brings the implementation, exactly as it does for `IAbapConnection`.
28
+ *
29
+ * **This carries no session recovery.** What happens to a session when the
30
+ * credential behind it is renewed mid-flight is the connection's business and
31
+ * stays there; see `JwtAbapConnection`, which still owns that machinery.
32
+ */
33
+ export declare class TokenAuthProvider implements IAuthProvider {
34
+ private readonly source;
35
+ readonly kind = "token";
36
+ /**
37
+ * A token, or something that can produce one.
38
+ *
39
+ * A bare string is a token with no renewal behind it — honest, and fine for a
40
+ * short task. An `ITokenRefresher` or a function is a provider that checks
41
+ * expiry and renews on its own, which is where this belongs in a long-lived
42
+ * process.
43
+ */
44
+ constructor(source: string | ITokenRefresher | (() => Promise<string>));
45
+ /**
46
+ * Asked every time, and nothing kept.
47
+ *
48
+ * The provider behind this already caches the token, knows when it expires,
49
+ * and renews before handing one back. A second cache here would serve the
50
+ * stale one and hide exactly the renewal the provider exists to do — which
51
+ * is what the first version of this class did.
52
+ */
53
+ authorizationHeader(): Promise<string>;
54
+ /**
55
+ * The token was refused, so force a new one.
56
+ *
57
+ * `getToken()` is documented to return the cached token while it believes it
58
+ * is still valid — which is exactly the situation after a 401 on a token the
59
+ * provider has not yet noticed is dead. `refreshToken()` is the contract's
60
+ * answer for that, and this is the only place it is called.
61
+ */
62
+ renew(): Promise<void>;
63
+ }
64
+ /**
65
+ * A SAML session, already negotiated, presented as cookies.
66
+ *
67
+ * No `Authorization` header at all — the cookies are the credential, and they
68
+ * are added by the connection alongside its own.
69
+ */
70
+ export declare class SamlAuthProvider implements IAuthProvider {
71
+ private readonly sessionCookies;
72
+ readonly kind = "saml";
73
+ constructor(sessionCookies: string);
74
+ authorizationHeader(): Promise<string>;
75
+ /** The cookies to present. */
76
+ cookies(): string;
77
+ }
78
+ /**
79
+ * Client certificate: the credential lives in the TLS handshake.
80
+ *
81
+ * `prepare()` is where the material is read, which is why it exists at all —
82
+ * building an agent before it is loaded is what used to reject a connect
83
+ * before a single request went out.
84
+ */
85
+ export declare class CertificateAuthProvider implements IAuthProvider {
86
+ private readonly loader;
87
+ private readonly config;
88
+ readonly kind = "certificate";
89
+ private material;
90
+ constructor(loader: ICertificateMaterialLoader, config: ISapConfig);
91
+ prepare(): Promise<void>;
92
+ authorizationHeader(): Promise<string>;
93
+ httpsAgentOptions(): AgentOptions;
94
+ }
95
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/auth/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAEV,0BAA0B,EAC1B,UAAU,EACV,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,2DAA2D;AAC3D,qBAAa,iBAAkB,YAAW,aAAa;IAInD,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,QAAQ,CAAC,IAAI,WAAW;gBAGL,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM;IAG7B,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;CAG7C;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IAYnD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAXzB,QAAQ,CAAC,IAAI,WAAW;IAExB;;;;;;;OAOG;gBAEgB,MAAM,EAAE,MAAM,GAAG,eAAe,GAAG,CAAC,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAG7E;;;;;;;OAOG;IACG,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IAU5C;;;;;;;OAOG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B;AAED;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,aAAa;IAGxC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAF3C,QAAQ,CAAC,IAAI,UAAU;gBAEM,cAAc,EAAE,MAAM;IAE7C,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IAI5C,8BAA8B;IAC9B,OAAO,IAAI,MAAM;CAGlB;AAED;;;;;;GAMG;AACH,qBAAa,uBAAwB,YAAW,aAAa;IAKzD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,OAAO,CAAC,QAAQ,CAAqC;gBAGlC,MAAM,EAAE,0BAA0B,EAClC,MAAM,EAAE,UAAU;IAG/B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAMxB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC;IAI5C,iBAAiB,IAAI,YAAY;CASlC"}
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /**
3
+ * The credentials a connector can be handed.
4
+ *
5
+ * Each is the authentication half of one of the connection classes that used to
6
+ * carry both halves, lifted out unchanged — the header a `BasicAuthProvider`
7
+ * builds is byte for byte what `BaseAbapConnection` built, and the TLS options a
8
+ * `CertificateAuthProvider` returns are what `CertificateAbapConnection`
9
+ * returned. Nothing about how a credential works changed; only who owns it.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.CertificateAuthProvider = exports.SamlAuthProvider = exports.TokenAuthProvider = exports.BasicAuthProvider = void 0;
13
+ /** Username and password, as `Basic base64(user:pass)`. */
14
+ class BasicAuthProvider {
15
+ username;
16
+ password;
17
+ kind = 'basic';
18
+ constructor(username, password) {
19
+ this.username = username;
20
+ this.password = password;
21
+ }
22
+ async authorizationHeader() {
23
+ return `Basic ${Buffer.from(`${this.username ?? ''}:${this.password ?? ''}`).toString('base64')}`;
24
+ }
25
+ }
26
+ exports.BasicAuthProvider = BasicAuthProvider;
27
+ /**
28
+ * A bearer token, kept current by whoever issued it.
29
+ *
30
+ * Takes an `ITokenRefresher` — the contract `@mcp-abap-adt/auth-broker` already
31
+ * produces and `@mcp-abap-adt/auth-providers` already implements twelve ways.
32
+ * This package depends on neither: it speaks the contract, and the consumer
33
+ * brings the implementation, exactly as it does for `IAbapConnection`.
34
+ *
35
+ * **This carries no session recovery.** What happens to a session when the
36
+ * credential behind it is renewed mid-flight is the connection's business and
37
+ * stays there; see `JwtAbapConnection`, which still owns that machinery.
38
+ */
39
+ class TokenAuthProvider {
40
+ source;
41
+ kind = 'token';
42
+ /**
43
+ * A token, or something that can produce one.
44
+ *
45
+ * A bare string is a token with no renewal behind it — honest, and fine for a
46
+ * short task. An `ITokenRefresher` or a function is a provider that checks
47
+ * expiry and renews on its own, which is where this belongs in a long-lived
48
+ * process.
49
+ */
50
+ constructor(source) {
51
+ this.source = source;
52
+ }
53
+ /**
54
+ * Asked every time, and nothing kept.
55
+ *
56
+ * The provider behind this already caches the token, knows when it expires,
57
+ * and renews before handing one back. A second cache here would serve the
58
+ * stale one and hide exactly the renewal the provider exists to do — which
59
+ * is what the first version of this class did.
60
+ */
61
+ async authorizationHeader() {
62
+ const token = typeof this.source === 'string'
63
+ ? this.source
64
+ : typeof this.source === 'function'
65
+ ? await this.source()
66
+ : await this.source.getToken();
67
+ return token ? `Bearer ${token}` : '';
68
+ }
69
+ /**
70
+ * The token was refused, so force a new one.
71
+ *
72
+ * `getToken()` is documented to return the cached token while it believes it
73
+ * is still valid — which is exactly the situation after a 401 on a token the
74
+ * provider has not yet noticed is dead. `refreshToken()` is the contract's
75
+ * answer for that, and this is the only place it is called.
76
+ */
77
+ async renew() {
78
+ if (typeof this.source === 'string' || typeof this.source === 'function') {
79
+ // Nothing to force: a fixed token has no source, and a function is asked
80
+ // afresh every time anyway.
81
+ return;
82
+ }
83
+ await this.source.refreshToken();
84
+ }
85
+ }
86
+ exports.TokenAuthProvider = TokenAuthProvider;
87
+ /**
88
+ * A SAML session, already negotiated, presented as cookies.
89
+ *
90
+ * No `Authorization` header at all — the cookies are the credential, and they
91
+ * are added by the connection alongside its own.
92
+ */
93
+ class SamlAuthProvider {
94
+ sessionCookies;
95
+ kind = 'saml';
96
+ constructor(sessionCookies) {
97
+ this.sessionCookies = sessionCookies;
98
+ }
99
+ async authorizationHeader() {
100
+ return '';
101
+ }
102
+ /** The cookies to present. */
103
+ cookies() {
104
+ return this.sessionCookies;
105
+ }
106
+ }
107
+ exports.SamlAuthProvider = SamlAuthProvider;
108
+ /**
109
+ * Client certificate: the credential lives in the TLS handshake.
110
+ *
111
+ * `prepare()` is where the material is read, which is why it exists at all —
112
+ * building an agent before it is loaded is what used to reject a connect
113
+ * before a single request went out.
114
+ */
115
+ class CertificateAuthProvider {
116
+ loader;
117
+ config;
118
+ kind = 'certificate';
119
+ material = null;
120
+ constructor(loader, config) {
121
+ this.loader = loader;
122
+ this.config = config;
123
+ }
124
+ async prepare() {
125
+ if (!this.material) {
126
+ this.material = await this.loader.load(this.config);
127
+ }
128
+ }
129
+ async authorizationHeader() {
130
+ return '';
131
+ }
132
+ httpsAgentOptions() {
133
+ if (!this.material) {
134
+ throw new Error('CertificateAuthProvider: certificate material not loaded. connect() prepares it; a request before that has nothing to present.');
135
+ }
136
+ const { cert, key, pfx, passphrase } = this.material;
137
+ return { cert, key, pfx, passphrase };
138
+ }
139
+ }
140
+ exports.CertificateAuthProvider = CertificateAuthProvider;
@@ -2,12 +2,29 @@ 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';
5
6
  import type { AbapConnection, AbapRequestOptions } from './AbapConnection.js';
6
7
  /**
7
8
  * Declares the capabilities explicitly rather than satisfying them by accident.
8
9
  * `AbapConnection` is the base contract every transport honours; these two are
9
10
  * the HTTP session's own, and naming them means a signature that drifts from
10
11
  * the published contract fails to compile here instead of at the consumer.
12
+ *
13
+ * **This gives the consumer instruments; it does not decide for it.** `connect()`
14
+ * opens one session and `disconnect()` closes it. How many connections to hold,
15
+ * how long to hold them and when to let go stays with the caller — there are no
16
+ * thresholds here, no pooling and no eviction, because none of that is knowable
17
+ * from inside a single connection. A session this one did not open is not its
18
+ * business: the session limit is per user and the pool is shared, so a SAP GUI
19
+ * logon of the same user sits in the same list.
20
+ *
21
+ * **Nothing the server decides is treated as something to count on.** Whether it
22
+ * issues a session cookie, whether it still holds a session it issued, how many
23
+ * it will tolerate, how fast it answers a logoff — all of that is its own and
24
+ * may differ by system and release. So each is observed and reported, never
25
+ * relied upon: the logoff is best effort under a bound the caller sets, a
26
+ * missing session cookie is a warning rather than a rule, and no code here
27
+ * counts sessions or predicts the next answer from the last one.
11
28
  */
12
29
  declare abstract class AbstractAbapConnection implements AbapConnection, ISessionLifecycleAware {
13
30
  private readonly config;
@@ -38,6 +55,53 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
38
55
  private inCriticalSection;
39
56
  /** Reference count for nested beginCriticalSection()/endCriticalSection() pairs. */
40
57
  private criticalSectionDepth;
58
+ /** The default release deadline, validated once at construction. */
59
+ private readonly releaseDeadlineMs;
60
+ /**
61
+ * The logoff for the session this connection last held, while it is on its
62
+ * way. One, because a connection holds one session: `connect()` opens it and
63
+ * `disconnect()` closes it, and how many connections to run is the caller's
64
+ * business, not something to be tracked here.
65
+ *
66
+ * Carries the session id, not just the promise, so a release still in flight
67
+ * for an EARLIER session is recognised as not being this one's — reusing it
68
+ * was what left the second session of a reconnect never released at all.
69
+ *
70
+ * The id is the `SAP_SESSIONID` value — the ABAP session, the one locks are
71
+ * bound to — never the cookie header: that header also carries `sap-XSRF_*`,
72
+ * which rotates within one and the same session, so comparing headers made a
73
+ * session stop recognising itself after a token refresh.
74
+ */
75
+ /**
76
+ * How this server opens and gives back a session, decided by asking it rather
77
+ * than by guessing which system it is. Set at establishment; until then the
78
+ * on-prem mechanism, which is the one that needs no resource.
79
+ */
80
+ /**
81
+ * The application server this session lives on, as the server named it.
82
+ *
83
+ * A session belongs to ONE application server. On a multi-node system a
84
+ * request that lands on another gets another session — and a lock held on the
85
+ * first is then dead through nobody's fault and no inactivity. Eclipse pins
86
+ * itself with these headers; without them every request is a fresh throw of
87
+ * the dice.
88
+ *
89
+ * Learned from `sap-adt-saplb` on a response, sent back as `saplb`. Cleared
90
+ * with the rest of the session state: it names a server for a session that no
91
+ * longer exists.
92
+ */
93
+ private appServer;
94
+ /**
95
+ * Whether the preflight opened a session of its own.
96
+ *
97
+ * Distinct from "there are cookies": a failed establishment often leaves a
98
+ * cookie from the 401 that rejected it, and that is debris, not a session.
99
+ * Only a preflight answered with a session address opened one, and only that
100
+ * is worth saying goodbye to when establishment then fails.
101
+ */
102
+ private preflightOpenedSession;
103
+ private sessionStrategy;
104
+ private pendingRelease;
41
105
  protected constructor(config: SapConfig, logger: ILogger | null, sessionId?: string, options?: {
42
106
  skipSessionType?: boolean;
43
107
  });
@@ -97,6 +161,27 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
97
161
  * preparation and fetch the CSRF token; connect() owns everything around it.
98
162
  */
99
163
  protected abstract establishSession(): Promise<void>;
164
+ /**
165
+ * Gets the credential ready before anything is sent.
166
+ *
167
+ * A no-op for the auth types whose credential is already in hand — basic
168
+ * builds a header from the configuration, JWT carries a token it was given.
169
+ * It exists for the ones that have to fetch or load theirs, because the
170
+ * preflight now runs BEFORE `establishSession()` and needs a credential to
171
+ * go out with: a certificate connection reads its material there, and
172
+ * without this the preflight throws `certificate material not loaded` while
173
+ * assembling the transport — before a single request is made, on every
174
+ * system, cloud or on-prem.
175
+ *
176
+ * Must be idempotent: `establishSession()` may prepare the same credential
177
+ * again, and does.
178
+ *
179
+ * Kerberos deliberately does NOT implement it. Minting the SPNEGO token this
180
+ * early changes when the exchange happens, and that connection is not
181
+ * production-tested — its preflight fails the way it already did, is caught
182
+ * inside the strategy, and the connection falls back to ICF as before.
183
+ */
184
+ protected prepareCredential(): Promise<void>;
100
185
  /**
101
186
  * Establishes the session, once, under the lifecycle.
102
187
  *
@@ -110,18 +195,84 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
110
195
  /**
111
196
  * Tears the session down. Never throws, and always settles.
112
197
  *
113
- * It waits for NOTHING. Deciding when to disconnect is the caller's, and so is
114
- * preparing for it — finishing chains, releasing locks. Waiting here on a
115
- * request whose caller chose no timeout is what made a teardown unbounded, and
116
- * an unbounded teardown blocks every later transition on the serialized tail.
198
+ * Tells the server the session is no longer needed, then clears the local
199
+ * state. **When the server actually reclaims it is the server's business** —
200
+ * possibly not until the next connect asks it for one — and nothing here
201
+ * waits for that or depends on it. What matters is that the session stops
202
+ * being counted against the user, which dropping the cookie alone does not
203
+ * achieve: the server keeps it until its own timeout, so a process that
204
+ * connects repeatedly leaves one behind every time. Measured on S/4HANA on-prem, 25 connects in a row: with the logoff,
205
+ * 24-25 of them were given a session; without it, 2. A connection that gets
206
+ * no session still answers `200` to a LOCK and hands back a handle the next
207
+ * request cannot use, so the leak surfaces as a half-written object rather
208
+ * than as anything about sessions.
209
+ *
210
+ * **The logoff is the only thing waited for**, under `deadlineMs`, and
211
+ * deciding that bound is the caller's — see the parameter. Nothing else is
212
+ * waited for: finishing chains and releasing locks stay the caller's to do
213
+ * before calling, and waiting here on a request whose caller chose no timeout
214
+ * is what made a teardown unbounded, which blocks every later transition on
215
+ * the serialized tail.
216
+ *
217
+ * **Requests already in flight are not waited for, and the logoff ends the
218
+ * session they are running on** — so they will start failing against a
219
+ * session that no longer exists. That is the caller having asked to
220
+ * disconnect, not a race, and it is a change from the version that only
221
+ * dropped the cookie. Generation fencing (see `SessionLifecycle.isCurrent`)
222
+ * keeps their results from reaching this connection either way.
223
+ *
224
+ * @param options.deadlineMs How long to spend telling the server, measured
225
+ * from this call and including time spent queued behind another transition.
226
+ * **Defaults to `SAP_RELEASE_DEADLINE_MS`, which is `0` — do not wait.**
227
+ * Waiting is for steps whose successor needs the server to have caught up;
228
+ * a teardown has none. The logoff is still sent at `0`, because saying so
229
+ * is not conditional on caring when it lands; its outcome is logged when it
230
+ * arrives rather than awaited. Pass a positive value to bound a wait you
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.
117
250
  *
118
- * Requests already in flight run to completion untouched. Generation fencing
119
- * (see `SessionLifecycle.isCurrent`) keeps their results from reaching this
120
- * connection afterwards.
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.
121
256
  *
122
- * Sends no ADT session-close — see the design's D2.
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.
123
274
  */
124
- disconnect(): Promise<void>;
275
+ private awaitReleaseWithin;
125
276
  isConnected(): boolean;
126
277
  /**
127
278
  * Fingerprint of the SAP-side session, or null when none is known.
@@ -131,14 +282,9 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
131
282
  * session cookie. Use {@link isConnected} for connection state.
132
283
  *
133
284
  * It follows that null → non-null is not a replacement but an identity being
134
- * learned; only a CHANGED value means the session was replaced.
285
+ * learned; only a CHANGED value means the session we had is gone.
135
286
  */
136
287
  getSessionIdentity(): string | null;
137
- /**
138
- * Discards the session at a caller's request: cancels queued recoveries and
139
- * queues the cleanup rather than tearing down under a live request.
140
- */
141
- reset(): void;
142
288
  /**
143
289
  * Re-establishes the session for a request that is recovering from a
144
290
  * credential renewal, then lets that request retry.
@@ -166,9 +312,50 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
166
312
  * Shared by connect() and recoverSession() rather than written twice —
167
313
  * the two drifted apart once already, and a third caller would drift again.
168
314
  */
315
+ /**
316
+ * What a session strategy is given: enough to make one request and to prove
317
+ * the session is ours, and nothing else. It cannot reach session state, so a
318
+ * strategy can neither mark this connection connected nor tear it down.
319
+ */
320
+ private sessionTransport;
321
+ /**
322
+ * Ask the server to open a session before the establishing call needs one.
323
+ *
324
+ * The strategy is chosen by what the server publishes, not by which system we
325
+ * believe it to be: ABAP Cloud offers a session resource and issues
326
+ * `SAP_SESSIONID` to whoever asks for one — with `x-sap-security-session:
327
+ * create` — while on-prem has no such resource and its session arrives with
328
+ * the establishing request. Believing cloud simply "issues no SAP_SESSIONID"
329
+ * is what happens when nobody asks.
330
+ */
331
+ /**
332
+ * Which session management this system uses — decided by the connection, not
333
+ * discovered by asking.
334
+ *
335
+ * On-prem and cloud do not manage sessions the same way, and the two
336
+ * implementations exist for that reason. On-prem the session arrives with the
337
+ * establishing request and the platform's ICF logoff gives it back, exactly as
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.
345
+ */
346
+ protected createSessionStrategy(): SessionStrategy;
347
+ private openServerSession;
169
348
  private establishAndCommit;
170
349
  /** The teardown epoch, for a recovery to capture before it starts. */
171
350
  protected get teardownEpoch(): number;
351
+ /**
352
+ * Which session the connection is on now.
353
+ *
354
+ * Moves whenever the session does. A response that comes back carrying an
355
+ * older one belongs to a session that has already been replaced, and must not
356
+ * be acted on as if it said something about the current one.
357
+ */
358
+ protected get sessionGeneration(): number;
172
359
  /**
173
360
  * Raises a session-lost teardown from inside request handling.
174
361
  *
@@ -211,6 +398,22 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
211
398
  * happened — on the error path and on every retry response.
212
399
  */
213
400
  private observeResponse;
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;
214
417
  /**
215
418
  * Acts on what a response said about the session identity.
216
419
  *
@@ -231,7 +434,7 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
231
434
  /**
232
435
  * Whether the server is telling us the session it was given no longer exists.
233
436
  *
234
- * The E19 shape was HTTP 400 with "Session not found", answered in ~60 ms
437
+ * One on-prem system answered HTTP 400 with "Session not found", answered in ~60 ms
235
438
  * with the cookie present — which is why identity comparison cannot see this:
236
439
  * the cookie, and therefore the fingerprint, is completely unchanged. The
237
440
  * exact match is landscape-specific and is one of the live probes this design
@@ -297,7 +500,9 @@ declare abstract class AbstractAbapConnection implements AbapConnection, ISessio
297
500
  * cookies (HTTP 401 on a mutation while a cached token exists). This forces the
298
501
  * next request path to fetch a fresh token and a fresh SAP_SESSIONID cookie.
299
502
  *
300
- * Distinct from reset(): this leaves the axios instance and interceptors in place.
503
+ * Distinct from disconnect(): this leaves the axios instance and interceptors
504
+ * in place, and tells the server nothing — it is a request-level repair, not a
505
+ * teardown.
301
506
  */
302
507
  private invalidateSession;
303
508
  private shouldRetryCsrf;
@@ -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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"}