@mcp-abap-adt/connection 4.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.
Files changed (87) hide show
  1. package/CHANGELOG.md +407 -1
  2. package/README.md +239 -53
  3. package/dist/auth/providers.d.ts +124 -0
  4. package/dist/auth/providers.d.ts.map +1 -0
  5. package/dist/auth/providers.js +183 -0
  6. package/dist/connection/AbstractAbapConnection.d.ts +209 -57
  7. package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
  8. package/dist/connection/AbstractAbapConnection.js +459 -457
  9. package/dist/connection/AdtCloudConnector.d.ts +34 -0
  10. package/dist/connection/AdtCloudConnector.d.ts.map +1 -0
  11. package/dist/connection/AdtCloudConnector.js +27 -0
  12. package/dist/connection/AdtOnPremConnector.d.ts +43 -0
  13. package/dist/connection/AdtOnPremConnector.d.ts.map +1 -0
  14. package/dist/connection/AdtOnPremConnector.js +28 -0
  15. package/dist/connection/CloudHttpTransport.d.ts +37 -0
  16. package/dist/connection/CloudHttpTransport.d.ts.map +1 -0
  17. package/dist/connection/CloudHttpTransport.js +145 -0
  18. package/dist/connection/CredentialAbapConnection.d.ts +55 -0
  19. package/dist/connection/CredentialAbapConnection.d.ts.map +1 -0
  20. package/dist/connection/CredentialAbapConnection.js +128 -0
  21. package/dist/connection/HttpTransport.d.ts +178 -0
  22. package/dist/connection/HttpTransport.d.ts.map +1 -0
  23. package/dist/connection/HttpTransport.js +402 -0
  24. package/dist/connection/IAdtTransport.d.ts +232 -0
  25. package/dist/connection/IAdtTransport.d.ts.map +1 -0
  26. package/dist/connection/IAdtTransport.js +28 -0
  27. package/dist/connection/LegacyOnPremHttpTransport.d.ts +40 -0
  28. package/dist/connection/LegacyOnPremHttpTransport.d.ts.map +1 -0
  29. package/dist/connection/LegacyOnPremHttpTransport.js +57 -0
  30. package/dist/connection/OnPremHttpTransport.d.ts +45 -0
  31. package/dist/connection/OnPremHttpTransport.d.ts.map +1 -0
  32. package/dist/connection/OnPremHttpTransport.js +91 -0
  33. package/dist/connection/RfcTransport.d.ts +89 -0
  34. package/dist/connection/RfcTransport.d.ts.map +1 -0
  35. package/dist/connection/RfcTransport.js +256 -0
  36. package/dist/connection/rfcConversation.d.ts +44 -0
  37. package/dist/connection/rfcConversation.d.ts.map +1 -0
  38. package/dist/connection/rfcConversation.js +71 -0
  39. package/dist/index.d.ts +10 -7
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +30 -18
  42. package/dist/session/SessionLifecycle.d.ts +17 -2
  43. package/dist/session/SessionLifecycle.d.ts.map +1 -1
  44. package/dist/session/SessionLifecycle.js +17 -2
  45. package/dist/utils/cookies.d.ts +12 -0
  46. package/dist/utils/cookies.d.ts.map +1 -0
  47. package/dist/utils/cookies.js +24 -0
  48. package/dist/utils/timeouts.d.ts +6 -3
  49. package/dist/utils/timeouts.d.ts.map +1 -1
  50. package/dist/utils/timeouts.js +6 -3
  51. package/docs/INDEX.md +5 -2
  52. package/docs/INSTALLATION.md +28 -6
  53. package/docs/JWT_AUTH_TOOLS.md +20 -4
  54. package/docs/MIGRATION-2.0.md +1 -1
  55. package/docs/MIGRATION-5.0.md +116 -0
  56. package/docs/MIGRATION-6.0.md +359 -0
  57. package/docs/SCOPE.md +1 -1
  58. package/docs/STATEFUL_SESSION_GUIDE.md +155 -20
  59. package/docs/USAGE.md +322 -111
  60. package/examples/basic-connection.js +15 -3
  61. package/examples/jwt-with-token-refresh.js +15 -7
  62. package/examples/saml-connection.js +15 -2
  63. package/package.json +12 -10
  64. package/dist/__tests__/helpers/session.d.ts +0 -15
  65. package/dist/__tests__/helpers/session.d.ts.map +0 -1
  66. package/dist/__tests__/helpers/session.js +0 -19
  67. package/dist/connection/BaseAbapConnection.d.ts +0 -23
  68. package/dist/connection/BaseAbapConnection.d.ts.map +0 -1
  69. package/dist/connection/BaseAbapConnection.js +0 -75
  70. package/dist/connection/CertificateAbapConnection.d.ts +0 -25
  71. package/dist/connection/CertificateAbapConnection.d.ts.map +0 -1
  72. package/dist/connection/CertificateAbapConnection.js +0 -79
  73. package/dist/connection/JwtAbapConnection.d.ts +0 -115
  74. package/dist/connection/JwtAbapConnection.d.ts.map +0 -1
  75. package/dist/connection/JwtAbapConnection.js +0 -358
  76. package/dist/connection/KerberosAbapConnection.d.ts +0 -24
  77. package/dist/connection/KerberosAbapConnection.d.ts.map +0 -1
  78. package/dist/connection/KerberosAbapConnection.js +0 -120
  79. package/dist/connection/RfcAbapConnection.d.ts +0 -49
  80. package/dist/connection/RfcAbapConnection.d.ts.map +0 -1
  81. package/dist/connection/RfcAbapConnection.js +0 -331
  82. package/dist/connection/SamlAbapConnection.d.ts +0 -25
  83. package/dist/connection/SamlAbapConnection.d.ts.map +0 -1
  84. package/dist/connection/SamlAbapConnection.js +0 -75
  85. package/dist/connection/connectionFactory.d.ts +0 -9
  86. package/dist/connection/connectionFactory.d.ts.map +0 -1
  87. package/dist/connection/connectionFactory.js +0 -32
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+ /**
3
+ * An ADT request carried over RFC.
4
+ *
5
+ * `SADT_REST_RFC_ENDPOINT` is the standard FM Eclipse ADT uses for on-prem ADT
6
+ * through JCo, and what it carries is a request line, header fields and a body
7
+ * — an HTTP request in all but the wire. So this translates, and translating is
8
+ * the whole of its job.
9
+ *
10
+ * It exists because an RFC conversation is inherently stateful: one ABAP
11
+ * session for the connection's lifetime, which is the way through HTTP 423
12
+ * "invalid lock handle" on legacy systems (BASIS < 7.50) where stateful HTTP
13
+ * sessions are not usable.
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
18
+ * also captured cookies would be doing that work twice, and the two copies
19
+ * would disagree the first time one of them was cleared.
20
+ */
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.RfcTransport = void 0;
23
+ const node_crypto_1 = require("node:crypto");
24
+ /**
25
+ * ADT exception types, mapped to what they would have been over HTTP. Standard
26
+ * types, returned in `<exc:exception>` XML.
27
+ */
28
+ const EXCEPTION_STATUS = {
29
+ ExceptionResourceNotFound: { code: 404, text: 'Not Found' },
30
+ ExceptionResourceNoAuthorization: { code: 403, text: 'Forbidden' },
31
+ ExceptionResourceAlreadyExists: { code: 409, text: 'Conflict' },
32
+ ExceptionResourceLocked: { code: 423, text: 'Locked' },
33
+ ExceptionBadRequest: { code: 400, text: 'Bad Request' },
34
+ ExceptionNotSupported: { code: 501, text: 'Not Implemented' },
35
+ ExceptionConflict: { code: 409, text: 'Conflict' },
36
+ };
37
+ function statusFromExceptionXml(body) {
38
+ const type = /<exc:type[^>]*>([^<]+)<\/exc:type>/.exec(body)?.[1]?.trim();
39
+ return ((type ? EXCEPTION_STATUS[type] : undefined) ?? {
40
+ code: 500,
41
+ text: 'Internal Server Error',
42
+ });
43
+ }
44
+ function message(e) {
45
+ if (e instanceof Error)
46
+ return e.message;
47
+ if (e && typeof e === 'object')
48
+ return JSON.stringify(e);
49
+ return String(e);
50
+ }
51
+ /** Axios's own default, which the classification above this seam is written against. */
52
+ const admits2xx = (status) => status >= 200 && status < 300;
53
+ class RfcTransport {
54
+ connect;
55
+ logger;
56
+ kind = 'rfc';
57
+ /** Which system this wire is for. Read by the compiler, never at runtime. */
58
+ system = 'onprem';
59
+ conversation = null;
60
+ /** Names the conversation, and so the ABAP session it carries. */
61
+ conversationId = '';
62
+ /**
63
+ * Nothing to fold in. `SADT_REST_RFC_ENDPOINT` answers with two header
64
+ * fields — `~server_protocol` and `content-type` — on every call, including
65
+ * one that asks for a token with `x-csrf-token: fetch`, and on a stateful
66
+ * POST. Measured on E19. There is no ICM in this path, so there is no ICF
67
+ * session to cookie and no application server to be redispatched between.
68
+ */
69
+ ingest() { }
70
+ /** None, and never any: see `ingest()`. */
71
+ cookies() {
72
+ return null;
73
+ }
74
+ /**
75
+ * Nothing to establish. `open()` made the ABAP session, and there is no token
76
+ * to earn: this endpoint answers `x-csrf-token: fetch` with the same two
77
+ * header fields it answers everything else with. A wire with no
78
+ * cross-site request to forge against needs no token to prove one was not.
79
+ */
80
+ async establish(_context) { }
81
+ /** None: see `establish()`. */
82
+ csrfToken() {
83
+ return null;
84
+ }
85
+ /**
86
+ * Ignored. Nothing on this wire reads a token, and keeping one would be
87
+ * state that never leaves the object.
88
+ */
89
+ adoptCsrfToken() { }
90
+ /**
91
+ * The conversation, which IS the session — so it is fingerprinted by its own
92
+ * existence rather than by an address the server hands out.
93
+ */
94
+ /** The conversation IS the session, so it exists exactly while that does. */
95
+ sessionEstablished() {
96
+ return this.conversation?.alive ?? false;
97
+ }
98
+ sessionFingerprint() {
99
+ const fingerprint = new Map();
100
+ if (this.conversation?.alive) {
101
+ fingerprint.set('rfc-conversation', this.conversationId);
102
+ }
103
+ return fingerprint;
104
+ }
105
+ /** None: there is no dispatcher in front of this wire to stay bound to. */
106
+ affinityHeaders() {
107
+ return {};
108
+ }
109
+ /**
110
+ * Nothing to forget separately. The session ends when the conversation does,
111
+ * and that is `close()` — a wire whose state could be dropped while the
112
+ * conversation stayed open would be claiming a session it had disowned.
113
+ */
114
+ forgetSession() { }
115
+ /**
116
+ * The client is built by a factory rather than constructed here: the SDK is
117
+ * an optional dependency loaded by `require` at open time, and a transport
118
+ * that reached for it in its constructor could not be built at all on a
119
+ * machine without it.
120
+ */
121
+ constructor(connect, logger = null) {
122
+ this.connect = connect;
123
+ this.logger = logger;
124
+ }
125
+ async open() {
126
+ if (this.conversation?.alive)
127
+ return;
128
+ const conversation = this.connect();
129
+ try {
130
+ await conversation.open();
131
+ }
132
+ catch (e) {
133
+ throw new Error(`Failed to open RFC connection: ${message(e)}`);
134
+ }
135
+ this.conversation = conversation;
136
+ // Minted here, where the session begins. A conversation opened after an
137
+ // earlier one closed is a DIFFERENT ABAP session, and the fingerprint has
138
+ // to say so — a constant would report `unchanged` across a reconnect and
139
+ // hide exactly the replacement the identity policy exists to catch.
140
+ this.conversationId = (0, node_crypto_1.randomUUID)();
141
+ this.logger?.debug('RFC conversation opened (stateful by nature)');
142
+ }
143
+ /** Never throws, and a repeat call finds nothing owed. */
144
+ async close() {
145
+ const conversation = this.conversation;
146
+ if (!conversation)
147
+ return;
148
+ this.conversation = null;
149
+ this.conversationId = '';
150
+ try {
151
+ await conversation.close();
152
+ this.logger?.debug('RFC conversation closed');
153
+ }
154
+ catch (e) {
155
+ this.logger?.debug(`RFC close error: ${message(e)}`);
156
+ }
157
+ }
158
+ async send(request) {
159
+ if (!this.conversation?.alive) {
160
+ throw new Error('RFC transport is not open. Call connect() first.');
161
+ }
162
+ const method = request.method.toUpperCase();
163
+ // HTTP clients serialise `params`; RFC has no such step, so they go into
164
+ // the URI here or they do not travel at all.
165
+ let uri = request.url;
166
+ const query = Object.entries(request.params ?? {}).filter(([, v]) => v !== undefined && v !== null);
167
+ if (query.length > 0) {
168
+ const serialised = new URLSearchParams(query.map(([k, v]) => [k, String(v)])).toString();
169
+ uri += (uri.includes('?') ? '&' : '?') + serialised;
170
+ }
171
+ const headerFields = Object.entries(request.headers ?? {}).map(([NAME, VALUE]) => ({ NAME, VALUE: String(VALUE) }));
172
+ // ADT refuses a request with no Accept — `400 ExceptionResourceBadRequest:
173
+ // Accept header missing`, measured on E19. Over HTTP axios supplies the
174
+ // default and nobody notices; this endpoint forwards only what it is
175
+ // handed, so without this the same call dies on this wire alone.
176
+ if (!headerFields.some((h) => h.NAME.toLowerCase() === 'accept')) {
177
+ headerFields.push({ NAME: 'Accept', VALUE: '*/*' });
178
+ }
179
+ const body = request.data !== undefined && request.data !== null
180
+ ? String(request.data)
181
+ : '';
182
+ if (body &&
183
+ !headerFields.some((h) => h.NAME.toLowerCase() === 'content-type')) {
184
+ headerFields.push({
185
+ NAME: 'Content-Type',
186
+ VALUE: 'text/plain; charset=utf-8',
187
+ });
188
+ }
189
+ this.logger?.debug(`RFC → ${method} ${uri}`);
190
+ let raw;
191
+ try {
192
+ raw = await this.conversation.call('SADT_REST_RFC_ENDPOINT', {
193
+ REQUEST: {
194
+ REQUEST_LINE: {
195
+ METHOD: method,
196
+ URI: uri,
197
+ VERSION: 'HTTP/1.1',
198
+ },
199
+ HEADER_FIELDS: headerFields,
200
+ MESSAGE_BODY: body ? Buffer.from(body, 'utf-8') : Buffer.alloc(0),
201
+ },
202
+ });
203
+ }
204
+ catch (e) {
205
+ // An RFC-level failure, not an answer: nothing came back to classify.
206
+ const msg = message(e);
207
+ this.logger?.error(`RFC call failed: ${msg}`);
208
+ throw new Error(`RFC call to SADT_REST_RFC_ENDPOINT failed: ${msg}`);
209
+ }
210
+ const answer = raw.RESPONSE ?? raw;
211
+ const rawCode = answer.STATUS_LINE?.STATUS_CODE ?? answer.STATUS_LINE?.CODE;
212
+ let status = typeof rawCode === 'string'
213
+ ? Number.parseInt(rawCode, 10)
214
+ : (rawCode ?? 0);
215
+ let statusText = answer.STATUS_LINE?.REASON_PHRASE ?? answer.STATUS_LINE?.REASON ?? '';
216
+ const data = answer.MESSAGE_BODY
217
+ ? Buffer.isBuffer(answer.MESSAGE_BODY)
218
+ ? answer.MESSAGE_BODY.toString('utf-8')
219
+ : String(answer.MESSAGE_BODY)
220
+ : '';
221
+ const headers = {};
222
+ for (const field of answer.HEADER_FIELDS ?? []) {
223
+ if (field.NAME && field.VALUE !== undefined) {
224
+ headers[String(field.NAME).toLowerCase()] = field.VALUE;
225
+ }
226
+ }
227
+ // BASIS < 7.50 answers without populating STATUS_LINE. Left alone, a
228
+ // failure would read as 200 with an error document in the body — which is
229
+ // the green run that inspected nothing, one layer down.
230
+ if (!status && data.includes('<exc:exception')) {
231
+ const detected = statusFromExceptionXml(data);
232
+ status = detected.code;
233
+ statusText = detected.text;
234
+ this.logger?.debug(`RFC: STATUS_LINE empty, read ${status} out of the exception XML`);
235
+ }
236
+ if (!status) {
237
+ status = 200;
238
+ statusText = statusText || 'OK';
239
+ }
240
+ this.logger?.debug(`RFC ← ${status} ${statusText} (${data.length} bytes)`);
241
+ const response = {
242
+ status,
243
+ statusText,
244
+ headers,
245
+ data,
246
+ };
247
+ const admits = request.validateStatus ?? admits2xx;
248
+ if (!admits(status)) {
249
+ const error = new Error(`Request failed with status ${status}: ${method} ${uri}`);
250
+ error.response = response;
251
+ throw error;
252
+ }
253
+ return response;
254
+ }
255
+ }
256
+ exports.RfcTransport = RfcTransport;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * An RFC conversation, from the config a consumer already holds.
3
+ *
4
+ * `RfcTransport` takes a factory rather than connection parameters, and that is
5
+ * deliberate: `@mcp-abap-adt/sap-rfc-lite` needs the SAP NW RFC SDK on the
6
+ * machine, and a transport that reached for it in its constructor could not be
7
+ * built at all where it is absent. But a factory is a poor front door — without
8
+ * this, reaching the RFC wire means every consumer writing the `require` and
9
+ * deriving `ashost` and `sysnr` for themselves, which is the derivation the
10
+ * per-transport connection class used to do for them.
11
+ *
12
+ * So the derivation lives here, once, and the SDK is still only touched when a
13
+ * conversation is actually opened.
14
+ */
15
+ import type { SapConfig } from '../config/sapConfig.js';
16
+ import type { IRfcConversation } from './RfcTransport.js';
17
+ /** What the native client is constructed with. */
18
+ export interface RfcConnectionParams {
19
+ ashost: string;
20
+ sysnr: string;
21
+ client: string;
22
+ user: string;
23
+ passwd: string;
24
+ lang: string;
25
+ }
26
+ /**
27
+ * Read out of the config, never asked of the server.
28
+ *
29
+ * The system number comes off the HTTP port by the SAP convention that 80XX is
30
+ * the ICM port for system XX — the convention the port itself follows, not a
31
+ * guess about the deployment. `SAP_SYSNR` overrides it for a port that follows
32
+ * no convention, which is the case on a system reached through 50400.
33
+ */
34
+ export declare function rfcParamsFrom(config: SapConfig): RfcConnectionParams;
35
+ /**
36
+ * The factory `RfcTransport` asks for.
37
+ *
38
+ * The SDK is loaded when a conversation is opened, not when this is called, so
39
+ * a consumer can build the transport on a machine that has no SDK and find out
40
+ * at `connect()` rather than at construction — with a message that says what to
41
+ * install.
42
+ */
43
+ export declare function rfcConversationFrom(config: SapConfig): () => IRfcConversation;
44
+ //# sourceMappingURL=rfcConversation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rfcConversation.d.ts","sourceRoot":"","sources":["../../src/connection/rfcConversation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,kDAAkD;AAClD,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,mBAAmB,CAoBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,gBAAgB,CAmB7E"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ /**
3
+ * An RFC conversation, from the config a consumer already holds.
4
+ *
5
+ * `RfcTransport` takes a factory rather than connection parameters, and that is
6
+ * deliberate: `@mcp-abap-adt/sap-rfc-lite` needs the SAP NW RFC SDK on the
7
+ * machine, and a transport that reached for it in its constructor could not be
8
+ * built at all where it is absent. But a factory is a poor front door — without
9
+ * this, reaching the RFC wire means every consumer writing the `require` and
10
+ * deriving `ashost` and `sysnr` for themselves, which is the derivation the
11
+ * per-transport connection class used to do for them.
12
+ *
13
+ * So the derivation lives here, once, and the SDK is still only touched when a
14
+ * conversation is actually opened.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.rfcParamsFrom = rfcParamsFrom;
18
+ exports.rfcConversationFrom = rfcConversationFrom;
19
+ /**
20
+ * Read out of the config, never asked of the server.
21
+ *
22
+ * The system number comes off the HTTP port by the SAP convention that 80XX is
23
+ * the ICM port for system XX — the convention the port itself follows, not a
24
+ * guess about the deployment. `SAP_SYSNR` overrides it for a port that follows
25
+ * no convention, which is the case on a system reached through 50400.
26
+ */
27
+ function rfcParamsFrom(config) {
28
+ if (!config.url) {
29
+ throw new Error('An RFC conversation needs a url to take its host from');
30
+ }
31
+ if (!config.username || !config.password) {
32
+ throw new Error('An RFC conversation needs both a username and a password');
33
+ }
34
+ const parsed = new URL(config.url);
35
+ const port = Number.parseInt(parsed.port || '8000', 10);
36
+ const derived = String(port - 8000).padStart(2, '0');
37
+ return {
38
+ ashost: parsed.hostname,
39
+ sysnr: process.env.SAP_SYSNR?.trim() || derived,
40
+ client: config.client || '000',
41
+ user: config.username,
42
+ passwd: config.password,
43
+ lang: 'EN',
44
+ };
45
+ }
46
+ /**
47
+ * The factory `RfcTransport` asks for.
48
+ *
49
+ * The SDK is loaded when a conversation is opened, not when this is called, so
50
+ * a consumer can build the transport on a machine that has no SDK and find out
51
+ * at `connect()` rather than at construction — with a message that says what to
52
+ * install.
53
+ */
54
+ function rfcConversationFrom(config) {
55
+ const params = rfcParamsFrom(config);
56
+ return () => {
57
+ let Client;
58
+ try {
59
+ // Dynamic, because the SDK is an optional peer: it needs the SAP NW RFC
60
+ // SDK installed on the machine, and most consumers travel over HTTP.
61
+ // biome-ignore lint/correctness/noNodejsModules: optional by design
62
+ Client = require('@mcp-abap-adt/sap-rfc-lite').Client;
63
+ }
64
+ catch (error) {
65
+ throw new Error('@mcp-abap-adt/sap-rfc-lite is not available. To take the RFC wire, ' +
66
+ 'install the SAP NW RFC SDK and run: npm install @mcp-abap-adt/sap-rfc-lite. ' +
67
+ `Details: ${error instanceof Error ? error.message : String(error)}`);
68
+ }
69
+ return new Client(params);
70
+ };
71
+ }
package/dist/index.d.ts CHANGED
@@ -1,17 +1,20 @@
1
1
  export type { IWebSocketCloseInfo, IWebSocketConnectOptions, IWebSocketMessageEnvelope, IWebSocketMessageHandler, IWebSocketTransport, } from '@mcp-abap-adt/interfaces';
