@mcp-abap-adt/connection 5.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +243 -1
- package/README.md +204 -48
- package/bin/sap-abap-auth.js +101 -13
- 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 +270 -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 -11
- 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,128 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.KerberosAbapConnection = void 0;
|
|
4
|
-
const axios_1 = require("axios");
|
|
5
|
-
const kerberosSpnego_js_1 = require("../auth/kerberosSpnego.js");
|
|
6
|
-
const ntlm_js_1 = require("../auth/ntlm.js");
|
|
7
|
-
const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
|
|
8
|
-
/**
|
|
9
|
-
* @deprecated The class you take should say which SYSTEM you are dialling,
|
|
10
|
-
* not which credential you hold — a session mechanism that rides along with
|
|
11
|
-
* the credential gets one of them wrong. Use `AdtOnPremConnector` with `a Kerberos credential`:
|
|
12
|
-
*
|
|
13
|
-
* not yet available — SPNEGO keeps its own class until its exchange is moved
|
|
14
|
-
*
|
|
15
|
-
* Kept working, and not removed here: existing consumers are unaffected.
|
|
16
|
-
*/
|
|
17
|
-
class KerberosAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnection {
|
|
18
|
-
spn;
|
|
19
|
-
currentToken = '';
|
|
20
|
-
constructor(config, logger, sessionId) {
|
|
21
|
-
KerberosAbapConnection.validateConfig(config);
|
|
22
|
-
super(config, logger || null, sessionId);
|
|
23
|
-
this.spn =
|
|
24
|
-
config.kerberosSpn ??
|
|
25
|
-
`${config.kerberosService ?? 'HTTP'}@${new URL(config.url).hostname}`;
|
|
26
|
-
}
|
|
27
|
-
static validateConfig(config) {
|
|
28
|
-
if (config.authType !== 'kerberos') {
|
|
29
|
-
throw new Error(`Kerberos connection expects authType "kerberos", got "${config.authType}"`);
|
|
30
|
-
}
|
|
31
|
-
if (config.connectionType === 'rfc') {
|
|
32
|
-
throw new Error('Kerberos auth is not supported with connectionType "rfc".');
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/** Generate the SPNEGO token once (single-leg). */
|
|
36
|
-
async ensureToken() {
|
|
37
|
-
if (!this.currentToken)
|
|
38
|
-
this.currentToken = await (0, kerberosSpnego_js_1.generateSpnegoToken)(this.spn);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Generates the SPNEGO token and primes the session. MUST be called before the first
|
|
42
|
-
* request — the first request carries the Negotiate header; SAP then issues a session
|
|
43
|
-
* cookie which is reused for subsequent requests.
|
|
44
|
-
*/
|
|
45
|
-
/**
|
|
46
|
-
* Establishes the session for this auth type. Called by
|
|
47
|
-
* AbstractAbapConnection.connect(), which owns the lifecycle around it.
|
|
48
|
-
*/
|
|
49
|
-
async establishSession() {
|
|
50
|
-
await this.ensureToken();
|
|
51
|
-
const baseUrl = await this.getBaseUrl();
|
|
52
|
-
const discoveryUrl = `${baseUrl}/sap/bc/adt/discovery`;
|
|
53
|
-
this.logger?.debug(`[DEBUG] KerberosAbapConnection - Connecting to SAP system: ${discoveryUrl}`);
|
|
54
|
-
try {
|
|
55
|
-
// ONE attempt, deliberately — the shared default is 3 retries a second
|
|
56
|
-
// apart, and every one of them corrupts this exchange:
|
|
57
|
-
//
|
|
58
|
-
// - A GSS token is one-shot. Replaying it is meaningless at best, and
|
|
59
|
-
// replay protection may answer it differently than the original.
|
|
60
|
-
// - Worse, a 401 that sets a cookie silences buildAuthorizationHeader()
|
|
61
|
-
// (a cookie carries auth after the first round trip), so the retry
|
|
62
|
-
// goes out with NO Authorization at all and draws the server's plain
|
|
63
|
-
// opening challenge.
|
|
64
|
-
//
|
|
65
|
-
// Either way the classifier below would read the LAST response and name
|
|
66
|
-
// the wrong cause. The retry cannot help here anyway: nothing about a
|
|
67
|
-
// rejected token changes by waiting a second. Retrying a transient
|
|
68
|
-
// network failure is the caller's business now that connect() is
|
|
69
|
-
// explicit, and a fresh connect() mints a fresh token.
|
|
70
|
-
const token = await this.fetchCsrfToken(discoveryUrl, 0, 0);
|
|
71
|
-
this.setCsrfToken(token);
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
// The token went out and was not accepted, so it is spent. Keeping it
|
|
75
|
-
// would make every later connect() replay a token already known to fail.
|
|
76
|
-
this.currentToken = '';
|
|
77
|
-
if (error instanceof axios_1.AxiosError && error.response?.headers) {
|
|
78
|
-
const wwwAuth = error.response.headers['www-authenticate'];
|
|
79
|
-
if ((0, ntlm_js_1.isNtlmChallenge)(wwwAuth)) {
|
|
80
|
-
throw new Error('KerberosAbapConnection: server offered NTLM authentication, which is rejected. ' +
|
|
81
|
-
'Only Kerberos/SPNEGO is supported. Ensure the SAP system accepts Kerberos (SPNEGO) for your user.');
|
|
82
|
-
}
|
|
83
|
-
if (this.getCookies()) {
|
|
84
|
-
this.logger?.debug('[DEBUG] KerberosAbapConnection - cookies captured from error response during connect');
|
|
85
|
-
}
|
|
86
|
-
// A 401 carrying a Negotiate token is a CONTINUATION, not a success.
|
|
87
|
-
//
|
|
88
|
-
// Per RFC 4559 the server is handing back gssapi-data for the client to
|
|
89
|
-
// feed into its GSS context and retry. This client cannot do that:
|
|
90
|
-
// generateSpnegoToken() calls step('') once and discards the context,
|
|
91
|
-
// so there is nothing to continue with. The token it holds is the
|
|
92
|
-
// initial one, and sending it again produces the same 401.
|
|
93
|
-
//
|
|
94
|
-
// So this fails, and says why — resolving here would claim a readiness
|
|
95
|
-
// that does not exist and move the failure to the first real request,
|
|
96
|
-
// where it reads as an unrelated auth error.
|
|
97
|
-
if ((0, ntlm_js_1.isNegotiateContinuation)(wwwAuth)) {
|
|
98
|
-
throw new Error('KerberosAbapConnection: the server continued the SPNEGO exchange ' +
|
|
99
|
-
'(401 with a Negotiate token), which requires feeding that token ' +
|
|
100
|
-
'back into the GSS context and retrying. This client performs a ' +
|
|
101
|
-
'single-leg exchange only — the context is discarded after the ' +
|
|
102
|
-
'first step — so multi-leg SPNEGO is not supported. See ' +
|
|
103
|
-
'https://www.rfc-editor.org/rfc/rfc4559');
|
|
104
|
-
}
|
|
105
|
-
// A BARE Negotiate is a different problem with a different fix: the
|
|
106
|
-
// initial Authorization has already gone out, so an empty challenge is
|
|
107
|
-
// the server declining it. No token came back, so there is nothing to
|
|
108
|
-
// step even for a client that supports multi-leg.
|
|
109
|
-
if ((0, ntlm_js_1.isBareNegotiateChallenge)(wwwAuth)) {
|
|
110
|
-
throw new Error('KerberosAbapConnection: the server rejected the SPNEGO token ' +
|
|
111
|
-
'(401 with a bare Negotiate challenge, no token returned). The ' +
|
|
112
|
-
'credentials were not accepted — check the SPN, the ticket ' +
|
|
113
|
-
'(klist / kinit), and that the user is permitted Kerberos logon.');
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
buildAuthorizationHeader() {
|
|
120
|
-
if (this.getCookies())
|
|
121
|
-
return ''; // cookie carries auth after first round-trip
|
|
122
|
-
if (!this.currentToken) {
|
|
123
|
-
throw new Error('KerberosAbapConnection: SPNEGO token not yet available. Call connect() before making requests.');
|
|
124
|
-
}
|
|
125
|
-
return `Negotiate ${this.currentToken}`;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
exports.KerberosAbapConnection = KerberosAbapConnection;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import type { IAbapRequestOptions, IAdtResponse } from '@mcp-abap-adt/interfaces';
|
|
2
|
-
import type { SapConfig } from '../config/sapConfig.js';
|
|
3
|
-
import type { ILogger } from '../logger.js';
|
|
4
|
-
import type { AbapConnection } from './AbapConnection.js';
|
|
5
|
-
/**
|
|
6
|
-
* RFC-based connection for on-premise SAP systems.
|
|
7
|
-
*
|
|
8
|
-
* Uses @mcp-abap-adt/sap-rfc-lite to call SADT_REST_RFC_ENDPOINT — the same standard SAP FM
|
|
9
|
-
* that Eclipse ADT uses for all on-premise ADT operations via JCo.
|
|
10
|
-
*
|
|
11
|
-
* RFC connections are inherently stateful: one ABAP session persists for the
|
|
12
|
-
* entire connection lifetime. This solves the HTTP 423 "invalid lock handle"
|
|
13
|
-
* problem on legacy systems (BASIS < 7.50) where HTTP stateful sessions
|
|
14
|
-
* are not supported.
|
|
15
|
-
*
|
|
16
|
-
* Connection parameters are derived from the standard ISapConfig.url field:
|
|
17
|
-
* http://saphost:8000 → ashost=saphost, sysnr=00
|
|
18
|
-
*
|
|
19
|
-
* Prerequisites:
|
|
20
|
-
* - SAP NW RFC SDK installed on the machine
|
|
21
|
-
* - @mcp-abap-adt/sap-rfc-lite package installed: npm install @mcp-abap-adt/sap-rfc-lite
|
|
22
|
-
*/
|
|
23
|
-
export declare class RfcAbapConnection implements AbapConnection {
|
|
24
|
-
private readonly config;
|
|
25
|
-
private readonly logger;
|
|
26
|
-
private rfcClient;
|
|
27
|
-
private readonly sessionId;
|
|
28
|
-
private readonly baseUrl;
|
|
29
|
-
private readonly rfcParams;
|
|
30
|
-
private sessionType;
|
|
31
|
-
private sessionCookie;
|
|
32
|
-
constructor(config: SapConfig, logger?: ILogger | null);
|
|
33
|
-
connect(): Promise<void>;
|
|
34
|
-
getBaseUrl(): Promise<string>;
|
|
35
|
-
getSessionId(): string | null;
|
|
36
|
-
setSessionType(type: 'stateful' | 'stateless'): void;
|
|
37
|
-
makeAdtRequest<T = any, D = any>(options: IAbapRequestOptions): Promise<IAdtResponse<T, D>>;
|
|
38
|
-
/**
|
|
39
|
-
* Close the RFC connection and release the ABAP session.
|
|
40
|
-
*/
|
|
41
|
-
close(): Promise<void>;
|
|
42
|
-
private static validateConfig;
|
|
43
|
-
}
|
|
44
|
-
//# sourceMappingURL=RfcAbapConnection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"RfcAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/RfcAbapConnection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAkG1D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IASpD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IATzB,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,aAAa,CAAuB;gBAGzB,MAAM,EAAE,SAAS,EACjB,MAAM,GAAE,OAAO,GAAG,IAAW;IAa1C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BxB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAU9C,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA8L9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAY5B,OAAO,CAAC,MAAM,CAAC,cAAc;CAqB9B"}
|
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RfcAbapConnection = void 0;
|
|
4
|
-
const node_crypto_1 = require("node:crypto");
|
|
5
|
-
function rfcErrorMessage(e) {
|
|
6
|
-
if (e instanceof Error)
|
|
7
|
-
return e.message;
|
|
8
|
-
if (e && typeof e === 'object')
|
|
9
|
-
return JSON.stringify(e);
|
|
10
|
-
return String(e);
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Derive RFC connection parameters from ISapConfig.
|
|
14
|
-
* Parses hostname from config.url, system number from port (80XX → XX).
|
|
15
|
-
*
|
|
16
|
-
* Examples:
|
|
17
|
-
* http://saphost:8000 → ashost=saphost, sysnr=00
|
|
18
|
-
* http://saphost:8001 → ashost=saphost, sysnr=01
|
|
19
|
-
* http://saphost:8042 → ashost=saphost, sysnr=42
|
|
20
|
-
*/
|
|
21
|
-
function buildRfcParams(config) {
|
|
22
|
-
const parsed = new URL(config.url);
|
|
23
|
-
const port = Number.parseInt(parsed.port || '8000', 10);
|
|
24
|
-
// SAP HTTP port convention: 80XX where XX = system number.
|
|
25
|
-
// SAP_SYSNR env var overrides derivation for non-standard ports (e.g. 50400).
|
|
26
|
-
const derivedSysnr = String(port - 8000).padStart(2, '0');
|
|
27
|
-
const sysnr = process.env.SAP_SYSNR?.trim() || derivedSysnr;
|
|
28
|
-
return {
|
|
29
|
-
ashost: parsed.hostname,
|
|
30
|
-
sysnr,
|
|
31
|
-
client: config.client || '000',
|
|
32
|
-
user: config.username || '',
|
|
33
|
-
passwd: config.password || '',
|
|
34
|
-
lang: 'EN',
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Map ADT exception type names to HTTP status codes.
|
|
39
|
-
* These are standard ADT exception types returned in `<exc:exception>` XML.
|
|
40
|
-
*/
|
|
41
|
-
const EXCEPTION_STATUS_MAP = {
|
|
42
|
-
ExceptionResourceNotFound: { code: 404, text: 'Not Found' },
|
|
43
|
-
ExceptionResourceNoAuthorization: { code: 403, text: 'Forbidden' },
|
|
44
|
-
ExceptionResourceAlreadyExists: { code: 409, text: 'Conflict' },
|
|
45
|
-
ExceptionResourceLocked: { code: 423, text: 'Locked' },
|
|
46
|
-
ExceptionBadRequest: { code: 400, text: 'Bad Request' },
|
|
47
|
-
ExceptionNotSupported: { code: 501, text: 'Not Implemented' },
|
|
48
|
-
ExceptionConflict: { code: 409, text: 'Conflict' },
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* Detect HTTP status from ADT exception XML in response body.
|
|
52
|
-
* On some legacy systems, SADT_REST_RFC_ENDPOINT returns errors as XML
|
|
53
|
-
* exceptions without setting proper HTTP status codes in STATUS_LINE.
|
|
54
|
-
*/
|
|
55
|
-
function detectExceptionStatus(body) {
|
|
56
|
-
// Extract exception type: <exc:exception ... xmlns:exc="..."><exc:type>ExceptionName</exc:type>
|
|
57
|
-
const typeMatch = body.match(/<exc:type[^>]*>([^<]+)<\/exc:type>/);
|
|
58
|
-
if (typeMatch) {
|
|
59
|
-
const exType = typeMatch[1].trim();
|
|
60
|
-
const mapped = EXCEPTION_STATUS_MAP[exType];
|
|
61
|
-
if (mapped) {
|
|
62
|
-
return { statusCode: mapped.code, statusText: mapped.text };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
// Unknown exception type — return 500 as generic server error
|
|
66
|
-
return { statusCode: 500, statusText: 'Internal Server Error' };
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* RFC-based connection for on-premise SAP systems.
|
|
70
|
-
*
|
|
71
|
-
* Uses @mcp-abap-adt/sap-rfc-lite to call SADT_REST_RFC_ENDPOINT — the same standard SAP FM
|
|
72
|
-
* that Eclipse ADT uses for all on-premise ADT operations via JCo.
|
|
73
|
-
*
|
|
74
|
-
* RFC connections are inherently stateful: one ABAP session persists for the
|
|
75
|
-
* entire connection lifetime. This solves the HTTP 423 "invalid lock handle"
|
|
76
|
-
* problem on legacy systems (BASIS < 7.50) where HTTP stateful sessions
|
|
77
|
-
* are not supported.
|
|
78
|
-
*
|
|
79
|
-
* Connection parameters are derived from the standard ISapConfig.url field:
|
|
80
|
-
* http://saphost:8000 → ashost=saphost, sysnr=00
|
|
81
|
-
*
|
|
82
|
-
* Prerequisites:
|
|
83
|
-
* - SAP NW RFC SDK installed on the machine
|
|
84
|
-
* - @mcp-abap-adt/sap-rfc-lite package installed: npm install @mcp-abap-adt/sap-rfc-lite
|
|
85
|
-
*/
|
|
86
|
-
class RfcAbapConnection {
|
|
87
|
-
config;
|
|
88
|
-
logger;
|
|
89
|
-
rfcClient = null;
|
|
90
|
-
sessionId;
|
|
91
|
-
baseUrl;
|
|
92
|
-
rfcParams;
|
|
93
|
-
sessionType = 'stateless';
|
|
94
|
-
sessionCookie = null;
|
|
95
|
-
constructor(config, logger = null) {
|
|
96
|
-
this.config = config;
|
|
97
|
-
this.logger = logger;
|
|
98
|
-
RfcAbapConnection.validateConfig(config);
|
|
99
|
-
this.sessionId = (0, node_crypto_1.randomUUID)();
|
|
100
|
-
this.baseUrl = config.url;
|
|
101
|
-
this.rfcParams = buildRfcParams(config);
|
|
102
|
-
this.logger?.debug(`RfcAbapConnection created for ${this.rfcParams.ashost}:${this.rfcParams.sysnr}, client ${this.rfcParams.client}`);
|
|
103
|
-
}
|
|
104
|
-
async connect() {
|
|
105
|
-
let Client;
|
|
106
|
-
try {
|
|
107
|
-
// Dynamic require — @mcp-abap-adt/sap-rfc-lite is NOT a declared dependency.
|
|
108
|
-
// Users who need RFC connections must install it manually:
|
|
109
|
-
// npm install @mcp-abap-adt/sap-rfc-lite (+ SAP NW RFC SDK on the machine)
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
111
|
-
const noderfc = require('@mcp-abap-adt/sap-rfc-lite');
|
|
112
|
-
Client = noderfc.Client;
|
|
113
|
-
}
|
|
114
|
-
catch (e) {
|
|
115
|
-
throw new Error('@mcp-abap-adt/sap-rfc-lite is not available. To use RFC connections, install SAP NW RFC SDK ' +
|
|
116
|
-
'and run: npm install @mcp-abap-adt/sap-rfc-lite. ' +
|
|
117
|
-
`Details: ${rfcErrorMessage(e)}`);
|
|
118
|
-
}
|
|
119
|
-
this.rfcClient = new Client(this.rfcParams);
|
|
120
|
-
try {
|
|
121
|
-
await this.rfcClient.open();
|
|
122
|
-
this.logger?.debug('RFC connection opened (stateful session)');
|
|
123
|
-
}
|
|
124
|
-
catch (e) {
|
|
125
|
-
this.rfcClient = null;
|
|
126
|
-
const msg = rfcErrorMessage(e);
|
|
127
|
-
this.logger?.error(`RFC connection failed: ${msg}`);
|
|
128
|
-
throw new Error(`Failed to open RFC connection: ${msg}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
async getBaseUrl() {
|
|
132
|
-
return this.baseUrl;
|
|
133
|
-
}
|
|
134
|
-
getSessionId() {
|
|
135
|
-
return this.sessionId;
|
|
136
|
-
}
|
|
137
|
-
setSessionType(type) {
|
|
138
|
-
this.sessionType = type;
|
|
139
|
-
if (type === 'stateless') {
|
|
140
|
-
this.sessionCookie = null;
|
|
141
|
-
this.logger?.debug('RFC session type: stateless (cookie cleared)');
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
this.logger?.debug('RFC session type: stateful (will capture cookies)');
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
async makeAdtRequest(options) {
|
|
148
|
-
if (!this.rfcClient?.alive) {
|
|
149
|
-
throw new Error('RFC connection is not open. Call connect() first.');
|
|
150
|
-
}
|
|
151
|
-
const method = options.method.toUpperCase();
|
|
152
|
-
// Encode query params into URI (axios does this automatically for HTTP,
|
|
153
|
-
// but RFC needs it done manually)
|
|
154
|
-
let uri = options.url;
|
|
155
|
-
if (options.params && typeof options.params === 'object') {
|
|
156
|
-
const entries = Object.entries(options.params).filter(([, v]) => v !== undefined && v !== null);
|
|
157
|
-
if (entries.length > 0) {
|
|
158
|
-
const qs = new URLSearchParams(entries.map(([k, v]) => [k, String(v)]));
|
|
159
|
-
uri += (uri.includes('?') ? '&' : '?') + qs.toString();
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Note: sap-client is NOT added to URI for RFC connections.
|
|
163
|
-
// The RFC session is already logged into the correct client
|
|
164
|
-
// (via the 'client' param in RFC connection). Adding sap-client
|
|
165
|
-
// to the URI can cause "object not found" on some systems.
|
|
166
|
-
// Build header fields
|
|
167
|
-
const headerFields = [];
|
|
168
|
-
if (options.headers) {
|
|
169
|
-
for (const [name, value] of Object.entries(options.headers)) {
|
|
170
|
-
headerFields.push({ NAME: name, VALUE: value });
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
// Inject session type header so SAP creates/maintains an ICM session
|
|
174
|
-
if (this.sessionType === 'stateful') {
|
|
175
|
-
headerFields.push({ NAME: 'x-sap-adt-sessiontype', VALUE: 'stateful' });
|
|
176
|
-
}
|
|
177
|
-
// Replay session cookie for stateful operations (cookie captured from LOCK response)
|
|
178
|
-
if (this.sessionCookie) {
|
|
179
|
-
const existingCookie = headerFields.find((h) => h.NAME.toLowerCase() === 'cookie');
|
|
180
|
-
if (!existingCookie) {
|
|
181
|
-
headerFields.push({ NAME: 'Cookie', VALUE: this.sessionCookie });
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
// Ensure Content-Type for body
|
|
185
|
-
const body = options.data !== undefined && options.data !== null
|
|
186
|
-
? String(options.data)
|
|
187
|
-
: '';
|
|
188
|
-
if (body &&
|
|
189
|
-
!headerFields.some((h) => h.NAME.toLowerCase() === 'content-type')) {
|
|
190
|
-
headerFields.push({
|
|
191
|
-
NAME: 'Content-Type',
|
|
192
|
-
VALUE: 'text/plain; charset=utf-8',
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
this.logger?.debug(`RFC → ${method} ${uri}`);
|
|
196
|
-
this.logger?.debug(`RFC → headers: ${JSON.stringify(headerFields)}`);
|
|
197
|
-
if (body)
|
|
198
|
-
this.logger?.debug(`RFC → body: ${body}`);
|
|
199
|
-
try {
|
|
200
|
-
const result = await this.rfcClient.call('SADT_REST_RFC_ENDPOINT', {
|
|
201
|
-
REQUEST: {
|
|
202
|
-
REQUEST_LINE: {
|
|
203
|
-
METHOD: method,
|
|
204
|
-
URI: uri,
|
|
205
|
-
VERSION: 'HTTP/1.1',
|
|
206
|
-
},
|
|
207
|
-
HEADER_FIELDS: headerFields,
|
|
208
|
-
MESSAGE_BODY: body ? Buffer.from(body, 'utf-8') : Buffer.alloc(0),
|
|
209
|
-
},
|
|
210
|
-
});
|
|
211
|
-
const resp = result.RESPONSE || result;
|
|
212
|
-
// Log raw RFC response structure for debugging
|
|
213
|
-
this.logger?.debug(`RFC raw response keys: ${Object.keys(resp).join(', ')}`);
|
|
214
|
-
if (resp.STATUS_LINE) {
|
|
215
|
-
this.logger?.debug(`RFC STATUS_LINE: ${JSON.stringify(resp.STATUS_LINE)}`);
|
|
216
|
-
}
|
|
217
|
-
// Parse status — RFC returns status in STATUS_LINE structure
|
|
218
|
-
// Field names: STATUS_CODE (not CODE), REASON_PHRASE (not REASON)
|
|
219
|
-
const rawCode = resp.STATUS_LINE?.STATUS_CODE || resp.STATUS_LINE?.CODE || 0;
|
|
220
|
-
let statusCode = typeof rawCode === 'string' ? Number.parseInt(rawCode, 10) : rawCode;
|
|
221
|
-
let statusText = resp.STATUS_LINE?.REASON_PHRASE || resp.STATUS_LINE?.REASON || '';
|
|
222
|
-
// Parse response body
|
|
223
|
-
const respBody = resp.MESSAGE_BODY
|
|
224
|
-
? Buffer.isBuffer(resp.MESSAGE_BODY)
|
|
225
|
-
? resp.MESSAGE_BODY.toString('utf-8')
|
|
226
|
-
: String(resp.MESSAGE_BODY)
|
|
227
|
-
: '';
|
|
228
|
-
// Parse response headers
|
|
229
|
-
const respHeaders = {};
|
|
230
|
-
const respHeaderFields = resp.HEADER_FIELDS || [];
|
|
231
|
-
for (const field of respHeaderFields) {
|
|
232
|
-
if (field.NAME && field.VALUE !== undefined) {
|
|
233
|
-
respHeaders[field.NAME.toLowerCase()] = field.VALUE;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
// Capture session cookie from LOCK/stateful responses for cookie replay
|
|
237
|
-
if (this.sessionType === 'stateful' && respHeaders['set-cookie']) {
|
|
238
|
-
const setCookieHeader = respHeaders['set-cookie'];
|
|
239
|
-
// Extract cookie name=value pairs (strip attributes like Path, Secure, etc.)
|
|
240
|
-
const cookies = Array.isArray(setCookieHeader)
|
|
241
|
-
? setCookieHeader
|
|
242
|
-
: [setCookieHeader];
|
|
243
|
-
const cookieValues = cookies
|
|
244
|
-
.map((c) => c.split(';')[0].trim())
|
|
245
|
-
.filter(Boolean);
|
|
246
|
-
if (cookieValues.length > 0) {
|
|
247
|
-
this.sessionCookie = cookieValues.join('; ');
|
|
248
|
-
this.logger?.debug(`RFC: captured session cookie: ${this.sessionCookie}`);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
// On some systems (e.g. BASIS < 7.50), SADT_REST_RFC_ENDPOINT does not
|
|
252
|
-
// populate STATUS_LINE, returning status 0. Detect errors from the
|
|
253
|
-
// response body: ADT exception XML indicates a failed request.
|
|
254
|
-
if (!statusCode && respBody.includes('<exc:exception')) {
|
|
255
|
-
const detected = detectExceptionStatus(respBody);
|
|
256
|
-
statusCode = detected.statusCode;
|
|
257
|
-
statusText = detected.statusText;
|
|
258
|
-
this.logger?.debug(`RFC: STATUS_LINE empty, detected ${statusCode} from exception XML`);
|
|
259
|
-
}
|
|
260
|
-
// Default to 200 OK only when no error was detected
|
|
261
|
-
if (!statusCode) {
|
|
262
|
-
statusCode = 200;
|
|
263
|
-
statusText = statusText || 'OK';
|
|
264
|
-
}
|
|
265
|
-
this.logger?.debug(`RFC ← ${statusCode} ${statusText} (${respBody.length} bytes)`);
|
|
266
|
-
this.logger?.debug(`RFC ← headers: ${JSON.stringify(respHeaders)}`);
|
|
267
|
-
if (respBody)
|
|
268
|
-
this.logger?.debug(`RFC ← body: ${respBody}`);
|
|
269
|
-
const response = {
|
|
270
|
-
data: respBody,
|
|
271
|
-
status: statusCode,
|
|
272
|
-
statusText,
|
|
273
|
-
headers: respHeaders,
|
|
274
|
-
};
|
|
275
|
-
// Throw for error status codes (matching HTTP/axios behavior)
|
|
276
|
-
if (statusCode >= 400) {
|
|
277
|
-
const error = new Error(`Request failed with status ${statusCode}: ${method} ${uri}`);
|
|
278
|
-
error.response = response;
|
|
279
|
-
throw error;
|
|
280
|
-
}
|
|
281
|
-
return response;
|
|
282
|
-
}
|
|
283
|
-
catch (e) {
|
|
284
|
-
// Re-throw our own errors (status >= 400)
|
|
285
|
-
if (e?.response) {
|
|
286
|
-
throw e;
|
|
287
|
-
}
|
|
288
|
-
// RFC-level error
|
|
289
|
-
const msg = rfcErrorMessage(e);
|
|
290
|
-
this.logger?.error(`RFC call failed: ${msg}`);
|
|
291
|
-
throw new Error(`RFC call to SADT_REST_RFC_ENDPOINT failed: ${msg}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
/**
|
|
295
|
-
* Close the RFC connection and release the ABAP session.
|
|
296
|
-
*/
|
|
297
|
-
async close() {
|
|
298
|
-
if (this.rfcClient) {
|
|
299
|
-
try {
|
|
300
|
-
await this.rfcClient.close();
|
|
301
|
-
this.logger?.debug('RFC connection closed');
|
|
302
|
-
}
|
|
303
|
-
catch (e) {
|
|
304
|
-
this.logger?.debug(`RFC close error: ${rfcErrorMessage(e)}`);
|
|
305
|
-
}
|
|
306
|
-
this.rfcClient = null;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
static validateConfig(config) {
|
|
310
|
-
if (config.connectionType !== 'rfc') {
|
|
311
|
-
throw new Error(`RFC connection expects connectionType "rfc", got "${config.connectionType}"`);
|
|
312
|
-
}
|
|
313
|
-
if (!config.url) {
|
|
314
|
-
throw new Error('RFC connection requires url (hostname is parsed from it)');
|
|
315
|
-
}
|
|
316
|
-
if (!config.username || !config.password) {
|
|
317
|
-
throw new Error('RFC connection requires both username and password');
|
|
318
|
-
}
|
|
319
|
-
if (!config.client) {
|
|
320
|
-
throw new Error('RFC connection requires SAP client');
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
exports.RfcAbapConnection = RfcAbapConnection;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { SapConfig } from '../config/sapConfig.js';
|
|
2
|
-
import type { ILogger } from '../logger.js';
|
|
3
|
-
import { AbstractAbapConnection } from './AbstractAbapConnection.js';
|
|
4
|
-
/**
|
|
5
|
-
* @deprecated The class you take should say which SYSTEM you are dialling,
|
|
6
|
-
* not which credential you hold — a session mechanism that rides along with
|
|
7
|
-
* the credential gets one of them wrong. Use `AdtOnPremConnector` with `SamlAuthProvider`:
|
|
8
|
-
*
|
|
9
|
-
* new AdtOnPremConnector(config, new SamlAuthProvider(cookies), logger)
|
|
10
|
-
*
|
|
11
|
-
* Kept working, and not removed here: existing consumers are unaffected.
|
|
12
|
-
*/
|
|
13
|
-
export declare class SamlAbapConnection extends AbstractAbapConnection {
|
|
14
|
-
private sessionCookies;
|
|
15
|
-
constructor(config: SapConfig, logger?: ILogger | null, sessionId?: string, options?: {
|
|
16
|
-
skipSessionType?: boolean;
|
|
17
|
-
});
|
|
18
|
-
/**
|
|
19
|
-
* Connect to SAP system using existing session cookies
|
|
20
|
-
* Fetches CSRF token to establish session context
|
|
21
|
-
*/
|
|
22
|
-
/**
|
|
23
|
-
* Establishes the session for this auth type. Called by
|
|
24
|
-
* AbstractAbapConnection.connect(), which owns the lifecycle around it.
|
|
25
|
-
*/
|
|
26
|
-
protected establishSession(): Promise<void>;
|
|
27
|
-
protected buildAuthorizationHeader(): string;
|
|
28
|
-
getAuthHeaders(): Promise<Record<string, string>>;
|
|
29
|
-
private static validateConfig;
|
|
30
|
-
}
|
|
31
|
-
//# sourceMappingURL=SamlAbapConnection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SamlAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/SamlAbapConnection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE;;;;;;;;GAQG;AACH,qBAAa,kBAAmB,SAAQ,sBAAsB;IAC5D,OAAO,CAAC,cAAc,CAAS;gBAG7B,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE;IAUzC;;;OAGG;IACH;;;OAGG;cACa,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAwCjD,SAAS,CAAC,wBAAwB,IAAI,MAAM;IAItC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAQvD,OAAO,CAAC,MAAM,CAAC,cAAc;CAW9B"}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SamlAbapConnection = void 0;
|
|
4
|
-
const axios_1 = require("axios");
|
|
5
|
-
const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
|
|
6
|
-
/**
|
|
7
|
-
* @deprecated The class you take should say which SYSTEM you are dialling,
|
|
8
|
-
* not which credential you hold — a session mechanism that rides along with
|
|
9
|
-
* the credential gets one of them wrong. Use `AdtOnPremConnector` with `SamlAuthProvider`:
|
|
10
|
-
*
|
|
11
|
-
* new AdtOnPremConnector(config, new SamlAuthProvider(cookies), logger)
|
|
12
|
-
*
|
|
13
|
-
* Kept working, and not removed here: existing consumers are unaffected.
|
|
14
|
-
*/
|
|
15
|
-
class SamlAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnection {
|
|
16
|
-
sessionCookies;
|
|
17
|
-
constructor(config, logger, sessionId, options) {
|
|
18
|
-
SamlAbapConnection.validateConfig(config);
|
|
19
|
-
super(config, logger || null, sessionId, options);
|
|
20
|
-
this.sessionCookies = config.sessionCookies || '';
|
|
21
|
-
if (this.sessionCookies) {
|
|
22
|
-
this.setInitialCookies(this.sessionCookies);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Connect to SAP system using existing session cookies
|
|
27
|
-
* Fetches CSRF token to establish session context
|
|
28
|
-
*/
|
|
29
|
-
/**
|
|
30
|
-
* Establishes the session for this auth type. Called by
|
|
31
|
-
* AbstractAbapConnection.connect(), which owns the lifecycle around it.
|
|
32
|
-
*/
|
|
33
|
-
async establishSession() {
|
|
34
|
-
const baseUrl = await this.getBaseUrl();
|
|
35
|
-
const discoveryUrl = `${baseUrl}/sap/bc/adt/discovery`;
|
|
36
|
-
this.logger?.debug(`[DEBUG] SamlAbapConnection - Connecting to SAP system: ${discoveryUrl}`);
|
|
37
|
-
try {
|
|
38
|
-
const token = await this.fetchCsrfToken(discoveryUrl);
|
|
39
|
-
this.setCsrfToken(token);
|
|
40
|
-
this.logger?.debug('Successfully connected to SAP system', {
|
|
41
|
-
hasCsrfToken: !!this.getCsrfToken(),
|
|
42
|
-
hasCookies: !!this.getCookies(),
|
|
43
|
-
cookieLength: this.getCookies()?.length || 0,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
48
|
-
this.logger?.warn(`[WARN] SamlAbapConnection - Could not connect to SAP system upfront: ${errorMsg}. Will retry on first request.`);
|
|
49
|
-
if (error instanceof axios_1.AxiosError && error.response?.headers) {
|
|
50
|
-
if (this.getCookies()) {
|
|
51
|
-
this.logger?.debug(`[DEBUG] SamlAbapConnection - Cookies extracted from error response during connect (first 100 chars): ${this.getCookies()?.substring(0, 100)}...`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Rethrow: a resolved connect() must mean a usable session exists. This
|
|
55
|
-
// used to swallow and resolve anyway, deferring establishment to the
|
|
56
|
-
// first request — coherent only while that lazy path existed. Without it,
|
|
57
|
-
// swallowing would leave a connection that reports success, holds
|
|
58
|
-
// nothing, and refuses every request.
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
buildAuthorizationHeader() {
|
|
63
|
-
return '';
|
|
64
|
-
}
|
|
65
|
-
async getAuthHeaders() {
|
|
66
|
-
const headers = await super.getAuthHeaders();
|
|
67
|
-
if (this.sessionCookies) {
|
|
68
|
-
headers.Cookie = this.sessionCookies;
|
|
69
|
-
}
|
|
70
|
-
return headers;
|
|
71
|
-
}
|
|
72
|
-
static validateConfig(config) {
|
|
73
|
-
if (config.authType !== 'saml') {
|
|
74
|
-
throw new Error(`SAML connection expects authType "saml", got "${config.authType}"`);
|
|
75
|
-
}
|
|
76
|
-
if (!config.sessionCookies) {
|
|
77
|
-
throw new Error('SAML authentication requires sessionCookies');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
exports.SamlAbapConnection = SamlAbapConnection;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { ICertificateMaterialLoader, ITokenRefresher } from '@mcp-abap-adt/interfaces';
|
|
2
|
-
import type { SapConfig } from '../config/sapConfig.js';
|
|
3
|
-
import type { ILogger } from '../logger.js';
|
|
4
|
-
import type { AbapConnection } from './AbapConnection.js';
|
|
5
|
-
/**
|
|
6
|
-
* Builds the connection the caller asked for.
|
|
7
|
-
*
|
|
8
|
-
* **Say which system it is** with `options.system`, and that is what you get —
|
|
9
|
-
* `'onprem'` or `'cloud'` — with the credential built from the config and
|
|
10
|
-
* handed in as an auth provider. Nothing about the system is worked out here:
|
|
11
|
-
* not from the credential, not from the host name, not by asking the server.
|
|
12
|
-
*
|
|
13
|
-
* Without it, the old behaviour: a connection chosen from `authType`, which is
|
|
14
|
-
* the arrangement where the credential drags the session mechanism along with
|
|
15
|
-
* it and gets one of them wrong. **Deprecated**, kept so existing callers keep
|
|
16
|
-
* working, and warned about once per call so it is visible rather than
|
|
17
|
-
* inherited by accident.
|
|
18
|
-
*/
|
|
19
|
-
export declare function createAbapConnection(config: SapConfig, logger?: ILogger | null, sessionId?: string, tokenRefresher?: ITokenRefresher, options?: {
|
|
20
|
-
skipSessionType?: boolean;
|
|
21
|
-
certLoader?: ICertificateMaterialLoader;
|
|
22
|
-
/** Which system this is. Said by the caller; never inferred. */
|
|
23
|
-
system?: 'onprem' | 'cloud';
|
|
24
|
-
}): AbapConnection;
|
|
25
|
-
//# sourceMappingURL=connectionFactory.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"connectionFactory.d.ts","sourceRoot":"","sources":["../../src/connection/connectionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAQlC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAmD1D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,eAAe,EAChC,OAAO,CAAC,EAAE;IACR,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,0BAA0B,CAAC;IACxC,gEAAgE;IAChE,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC7B,GACA,cAAc,CA2ChB"}
|