@mcp-abap-adt/connection 0.1.13 → 0.1.15
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/README.md +52 -0
- package/dist/config/sapConfig.d.ts +4 -14
- package/dist/config/sapConfig.d.ts.map +1 -1
- package/dist/connection/AbapConnection.d.ts +3 -23
- package/dist/connection/AbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
- package/dist/connection/AbstractAbapConnection.js +30 -48
- package/dist/logger.d.ts +3 -66
- package/dist/logger.d.ts.map +1 -1
- package/dist/utils/timeouts.d.ts +3 -6
- package/dist/utils/timeouts.d.ts.map +1 -1
- package/dist/utils/tokenRefresh.d.ts +3 -5
- package/dist/utils/tokenRefresh.d.ts.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -46,6 +46,58 @@ The package uses a clean separation of concerns:
|
|
|
46
46
|
- Permission vs auth error detection
|
|
47
47
|
- Suitable for SAP BTP ABAP Environment
|
|
48
48
|
|
|
49
|
+
## Responsibilities and Design Principles
|
|
50
|
+
|
|
51
|
+
### Core Development Principle
|
|
52
|
+
|
|
53
|
+
**Interface-Only Communication**: This package follows a fundamental development principle: **all interactions with external dependencies happen ONLY through interfaces**. The code knows **NOTHING beyond what is defined in the interfaces**.
|
|
54
|
+
|
|
55
|
+
This means:
|
|
56
|
+
- Does not know about concrete implementation classes from other packages
|
|
57
|
+
- Does not know about internal data structures or methods not defined in interfaces
|
|
58
|
+
- Does not make assumptions about implementation behavior beyond interface contracts
|
|
59
|
+
- Does not access properties or methods not explicitly defined in interfaces
|
|
60
|
+
|
|
61
|
+
This principle ensures:
|
|
62
|
+
- **Loose coupling**: Connection classes are decoupled from concrete implementations in other packages
|
|
63
|
+
- **Flexibility**: New implementations can be added without modifying connection classes
|
|
64
|
+
- **Testability**: Easy to mock dependencies for testing
|
|
65
|
+
- **Maintainability**: Changes to implementations don't affect connection classes
|
|
66
|
+
|
|
67
|
+
### Package Responsibilities
|
|
68
|
+
|
|
69
|
+
This package is responsible for:
|
|
70
|
+
|
|
71
|
+
1. **HTTP communication with SAP systems**: Makes HTTP requests to SAP ABAP systems via ADT protocol
|
|
72
|
+
2. **Authentication handling**: Supports Basic Auth and JWT/OAuth2 authentication methods
|
|
73
|
+
3. **Session management**: Manages cookies, CSRF tokens, and session state
|
|
74
|
+
4. **Token refresh**: Automatically refreshes expired JWT tokens (for `JwtAbapConnection`)
|
|
75
|
+
5. **Error handling**: Distinguishes between authentication errors and permission errors
|
|
76
|
+
|
|
77
|
+
#### What This Package Does
|
|
78
|
+
|
|
79
|
+
- **Provides connection abstraction**: `AbapConnection` interface for interacting with SAP systems
|
|
80
|
+
- **Handles HTTP requests**: Makes requests to SAP ADT endpoints with proper headers and authentication
|
|
81
|
+
- **Manages sessions**: Handles cookies, CSRF tokens, and session state persistence
|
|
82
|
+
- **Refreshes tokens**: Automatically refreshes expired JWT tokens when detected
|
|
83
|
+
- **Validates tokens**: Detects expired tokens by analyzing HTTP response codes (401/403)
|
|
84
|
+
|
|
85
|
+
#### What This Package Does NOT Do
|
|
86
|
+
|
|
87
|
+
- **Does NOT obtain tokens**: Token acquisition is handled by `@mcp-abap-adt/auth-providers` and `@mcp-abap-adt/auth-broker`
|
|
88
|
+
- **Does NOT store tokens**: Token storage is handled by `@mcp-abap-adt/auth-stores`
|
|
89
|
+
- **Does NOT orchestrate authentication**: Token lifecycle management is handled by `@mcp-abap-adt/auth-broker`
|
|
90
|
+
- **Does NOT know about destinations**: Destination-based authentication is handled by consumers
|
|
91
|
+
- **Does NOT handle OAuth2 flows**: OAuth2 flows are handled by token providers
|
|
92
|
+
|
|
93
|
+
### External Dependencies
|
|
94
|
+
|
|
95
|
+
This package interacts with external packages **ONLY through interfaces**:
|
|
96
|
+
|
|
97
|
+
- **Logger interface**: Uses `ILogger` interface for logging - does not know about concrete logger implementation
|
|
98
|
+
- **Session storage interface**: Uses `ISessionStorage` interface for session persistence - does not know about concrete storage implementation
|
|
99
|
+
- **No direct dependencies on auth packages**: All token-related operations are handled through configuration (`SapConfig`) passed by consumers
|
|
100
|
+
|
|
49
101
|
## Documentation
|
|
50
102
|
|
|
51
103
|
- 📦 **[Installation Guide](./docs/INSTALLATION.md)** - Setup and installation instructions
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
authType: SapAuthType;
|
|
6
|
-
username?: string;
|
|
7
|
-
password?: string;
|
|
8
|
-
jwtToken?: string;
|
|
9
|
-
refreshToken?: string;
|
|
10
|
-
uaaUrl?: string;
|
|
11
|
-
uaaClientId?: string;
|
|
12
|
-
uaaClientSecret?: string;
|
|
13
|
-
}
|
|
14
|
-
export declare function sapConfigSignature(config: SapConfig): string;
|
|
1
|
+
import type { ISapConfig, SapAuthType } from '@mcp-abap-adt/interfaces';
|
|
2
|
+
export type { SapAuthType };
|
|
3
|
+
export type SapConfig = ISapConfig;
|
|
4
|
+
export declare function sapConfigSignature(config: ISapConfig): string;
|
|
15
5
|
//# sourceMappingURL=sapConfig.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sapConfig.d.ts","sourceRoot":"","sources":["../../src/config/sapConfig.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sapConfig.d.ts","sourceRoot":"","sources":["../../src/config/sapConfig.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGxE,YAAY,EAAE,WAAW,EAAE,CAAC;AAC5B,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC;AAEnC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAmB7D"}
|
|
@@ -1,24 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export interface AbapRequestOptions {
|
|
5
|
-
url: string;
|
|
6
|
-
method: string;
|
|
7
|
-
timeout: number;
|
|
8
|
-
data?: any;
|
|
9
|
-
params?: any;
|
|
10
|
-
headers?: Record<string, string>;
|
|
11
|
-
}
|
|
12
|
-
export interface AbapConnection {
|
|
13
|
-
getConfig(): SapConfig;
|
|
14
|
-
getBaseUrl(): Promise<string>;
|
|
15
|
-
getAuthHeaders(): Promise<Record<string, string>>;
|
|
16
|
-
getSessionId(): string | null;
|
|
17
|
-
setSessionType(type: "stateful" | "stateless"): void;
|
|
18
|
-
makeAdtRequest(options: AbapRequestOptions): Promise<AxiosResponse>;
|
|
19
|
-
connect(): Promise<void>;
|
|
20
|
-
reset(): void;
|
|
21
|
-
getSessionState(): SessionState | null;
|
|
22
|
-
setSessionState(state: SessionState): void;
|
|
23
|
-
}
|
|
1
|
+
import type { IAbapRequestOptions, IAbapConnection } from '@mcp-abap-adt/interfaces';
|
|
2
|
+
export type AbapRequestOptions = IAbapRequestOptions;
|
|
3
|
+
export type AbapConnection = IAbapConnection;
|
|
24
4
|
//# sourceMappingURL=AbapConnection.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbapConnection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbapConnection.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAGrF,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AACrD,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"AAAA,OAAc,EAAiD,aAAa,EAAE,MAAM,OAAO,CAAC;AAG5F,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGzE,uBAAe,sBAAuB,YAAW,cAAc;IAW3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO;IAXpC,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,cAAc,CAAgC;IACtD,OAAO,CAAC,WAAW,CAAyC;IAE5D,SAAS,aACU,MAAM,EAAE,SAAS,EACf,MAAM,EAAE,OAAO,EAClC,cAAc,CAAC,EAAE,eAAe,EAChC,SAAS,CAAC,EAAE,MAAM;IAmBpB;;;;;OAKG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAQpD;;;;;OAKG;IACH,qBAAqB,IAAI,IAAI;IAI7B;;;;OAIG;IACG,sBAAsB,CAAC,iBAAiB,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB/E;;OAEG;IACH,cAAc,IAAI,WAAW,GAAG,UAAU;IAI1C;;;;OAIG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IASrC;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B;;;OAGG;IACG,iBAAiB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAcvE;;OAEG;IACH,iBAAiB,IAAI,eAAe,GAAG,IAAI;IAI3C;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBvC;;;OAGG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAyBvC;;;;OAIG;IACH,eAAe,IAAI,YAAY,GAAG,IAAI;IAYtC;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI;IAY1C;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBxC,SAAS,IAAI,SAAS;IAItB,KAAK,IAAI,IAAI;IAYP,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAevD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -404,12 +404,10 @@ class AbstractAbapConnection {
|
|
|
404
404
|
}
|
|
405
405
|
// Retry logic for CSRF token errors (403 with CSRF message)
|
|
406
406
|
if (this.shouldRetryCsrf(error)) {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
});
|
|
412
|
-
}
|
|
407
|
+
this.logger.debug("CSRF token validation failed, fetching new token and retrying request", {
|
|
408
|
+
url: requestUrl,
|
|
409
|
+
method: normalizedMethod
|
|
410
|
+
});
|
|
413
411
|
this.csrfToken = await this.fetchCsrfToken(requestUrl, 5, 2000);
|
|
414
412
|
if (this.csrfToken) {
|
|
415
413
|
requestHeaders["x-csrf-token"] = this.csrfToken;
|
|
@@ -478,13 +476,11 @@ class AbstractAbapConnection {
|
|
|
478
476
|
csrfUrl = `${base}${csrfConfig_js_1.CSRF_CONFIG.ENDPOINT}`;
|
|
479
477
|
}
|
|
480
478
|
// If URL already contains the endpoint, use it as is
|
|
481
|
-
|
|
482
|
-
this.logger.csrfToken("fetch", `Fetching CSRF token from: ${csrfUrl}`);
|
|
483
|
-
}
|
|
479
|
+
this.logger.debug(`Fetching CSRF token from: ${csrfUrl}`);
|
|
484
480
|
for (let attempt = 0; attempt <= retryCount; attempt++) {
|
|
485
481
|
try {
|
|
486
|
-
if (attempt > 0
|
|
487
|
-
this.logger.
|
|
482
|
+
if (attempt > 0) {
|
|
483
|
+
this.logger.debug(`Retry attempt ${attempt}/${retryCount} for CSRF token`);
|
|
488
484
|
}
|
|
489
485
|
const authHeaders = await this.getAuthHeaders();
|
|
490
486
|
const headers = {
|
|
@@ -511,12 +507,10 @@ class AbstractAbapConnection {
|
|
|
511
507
|
this.updateCookiesFromResponse(response.headers);
|
|
512
508
|
const token = response.headers["x-csrf-token"];
|
|
513
509
|
if (!token) {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
});
|
|
519
|
-
}
|
|
510
|
+
this.logger.error("No CSRF token in response headers", {
|
|
511
|
+
headers: response.headers,
|
|
512
|
+
status: response.status
|
|
513
|
+
});
|
|
520
514
|
if (attempt < retryCount) {
|
|
521
515
|
await new Promise((resolve) => setTimeout(resolve, retryDelay));
|
|
522
516
|
continue;
|
|
@@ -527,18 +521,14 @@ class AbstractAbapConnection {
|
|
|
527
521
|
this.updateCookiesFromResponse(response.headers);
|
|
528
522
|
if (this.cookies) {
|
|
529
523
|
this.logger.debug(`[DEBUG] BaseAbapConnection - Cookies received from CSRF response (first 100 chars): ${this.cookies.substring(0, 100)}...`);
|
|
530
|
-
|
|
531
|
-
this.
|
|
532
|
-
|
|
533
|
-
});
|
|
534
|
-
}
|
|
524
|
+
this.logger.debug("Cookies extracted from response", {
|
|
525
|
+
cookieLength: this.cookies.length
|
|
526
|
+
});
|
|
535
527
|
}
|
|
536
528
|
}
|
|
537
529
|
// Save session state after CSRF token fetch (cookies and token are now available)
|
|
538
530
|
await this.saveSessionState();
|
|
539
|
-
|
|
540
|
-
this.logger.csrfToken("success", "CSRF token successfully obtained");
|
|
541
|
-
}
|
|
531
|
+
this.logger.debug("CSRF token successfully obtained");
|
|
542
532
|
return token;
|
|
543
533
|
}
|
|
544
534
|
catch (error) {
|
|
@@ -554,18 +544,14 @@ class AbstractAbapConnection {
|
|
|
554
544
|
});
|
|
555
545
|
}
|
|
556
546
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
});
|
|
564
|
-
}
|
|
547
|
+
this.logger.error(`CSRF token error: ${error.message}`, {
|
|
548
|
+
url: csrfUrl,
|
|
549
|
+
status: error.response?.status,
|
|
550
|
+
attempt: attempt + 1,
|
|
551
|
+
maxAttempts: retryCount + 1
|
|
552
|
+
});
|
|
565
553
|
if (error.response?.status === 405 && error.response?.headers["x-csrf-token"]) {
|
|
566
|
-
|
|
567
|
-
this.logger.csrfToken("retry", "CSRF: SAP returned 405 (Method Not Allowed) — not critical, token found in header");
|
|
568
|
-
}
|
|
554
|
+
this.logger.debug("CSRF: SAP returned 405 (Method Not Allowed) — not critical, token found in header");
|
|
569
555
|
const token = error.response.headers["x-csrf-token"];
|
|
570
556
|
if (token) {
|
|
571
557
|
this.updateCookiesFromResponse(error.response.headers);
|
|
@@ -573,15 +559,13 @@ class AbstractAbapConnection {
|
|
|
573
559
|
}
|
|
574
560
|
}
|
|
575
561
|
if (error.response?.headers["x-csrf-token"]) {
|
|
576
|
-
|
|
577
|
-
this.logger.csrfToken("success", `Got CSRF token despite error (status: ${error.response?.status})`);
|
|
578
|
-
}
|
|
562
|
+
this.logger.debug(`Got CSRF token despite error (status: ${error.response?.status})`);
|
|
579
563
|
const token = error.response.headers["x-csrf-token"];
|
|
580
564
|
this.updateCookiesFromResponse(error.response.headers);
|
|
581
565
|
return token;
|
|
582
566
|
}
|
|
583
|
-
if (error.response
|
|
584
|
-
this.logger.
|
|
567
|
+
if (error.response) {
|
|
568
|
+
this.logger.error("CSRF error details", {
|
|
585
569
|
status: error.response.status,
|
|
586
570
|
statusText: error.response.statusText,
|
|
587
571
|
headers: Object.keys(error.response.headers),
|
|
@@ -590,14 +574,14 @@ class AbstractAbapConnection {
|
|
|
590
574
|
: JSON.stringify(error.response.data).slice(0, 200)
|
|
591
575
|
});
|
|
592
576
|
}
|
|
593
|
-
else if (error.request
|
|
594
|
-
this.logger.
|
|
577
|
+
else if (error.request) {
|
|
578
|
+
this.logger.error("CSRF request error - no response received", {
|
|
595
579
|
request: error.request.path
|
|
596
580
|
});
|
|
597
581
|
}
|
|
598
582
|
}
|
|
599
|
-
else
|
|
600
|
-
this.logger.
|
|
583
|
+
else {
|
|
584
|
+
this.logger.error("CSRF non-axios error", {
|
|
601
585
|
error: error instanceof Error ? error.message : String(error)
|
|
602
586
|
});
|
|
603
587
|
}
|
|
@@ -678,9 +662,7 @@ class AbstractAbapConnection {
|
|
|
678
662
|
const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === "1" ||
|
|
679
663
|
(process.env.TLS_REJECT_UNAUTHORIZED === "1" &&
|
|
680
664
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0");
|
|
681
|
-
|
|
682
|
-
this.logger.tlsConfig(rejectUnauthorized);
|
|
683
|
-
}
|
|
665
|
+
this.logger.debug(`TLS configuration: rejectUnauthorized=${rejectUnauthorized}`);
|
|
684
666
|
this.axiosInstance = axios_1.default.create({
|
|
685
667
|
httpsAgent: new https_1.Agent({
|
|
686
668
|
rejectUnauthorized
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,67 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
export interface ILogger {
|
|
6
|
-
/**
|
|
7
|
-
* Log informational message
|
|
8
|
-
*/
|
|
9
|
-
info(message: string, meta?: any): void;
|
|
10
|
-
/**
|
|
11
|
-
* Log error message
|
|
12
|
-
*/
|
|
13
|
-
error(message: string, meta?: any): void;
|
|
14
|
-
/**
|
|
15
|
-
* Log warning message
|
|
16
|
-
*/
|
|
17
|
-
warn(message: string, meta?: any): void;
|
|
18
|
-
/**
|
|
19
|
-
* Log debug message
|
|
20
|
-
*/
|
|
21
|
-
debug(message: string, meta?: any): void;
|
|
22
|
-
/**
|
|
23
|
-
* Log CSRF token operations
|
|
24
|
-
* @param action - Type of CSRF operation: "fetch", "retry", "success", or "error"
|
|
25
|
-
* @param message - Log message
|
|
26
|
-
* @param meta - Additional metadata
|
|
27
|
-
*/
|
|
28
|
-
csrfToken?(action: "fetch" | "retry" | "success" | "error", message: string, meta?: any): void;
|
|
29
|
-
/**
|
|
30
|
-
* Log TLS configuration
|
|
31
|
-
* @param rejectUnauthorized - Whether TLS certificate validation is enabled
|
|
32
|
-
*/
|
|
33
|
-
tlsConfig?(rejectUnauthorized: boolean): void;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Session state interface for stateful connections
|
|
37
|
-
* Contains cookies and CSRF token that need to be preserved across requests
|
|
38
|
-
*/
|
|
39
|
-
export interface SessionState {
|
|
40
|
-
cookies: string | null;
|
|
41
|
-
csrfToken: string | null;
|
|
42
|
-
cookieStore: Record<string, string>;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Interface for storing and retrieving session state
|
|
46
|
-
* Allows connection layer to persist session state (cookies, CSRF token) externally
|
|
47
|
-
*/
|
|
48
|
-
export interface ISessionStorage {
|
|
49
|
-
/**
|
|
50
|
-
* Save session state for a given session ID
|
|
51
|
-
* @param sessionId - Unique session identifier
|
|
52
|
-
* @param state - Session state to save
|
|
53
|
-
*/
|
|
54
|
-
save(sessionId: string, state: SessionState): Promise<void>;
|
|
55
|
-
/**
|
|
56
|
-
* Load session state for a given session ID
|
|
57
|
-
* @param sessionId - Unique session identifier
|
|
58
|
-
* @returns Session state or null if not found
|
|
59
|
-
*/
|
|
60
|
-
load(sessionId: string): Promise<SessionState | null>;
|
|
61
|
-
/**
|
|
62
|
-
* Delete session state for a given session ID
|
|
63
|
-
* @param sessionId - Unique session identifier
|
|
64
|
-
*/
|
|
65
|
-
delete(sessionId: string): Promise<void>;
|
|
66
|
-
}
|
|
1
|
+
import type { ILogger, ISessionStorage, ISessionState } from '@mcp-abap-adt/interfaces';
|
|
2
|
+
export type { ILogger, ISessionStorage };
|
|
3
|
+
export type SessionState = ISessionState;
|
|
67
4
|
//# sourceMappingURL=logger.d.ts.map
|
package/dist/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAGxF,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC;AACzC,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC"}
|
package/dist/utils/timeouts.d.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
long: number;
|
|
5
|
-
}
|
|
6
|
-
export declare function getTimeoutConfig(): TimeoutConfig;
|
|
1
|
+
import type { ITimeoutConfig } from '@mcp-abap-adt/interfaces';
|
|
2
|
+
export type TimeoutConfig = ITimeoutConfig;
|
|
3
|
+
export declare function getTimeoutConfig(): ITimeoutConfig;
|
|
7
4
|
export declare function getTimeout(type?: "default" | "csrf" | "long" | number): number;
|
|
8
5
|
//# sourceMappingURL=timeouts.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeouts.d.ts","sourceRoot":"","sources":["../../src/utils/timeouts.ts"],"names":[],"mappings":"AAAA,
|
|
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,CAUjD;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,MAAkB,GAAG,MAAM,CAOzF"}
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Token refresh utilities for JWT authentication
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
refreshToken?: string;
|
|
7
|
-
}
|
|
4
|
+
import type { ITokenRefreshResult } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
export type TokenRefreshResult = ITokenRefreshResult;
|
|
8
6
|
/**
|
|
9
7
|
* Refreshes the access token using refresh token
|
|
10
8
|
* @param refreshToken Refresh token
|
|
@@ -13,5 +11,5 @@ export interface TokenRefreshResult {
|
|
|
13
11
|
* @param clientSecret UAA client secret
|
|
14
12
|
* @returns Promise that resolves to new tokens
|
|
15
13
|
*/
|
|
16
|
-
export declare function refreshJwtToken(refreshToken: string, uaaUrl: string, clientId: string, clientSecret: string): Promise<
|
|
14
|
+
export declare function refreshJwtToken(refreshToken: string, uaaUrl: string, clientId: string, clientSecret: string): Promise<ITokenRefreshResult>;
|
|
17
15
|
//# sourceMappingURL=tokenRefresh.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../../src/utils/tokenRefresh.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"tokenRefresh.d.ts","sourceRoot":"","sources":["../../src/utils/tokenRefresh.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGpE,MAAM,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAErD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,mBAAmB,CAAC,CAqC9B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/connection",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "ABAP connection layer for MCP ABAP ADT server",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"node": ">=18.0.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
+
"@mcp-abap-adt/interfaces": "^0.1.1",
|
|
46
47
|
"axios": "^1.11.0",
|
|
47
48
|
"commander": "^14.0.2",
|
|
48
49
|
"express": "^5.1.0",
|