2
2
  export { FileCertificateMaterialLoader } from './auth/FileCertificateMaterialLoader.js';
3
+ export { BasicAuthProvider, CertificateAuthProvider, SamlAuthProvider, TokenAuthProvider, } from './auth/providers.js';
3
4
  export type { SapAuthType, SapConfig, SapConnectionType, } from './config/sapConfig.js';
4
5
  export { sapConfigSignature } from './config/sapConfig.js';
5
6
  export type { AbapConnection, AbapRequestOptions, } from './connection/AbapConnection.js';
6
- export { BaseAbapConnection, BaseAbapConnection as OnPremAbapConnection, } from './connection/BaseAbapConnection.js';
7
- export { CertificateAbapConnection } from './connection/CertificateAbapConnection.js';
8
- export { createAbapConnection } from './connection/connectionFactory.js';
7
+ export { AdtCloudConnector } from './connection/AdtCloudConnector.js';
8
+ export { AdtOnPremConnector } from './connection/AdtOnPremConnector.js';
9
+ export { CloudHttpTransport } from './connection/CloudHttpTransport.js';
9
10
  export { CSRF_CONFIG, CSRF_ERROR_MESSAGES } from './connection/csrfConfig.js';
10
11
  export { GenericWebSocketTransport, type IWebSocketFactory, type IWebSocketLike, } from './connection/GenericWebSocketTransport.js';
11
- export { JwtAbapConnection, JwtAbapConnection as CloudAbapConnection, } from './connection/JwtAbapConnection.js';
12
- export { KerberosAbapConnection } from './connection/KerberosAbapConnection.js';
13
- export { RfcAbapConnection } from './connection/RfcAbapConnection.js';
14
- export { SamlAbapConnection } from './connection/SamlAbapConnection.js';
12
+ export { HttpTransport } from './connection/HttpTransport.js';
13
+ export type { IAdtEstablishContext, IAdtSessionContext, IAdtTransport, IAdtTransportRequest, IAdtTransportResponse, ICloudTransport, IOnPremTransport, } from './connection/IAdtTransport.js';
14
+ export { LegacyOnPremHttpTransport } from './connection/LegacyOnPremHttpTransport.js';
15
+ export { OnPremHttpTransport } from './connection/OnPremHttpTransport.js';
16
+ export { type IRfcConversation, RfcTransport, } from './connection/RfcTransport.js';
17
+ export { type RfcConnectionParams, rfcConversationFrom, rfcParamsFrom, } from './connection/rfcConversation.js';
15
18
  export type { ILogger } from './logger.js';
16
19
  export { getTimeout, getTimeoutConfig, type TimeoutConfig, } from './utils/timeouts.js';
17
20
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAC;AACxF,YAAY,EACV,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AAOxC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,IAAI,oBAAoB,GAC3C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAEtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,IAAI,mBAAmB,GACzC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAC;AAKxF,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAQxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAC9D,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,GACjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EACL,KAAK,gBAAgB,EACrB,YAAY,GACb,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EACL,KAAK,mBAAmB,EACxB,mBAAmB,EACnB,aAAa,GACd,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -1,41 +1,53 @@
1
1
  "use strict";
2
2
  // Types - re-exported from interfaces package with backward compatibility aliases
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.getTimeoutConfig = exports.getTimeout = exports.SamlAbapConnection = exports.RfcAbapConnection = exports.KerberosAbapConnection = exports.CloudAbapConnection = exports.JwtAbapConnection = exports.GenericWebSocketTransport = exports.CSRF_ERROR_MESSAGES = exports.CSRF_CONFIG = exports.createAbapConnection = exports.CertificateAbapConnection = exports.OnPremAbapConnection = exports.BaseAbapConnection = exports.sapConfigSignature = exports.FileCertificateMaterialLoader = void 0;
4
+ exports.getTimeoutConfig = exports.getTimeout = exports.rfcParamsFrom = exports.rfcConversationFrom = exports.RfcTransport = exports.OnPremHttpTransport = exports.LegacyOnPremHttpTransport = exports.HttpTransport = exports.GenericWebSocketTransport = exports.CSRF_ERROR_MESSAGES = exports.CSRF_CONFIG = exports.CloudHttpTransport = exports.AdtOnPremConnector = exports.AdtCloudConnector = exports.sapConfigSignature = exports.TokenAuthProvider = exports.SamlAuthProvider = exports.CertificateAuthProvider = exports.BasicAuthProvider = exports.FileCertificateMaterialLoader = void 0;
5
5
  var FileCertificateMaterialLoader_js_1 = require("./auth/FileCertificateMaterialLoader.js");
6
6
  Object.defineProperty(exports, "FileCertificateMaterialLoader", { enumerable: true, get: function () { return FileCertificateMaterialLoader_js_1.FileCertificateMaterialLoader; } });
7
+ // IAuthProvider is deliberately NOT re-exported. It lives in
8
+ // @mcp-abap-adt/interfaces and a consumer imports it from there — the same rule
9
+ // the session-lifecycle vocabulary follows below, and for the same reason: two
10
+ // names for one contract let the two drift.
11
+ var providers_js_1 = require("./auth/providers.js");
12
+ Object.defineProperty(exports, "BasicAuthProvider", { enumerable: true, get: function () { return providers_js_1.BasicAuthProvider; } });
13
+ Object.defineProperty(exports, "CertificateAuthProvider", { enumerable: true, get: function () { return providers_js_1.CertificateAuthProvider; } });
14
+ Object.defineProperty(exports, "SamlAuthProvider", { enumerable: true, get: function () { return providers_js_1.SamlAuthProvider; } });
15
+ Object.defineProperty(exports, "TokenAuthProvider", { enumerable: true, get: function () { return providers_js_1.TokenAuthProvider; } });
7
16
  // Config utilities
8
17
  var sapConfig_js_1 = require("./config/sapConfig.js");
9
18
  Object.defineProperty(exports, "sapConfigSignature", { enumerable: true, get: function () { return sapConfig_js_1.sapConfigSignature; } });
19
+ var AdtCloudConnector_js_1 = require("./connection/AdtCloudConnector.js");
20
+ Object.defineProperty(exports, "AdtCloudConnector", { enumerable: true, get: function () { return AdtCloudConnector_js_1.AdtCloudConnector; } });
21
+ var AdtOnPremConnector_js_1 = require("./connection/AdtOnPremConnector.js");
22
+ Object.defineProperty(exports, "AdtOnPremConnector", { enumerable: true, get: function () { return AdtOnPremConnector_js_1.AdtOnPremConnector; } });
23
+ var CloudHttpTransport_js_1 = require("./connection/CloudHttpTransport.js");
24
+ Object.defineProperty(exports, "CloudHttpTransport", { enumerable: true, get: function () { return CloudHttpTransport_js_1.CloudHttpTransport; } });
10
25
  // The session lifecycle vocabulary — ISessionLifecycleAware, ADT_SESSION_ERROR —
11
26
  // is deliberately NOT exported here. It lives in @mcp-abap-adt/interfaces, and a
12
27
  // consumer imports it from there: re-exporting a contract type gives it two
13
28
  // names and lets the two drift.
14
29
  // Connection classes - only final implementations
15
30
  // Deprecated aliases for backward compatibility
16
- var BaseAbapConnection_js_1 = require("./connection/BaseAbapConnection.js");
17
- Object.defineProperty(exports, "BaseAbapConnection", { enumerable: true, get: function () { return BaseAbapConnection_js_1.BaseAbapConnection; } });
18
- Object.defineProperty(exports, "OnPremAbapConnection", { enumerable: true, get: function () { return BaseAbapConnection_js_1.BaseAbapConnection; } });
19
- var CertificateAbapConnection_js_1 = require("./connection/CertificateAbapConnection.js");
20
- Object.defineProperty(exports, "CertificateAbapConnection", { enumerable: true, get: function () { return CertificateAbapConnection_js_1.CertificateAbapConnection; } });
21
- // Factory
22
- var connectionFactory_js_1 = require("./connection/connectionFactory.js");
23
- Object.defineProperty(exports, "createAbapConnection", { enumerable: true, get: function () { return connectionFactory_js_1.createAbapConnection; } });
24
31
  // CSRF configuration
25
32
  var csrfConfig_js_1 = require("./connection/csrfConfig.js");
26
33
  Object.defineProperty(exports, "CSRF_CONFIG", { enumerable: true, get: function () { return csrfConfig_js_1.CSRF_CONFIG; } });
27
34
  Object.defineProperty(exports, "CSRF_ERROR_MESSAGES", { enumerable: true, get: function () { return csrfConfig_js_1.CSRF_ERROR_MESSAGES; } });
28
35
  var GenericWebSocketTransport_js_1 = require("./connection/GenericWebSocketTransport.js");
29
36
  Object.defineProperty(exports, "GenericWebSocketTransport", { enumerable: true, get: function () { return GenericWebSocketTransport_js_1.GenericWebSocketTransport; } });
30
- var JwtAbapConnection_js_1 = require("./connection/JwtAbapConnection.js");
31
- Object.defineProperty(exports, "JwtAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_1.JwtAbapConnection; } });
32
- Object.defineProperty(exports, "CloudAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_1.JwtAbapConnection; } });
33
- var KerberosAbapConnection_js_1 = require("./connection/KerberosAbapConnection.js");
34
- Object.defineProperty(exports, "KerberosAbapConnection", { enumerable: true, get: function () { return KerberosAbapConnection_js_1.KerberosAbapConnection; } });
35
- var RfcAbapConnection_js_1 = require("./connection/RfcAbapConnection.js");
36
- Object.defineProperty(exports, "RfcAbapConnection", { enumerable: true, get: function () { return RfcAbapConnection_js_1.RfcAbapConnection; } });
37
- var SamlAbapConnection_js_1 = require("./connection/SamlAbapConnection.js");
38
- Object.defineProperty(exports, "SamlAbapConnection", { enumerable: true, get: function () { return SamlAbapConnection_js_1.SamlAbapConnection; } });
37
+ // The transport axis. Both ends are objects, so a caller can name either.
38
+ var HttpTransport_js_1 = require("./connection/HttpTransport.js");
39
+ Object.defineProperty(exports, "HttpTransport", { enumerable: true, get: function () { return HttpTransport_js_1.HttpTransport; } });
40
+ var LegacyOnPremHttpTransport_js_1 = require("./connection/LegacyOnPremHttpTransport.js");
41
+ Object.defineProperty(exports, "LegacyOnPremHttpTransport", { enumerable: true, get: function () { return LegacyOnPremHttpTransport_js_1.LegacyOnPremHttpTransport; } });
42
+ var OnPremHttpTransport_js_1 = require("./connection/OnPremHttpTransport.js");
43
+ Object.defineProperty(exports, "OnPremHttpTransport", { enumerable: true, get: function () { return OnPremHttpTransport_js_1.OnPremHttpTransport; } });
44
+ var RfcTransport_js_1 = require("./connection/RfcTransport.js");
45
+ Object.defineProperty(exports, "RfcTransport", { enumerable: true, get: function () { return RfcTransport_js_1.RfcTransport; } });
46
+ // The front door to the RFC wire: the derivation a consumer would otherwise
47
+ // copy, and the SDK loaded only when a conversation is opened.
48
+ var rfcConversation_js_1 = require("./connection/rfcConversation.js");
49
+ Object.defineProperty(exports, "rfcConversationFrom", { enumerable: true, get: function () { return rfcConversation_js_1.rfcConversationFrom; } });
50
+ Object.defineProperty(exports, "rfcParamsFrom", { enumerable: true, get: function () { return rfcConversation_js_1.rfcParamsFrom; } });
39
51
  // Timeouts
40
52
  var timeouts_js_1 = require("./utils/timeouts.js");
41
53
  Object.defineProperty(exports, "getTimeout", { enumerable: true, get: function () { return timeouts_js_1.getTimeout; } });
@@ -7,8 +7,23 @@
7
7
  * this unit testable without a server, and it is where the hard part lives:
8
8
  * ordering, admission, and telling one session from the next.
9
9
  *
10
- * Design: docs/superpowers/specs/2026-07-31-teardown-policy-design.md in
11
- * @mcp-abap-adt/adt-clients.
10
+ * What it guarantees, since the design note it used to point at lived in another
11
+ * package and has been deleted with the work it described:
12
+ *
13
+ * - **One transition at a time**, on a serializing tail. Connects join
14
+ * connects and disconnects join disconnects, so concurrent callers share one
15
+ * establishment or one teardown rather than racing to build and demolish the
16
+ * same session.
17
+ * - **Admission is synchronous.** A request is admitted, or refused, in the
18
+ * same step that counts it — so nothing can be admitted and still be
19
+ * invisible to a teardown draining at that instant.
20
+ * - **A teardown waits for nothing.** In-flight requests run to completion
21
+ * untouched; their results are fenced by session generation so a late answer
22
+ * cannot write over a session established since.
23
+ * - **Identity is observed, never assumed.** A changed fingerprint means the
24
+ * session we had is gone and this is a different one, which is fatal to
25
+ * anything held against the old one;
26
+ * an unchanged one says nothing about whether the server still has it.
12
27
  */
13
28
  import { type AdtSessionErrorCode } from '@mcp-abap-adt/interfaces';
14
29
  export type TransitionKind = 'connect' | 'disconnect' | 'recover' | 'cleanup';
@@ -1 +1 @@
1
- {"version":3,"file":"SessionLifecycle.d.ts","sourceRoot":"","sources":["../../src/session/SessionLifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAElC,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,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAElC,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"}
@@ -8,8 +8,23 @@
8
8
  * this unit testable without a server, and it is where the hard part lives:
9
9
  * ordering, admission, and telling one session from the next.
10
10
  *
11
- * Design: docs/superpowers/specs/2026-07-31-teardown-policy-design.md in
12
- * @mcp-abap-adt/adt-clients.
11
+ * What it guarantees, since the design note it used to point at lived in another
12
+ * package and has been deleted with the work it described:
13
+ *
14
+ * - **One transition at a time**, on a serializing tail. Connects join
15
+ * connects and disconnects join disconnects, so concurrent callers share one
16
+ * establishment or one teardown rather than racing to build and demolish the
17
+ * same session.
18
+ * - **Admission is synchronous.** A request is admitted, or refused, in the
19
+ * same step that counts it — so nothing can be admitted and still be
20
+ * invisible to a teardown draining at that instant.
21
+ * - **A teardown waits for nothing.** In-flight requests run to completion
22
+ * untouched; their results are fenced by session generation so a late answer
23
+ * cannot write over a session established since.
24
+ * - **Identity is observed, never assumed.** A changed fingerprint means the
25
+ * session we had is gone and this is a different one, which is fatal to
26
+ * anything held against the old one;
27
+ * an unchanged one says nothing about whether the server still has it.
13
28
  */
14
29
  Object.defineProperty(exports, "__esModule", { value: true });
15
30
  exports.SessionLifecycle = void 0;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Join two `Cookie` header values, later wins on a repeated name.
3
+ *
4
+ * Used wherever a request already carries cookies and more are added: a
5
+ * credential can BE cookies — a SAML session is handed over as `MYSAPSSO2` —
6
+ * so writing the session jar over the header drops the very thing the request
7
+ * authenticates with. Both belong on the wire: one says who we are, the other
8
+ * which session we are in. A name in both is the session's, because that is the
9
+ * one the server just issued.
10
+ */
11
+ export declare function mergeCookieHeaders(existing: string | undefined, incoming: string | undefined): string;
12
+ //# sourceMappingURL=cookies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/utils/cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CASR"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeCookieHeaders = mergeCookieHeaders;
4
+ /**
5
+ * Join two `Cookie` header values, later wins on a repeated name.
6
+ *
7
+ * Used wherever a request already carries cookies and more are added: a
8
+ * credential can BE cookies — a SAML session is handed over as `MYSAPSSO2` —
9
+ * so writing the session jar over the header drops the very thing the request
10
+ * authenticates with. Both belong on the wire: one says who we are, the other
11
+ * which session we are in. A name in both is the session's, because that is the
12
+ * one the server just issued.
13
+ */
14
+ function mergeCookieHeaders(existing, incoming) {
15
+ const parts = [existing, incoming].filter(Boolean).join('; ');
16
+ const byName = new Map();
17
+ for (const part of parts.split(';')) {
18
+ const pair = part.trim();
19
+ if (!pair)
20
+ continue;
21
+ byName.set(pair.slice(0, pair.indexOf('=')), pair);
22
+ }
23
+ return [...byName.values()].join('; ');
24
+ }
@@ -5,9 +5,12 @@ export declare function getTimeout(type?: 'default' | 'csrf' | 'long' | number):
5
5
  /**
6
6
  * Large ceiling timeout applied while a connection is inside an uninterruptible
7
7
  * critical section (lock → modify → unlock). A short per-request timeout must
8
- * not abort such a request mid-flight: aborting tears down the socket, which
9
- * drops the stateful ADT session and orphans the lock handle (object left
10
- * locked and inactive). Configurable via SAP_TIMEOUT_CRITICAL (ms).
8
+ * not abort such a request mid-flight — not because aborting ends anything on
9
+ * the server, but because it ends what the CLIENT knows: the modification may
10
+ * or may not have been applied, and the handle `unlock` needs is gone, while
11
+ * the lock and the ABAP session holding it sit there until that server-side
12
+ * idle timeout, which this side can neither read nor influence.
13
+ * Configurable via SAP_TIMEOUT_CRITICAL (ms).
11
14
  * Default 600000 (10 minutes) — a generous ceiling that still guards against a
12
15
  * permanently dead socket hanging forever.
13
16
  */
@@ -1 +1 @@
1
- {"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG/D,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,wBAAgB,gBAAgB,IAAI,cAAc,CAajD;AAED,wBAAgB,UAAU,CACxB,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GACrD,MAAM,CAOR;AAED;;;;;;;;GAQG;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,0BAA0B,CAAC;AAG/D,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC;AAE3C,wBAAgB,gBAAgB,IAAI,cAAc,CAajD;AAED,wBAAgB,UAAU,CACxB,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GACrD,MAAM,CAOR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAElD"}
@@ -23,9 +23,12 @@ function getTimeout(type = 'default') {
23
23
  /**
24
24
  * Large ceiling timeout applied while a connection is inside an uninterruptible
25
25
  * critical section (lock → modify → unlock). A short per-request timeout must
26
- * not abort such a request mid-flight: aborting tears down the socket, which
27
- * drops the stateful ADT session and orphans the lock handle (object left
28
- * locked and inactive). Configurable via SAP_TIMEOUT_CRITICAL (ms).
26
+ * not abort such a request mid-flight — not because aborting ends anything on
27
+ * the server, but because it ends what the CLIENT knows: the modification may
28
+ * or may not have been applied, and the handle `unlock` needs is gone, while
29
+ * the lock and the ABAP session holding it sit there until that server-side
30
+ * idle timeout, which this side can neither read nor influence.
31
+ * Configurable via SAP_TIMEOUT_CRITICAL (ms).
29
32
  * Default 600000 (10 minutes) — a generous ceiling that still guards against a
30
33
  * permanently dead socket hanging forever.
31
34
  */