@mcp-abap-adt/connection 1.10.2 → 2.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 (44) hide show
  1. package/CHANGELOG.md +758 -0
  2. package/README.md +42 -11
  3. package/dist/__tests__/helpers/session.d.ts +15 -0
  4. package/dist/__tests__/helpers/session.d.ts.map +1 -0
  5. package/dist/__tests__/helpers/session.js +19 -0
  6. package/dist/auth/ntlm.d.ts +15 -0
  7. package/dist/auth/ntlm.d.ts.map +1 -1
  8. package/dist/auth/ntlm.js +38 -0
  9. package/dist/connection/AbstractAbapConnection.d.ts +163 -11
  10. package/dist/connection/AbstractAbapConnection.d.ts.map +1 -1
  11. package/dist/connection/AbstractAbapConnection.js +351 -14
  12. package/dist/connection/BaseAbapConnection.d.ts +5 -1
  13. package/dist/connection/BaseAbapConnection.d.ts.map +1 -1
  14. package/dist/connection/BaseAbapConnection.js +11 -1
  15. package/dist/connection/CertificateAbapConnection.d.ts +5 -1
  16. package/dist/connection/CertificateAbapConnection.d.ts.map +1 -1
  17. package/dist/connection/CertificateAbapConnection.js +11 -1
  18. package/dist/connection/JwtAbapConnection.d.ts +3 -2
  19. package/dist/connection/JwtAbapConnection.d.ts.map +1 -1
  20. package/dist/connection/JwtAbapConnection.js +19 -8
  21. package/dist/connection/KerberosAbapConnection.d.ts +5 -1
  22. package/dist/connection/KerberosAbapConnection.d.ts.map +1 -1
  23. package/dist/connection/KerberosAbapConnection.js +54 -3
  24. package/dist/connection/SamlAbapConnection.d.ts +5 -1
  25. package/dist/connection/SamlAbapConnection.d.ts.map +1 -1
  26. package/dist/connection/SamlAbapConnection.js +11 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -0
  29. package/dist/session/SessionLifecycle.d.ts +2 -9
  30. package/dist/session/SessionLifecycle.d.ts.map +1 -1
  31. package/dist/session/SessionLifecycle.js +8 -8
  32. package/docs/INDEX.md +106 -0
  33. package/docs/INSTALLATION.md +304 -0
  34. package/docs/JWT_AUTH_TOOLS.md +142 -0
  35. package/docs/MIGRATION-2.0.md +125 -0
  36. package/docs/SCOPE.md +44 -0
  37. package/docs/STATEFUL_SESSION_GUIDE.md +121 -0
  38. package/docs/USAGE.md +749 -0
  39. package/examples/README.md +112 -0
  40. package/examples/basic-connection.js +55 -0
  41. package/examples/jwt-with-token-refresh.js +87 -0
  42. package/examples/saml-connection.js +52 -0
  43. package/examples/websocket-transport.js +87 -0
  44. package/package.json +11 -4
@@ -52,9 +52,10 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
52
52
  }
53
53
  }
54
54
  /**
55
- * Override connect to handle JWT token refresh on errors
55
+ * Establishes the session for this auth type. Called by
56
+ * AbstractAbapConnection.connect(), which owns the lifecycle around it.
56
57
  */
57
- async connect() {
58
+ async establishSession() {
58
59
  const baseUrl = await this.getBaseUrl();
59
60
  const discoveryUrl = `${baseUrl}/sap/bc/adt/discovery`;
60
61
  this.logger?.debug(`[DEBUG] JwtAbapConnection - Connecting to SAP system: ${discoveryUrl}`);
@@ -85,9 +86,11 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
85
86
  }
86
87
  // Try to refresh token if tokenRefresher is available
87
88
  if (await this.tryRefreshToken()) {
88
- // Retry connect with new token
89
- this.logger?.debug(`[DEBUG] JwtAbapConnection - Retrying connect after token refresh...`);
90
- return this.connect();
89
+ // Retry the ESTABLISHMENT, not connect(): connect() runs this as a
90
+ // joinable transition, so a nested call joins the transition already
91
+ // in flight — this one — and waits for itself forever.
92
+ this.logger?.debug(`[DEBUG] JwtAbapConnection - Retrying establishment after token refresh...`);
93
+ return this.establishSession();
91
94
  }
92
95
  throw new Error('JWT token has expired. Please re-authenticate.');
93
96
  }
@@ -99,6 +102,9 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
99
102
  * Override makeAdtRequest to handle JWT auth errors with automatic token refresh
100
103
  */
101
104
  async makeAdtRequest(options) {
105
+ // Captured before the attempt: a recovery asks "has the caller asked to
106
+ // stop since this request began", not since some later bookkeeping step.
107
+ const baselineEpoch = this.teardownEpoch;
102
108
  this.logger?.debug(`[DEBUG] JwtAbapConnection.makeAdtRequest - Starting request: ${options.method} ${options.url}`);
103
109
  try {
104
110
  const response = await super.makeAdtRequest(options);
@@ -124,9 +130,14 @@ class JwtAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnecti
124
130
  }
125
131
  // Try to refresh token if tokenRefresher is available
126
132
  if (await this.tryRefreshToken()) {
127
- // Reset connection state and retry request with new token
128
- this.logger?.debug(`[DEBUG] JwtAbapConnection.makeAdtRequest - Retrying request after token refresh...`);
129
- this.reset();
133
+ this.logger?.debug(`[DEBUG] JwtAbapConnection.makeAdtRequest - Recovering session after token refresh...`);
134
+ // The renewed credential cannot keep the old ABAP session, so this is
135
+ // a session-lost teardown — internal, or it would cancel the very
136
+ // recovery it is setting up. reset() would be the caller-origin one.
137
+ this.discardSession();
138
+ // Re-establish before retrying: the retry goes through admission, and
139
+ // a discarded session admits nothing.
140
+ await this.recoverSession(baselineEpoch);
130
141
  return super.makeAdtRequest(options);
131
142
  }
132
143
  throw new Error('JWT token has expired. Please re-authenticate.');
@@ -14,7 +14,11 @@ export declare class KerberosAbapConnection extends AbstractAbapConnection {
14
14
  * request — the first request carries the Negotiate header; SAP then issues a session
15
15
  * cookie which is reused for subsequent requests.
16
16
  */
17
- connect(): Promise<void>;
17
+ /**
18
+ * Establishes the session for this auth type. Called by
19
+ * AbstractAbapConnection.connect(), which owns the lifecycle around it.
20
+ */
21
+ protected establishSession(): Promise<void>;
18
22
  protected buildAuthorizationHeader(): string;
19
23
  }
20
24
  //# sourceMappingURL=KerberosAbapConnection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"KerberosAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/KerberosAbapConnection.ts"],"names":[],"mappings":"AAGA,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,uGAAuG;AACvG,qBAAa,sBAAuB,SAAQ,sBAAsB;IAChE,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAAM;gBAEd,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;IAQ1E,OAAO,CAAC,MAAM,CAAC,cAAc;IAa7B,mDAAmD;cACnC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC9B,SAAS,CAAC,wBAAwB,IAAI,MAAM;CAS7C"}
1
+ {"version":3,"file":"KerberosAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/KerberosAbapConnection.ts"],"names":[],"mappings":"AAOA,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,uGAAuG;AACvG,qBAAa,sBAAuB,SAAQ,sBAAsB;IAChE,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAAM;gBAEd,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;IAQ1E,OAAO,CAAC,MAAM,CAAC,cAAc;IAa7B,mDAAmD;cACnC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C;;;;OAIG;IACH;;;OAGG;cACa,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAqFjD,SAAS,CAAC,wBAAwB,IAAI,MAAM;CAS7C"}
@@ -34,16 +34,38 @@ class KerberosAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapCon
34
34
  * request — the first request carries the Negotiate header; SAP then issues a session
35
35
  * cookie which is reused for subsequent requests.
36
36
  */
37
- async connect() {
37
+ /**
38
+ * Establishes the session for this auth type. Called by
39
+ * AbstractAbapConnection.connect(), which owns the lifecycle around it.
40
+ */
41
+ async establishSession() {
38
42
  await this.ensureToken();
39
43
  const baseUrl = await this.getBaseUrl();
40
44
  const discoveryUrl = `${baseUrl}/sap/bc/adt/discovery`;
41
45
  this.logger?.debug(`[DEBUG] KerberosAbapConnection - Connecting to SAP system: ${discoveryUrl}`);
42
46
  try {
43
- const token = await this.fetchCsrfToken(discoveryUrl, 3, 1000);
47
+ // ONE attempt, deliberately — the shared default is 3 retries a second
48
+ // apart, and every one of them corrupts this exchange:
49
+ //
50
+ // - A GSS token is one-shot. Replaying it is meaningless at best, and
51
+ // replay protection may answer it differently than the original.
52
+ // - Worse, a 401 that sets a cookie silences buildAuthorizationHeader()
53
+ // (a cookie carries auth after the first round trip), so the retry
54
+ // goes out with NO Authorization at all and draws the server's plain
55
+ // opening challenge.
56
+ //
57
+ // Either way the classifier below would read the LAST response and name
58
+ // the wrong cause. The retry cannot help here anyway: nothing about a
59
+ // rejected token changes by waiting a second. Retrying a transient
60
+ // network failure is the caller's business now that connect() is
61
+ // explicit, and a fresh connect() mints a fresh token.
62
+ const token = await this.fetchCsrfToken(discoveryUrl, 0, 0);
44
63
  this.setCsrfToken(token);
45
64
  }
46
65
  catch (error) {
66
+ // The token went out and was not accepted, so it is spent. Keeping it
67
+ // would make every later connect() replay a token already known to fail.
68
+ this.currentToken = '';
47
69
  if (error instanceof axios_1.AxiosError && error.response?.headers) {
48
70
  const wwwAuth = error.response.headers['www-authenticate'];
49
71
  if ((0, ntlm_js_1.isNtlmChallenge)(wwwAuth)) {
@@ -53,8 +75,37 @@ class KerberosAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapCon
53
75
  if (this.getCookies()) {
54
76
  this.logger?.debug('[DEBUG] KerberosAbapConnection - cookies captured from error response during connect');
55
77
  }
78
+ // A 401 carrying a Negotiate token is a CONTINUATION, not a success.
79
+ //
80
+ // Per RFC 4559 the server is handing back gssapi-data for the client to
81
+ // feed into its GSS context and retry. This client cannot do that:
82
+ // generateSpnegoToken() calls step('') once and discards the context,
83
+ // so there is nothing to continue with. The token it holds is the
84
+ // initial one, and sending it again produces the same 401.
85
+ //
86
+ // So this fails, and says why — resolving here would claim a readiness
87
+ // that does not exist and move the failure to the first real request,
88
+ // where it reads as an unrelated auth error.
89
+ if ((0, ntlm_js_1.isNegotiateContinuation)(wwwAuth)) {
90
+ throw new Error('KerberosAbapConnection: the server continued the SPNEGO exchange ' +
91
+ '(401 with a Negotiate token), which requires feeding that token ' +
92
+ 'back into the GSS context and retrying. This client performs a ' +
93
+ 'single-leg exchange only — the context is discarded after the ' +
94
+ 'first step — so multi-leg SPNEGO is not supported. See ' +
95
+ 'https://www.rfc-editor.org/rfc/rfc4559');
96
+ }
97
+ // A BARE Negotiate is a different problem with a different fix: the
98
+ // initial Authorization has already gone out, so an empty challenge is
99
+ // the server declining it. No token came back, so there is nothing to
100
+ // step even for a client that supports multi-leg.
101
+ if ((0, ntlm_js_1.isBareNegotiateChallenge)(wwwAuth)) {
102
+ throw new Error('KerberosAbapConnection: the server rejected the SPNEGO token ' +
103
+ '(401 with a bare Negotiate challenge, no token returned). The ' +
104
+ 'credentials were not accepted — check the SPN, the ticket ' +
105
+ '(klist / kinit), and that the user is permitted Kerberos logon.');
106
+ }
56
107
  }
57
- this.logger?.warn(`[WARN] KerberosAbapConnection - connect deferred: ${error instanceof Error ? error.message : String(error)}`);
108
+ throw error;
58
109
  }
59
110
  }
60
111
  buildAuthorizationHeader() {
@@ -13,7 +13,11 @@ export declare class SamlAbapConnection extends AbstractAbapConnection {
13
13
  * Connect to SAP system using existing session cookies
14
14
  * Fetches CSRF token to establish session context
15
15
  */
16
- connect(): Promise<void>;
16
+ /**
17
+ * Establishes the session for this auth type. Called by
18
+ * AbstractAbapConnection.connect(), which owns the lifecycle around it.
19
+ */
20
+ protected establishSession(): Promise<void>;
17
21
  protected buildAuthorizationHeader(): string;
18
22
  getAuthHeaders(): Promise<Record<string, string>>;
19
23
  private static validateConfig;
@@ -1 +1 @@
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;;GAEG;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;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAiC9B,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
+ {"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;;GAEG;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"}
@@ -20,7 +20,11 @@ class SamlAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnect
20
20
  * Connect to SAP system using existing session cookies
21
21
  * Fetches CSRF token to establish session context
22
22
  */
23
- async connect() {
23
+ /**
24
+ * Establishes the session for this auth type. Called by
25
+ * AbstractAbapConnection.connect(), which owns the lifecycle around it.
26
+ */
27
+ async establishSession() {
24
28
  const baseUrl = await this.getBaseUrl();
25
29
  const discoveryUrl = `${baseUrl}/sap/bc/adt/discovery`;
26
30
  this.logger?.debug(`[DEBUG] SamlAbapConnection - Connecting to SAP system: ${discoveryUrl}`);
@@ -41,6 +45,12 @@ class SamlAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnect
41
45
  this.logger?.debug(`[DEBUG] SamlAbapConnection - Cookies extracted from error response during connect (first 100 chars): ${this.getCookies()?.substring(0, 100)}...`);
42
46
  }
43
47
  }
48
+ // Rethrow: a resolved connect() must mean a usable session exists. This
49
+ // used to swallow and resolve anyway, deferring establishment to the
50
+ // first request — coherent only while that lazy path existed. Without it,
51
+ // swallowing would leave a connection that reports success, holds
52
+ // nothing, and refuses every request.
53
+ throw error;
44
54
  }
45
55
  }
46
56
  buildAuthorizationHeader() {
@@ -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;AAGxC,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;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"}
package/dist/index.js CHANGED
@@ -7,6 +7,10 @@ Object.defineProperty(exports, "FileCertificateMaterialLoader", { enumerable: tr
7
7
  // Config utilities
8
8
  var sapConfig_js_1 = require("./config/sapConfig.js");
9
9
  Object.defineProperty(exports, "sapConfigSignature", { enumerable: true, get: function () { return sapConfig_js_1.sapConfigSignature; } });
10
+ // The session lifecycle vocabulary — ISessionLifecycleAware, ILockWindowAware,
11
+ // ITeardownReport, WindowToken, ADT_SESSION_ERROR — is deliberately NOT exported
12
+ // here. It lives in @mcp-abap-adt/interfaces, and a consumer imports it from
13
+ // there: re-exporting a contract type gives it two names and lets the two drift.
10
14
  // Connection classes - only final implementations
11
15
  // Deprecated aliases for backward compatibility
12
16
  var BaseAbapConnection_js_1 = require("./connection/BaseAbapConnection.js");
@@ -10,8 +10,7 @@
10
10
  * Design: docs/superpowers/specs/2026-07-27-session-lifecycle-design.md in
11
11
  * @mcp-abap-adt/adt-clients.
12
12
  */
13
- /** Opaque handle for one open lock window: `Symbol(label)`. */
14
- export type WindowToken = symbol;
13
+ import { type AdtSessionErrorCode, type WindowToken } from '@mcp-abap-adt/interfaces';
15
14
  export type TransitionKind = 'connect' | 'disconnect' | 'recover' | 'cleanup';
16
15
  export interface RequestLease {
17
16
  /** Teardown epoch at admission — the baseline a recovery compares against. */
@@ -29,12 +28,6 @@ export interface BeginTeardownOptions {
29
28
  /** Decides admission: a lost session cannot let an open window finish. */
30
29
  sessionLost: boolean;
31
30
  }
32
- export declare const ADT_SESSION_ERROR: {
33
- readonly NOT_CONNECTED: "ADT_NOT_CONNECTED";
34
- readonly SESSION_REPLACED: "ADT_SESSION_REPLACED";
35
- readonly RELEASE_PENDING: "ADT_RELEASE_PENDING";
36
- };
37
- export type AdtSessionErrorCode = (typeof ADT_SESSION_ERROR)[keyof typeof ADT_SESSION_ERROR];
38
31
  export declare function sessionError(code: AdtSessionErrorCode, message?: string): Error & {
39
32
  code: AdtSessionErrorCode;
40
33
  };
@@ -131,7 +124,7 @@ export declare class SessionLifecycle {
131
124
  * internal cleanup owes its result to nobody while a caller's teardown owes a
132
125
  * report.
133
126
  */
134
- transition(kind: TransitionKind, run: () => Promise<void>): Promise<void>;
127
+ transition<T>(kind: TransitionKind, run: () => Promise<T>): Promise<T>;
135
128
  private wake;
136
129
  private changed;
137
130
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SessionLifecycle.d.ts","sourceRoot":"","sources":["../../src/session/SessionLifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SessionLifecycle.d.ts","sourceRoot":"","sources":["../../src/session/SessionLifecycle.ts"],"names":[],"mappings":"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"}
@@ -12,13 +12,9 @@
12
12
  * @mcp-abap-adt/adt-clients.
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.SessionLifecycle = exports.ADT_SESSION_ERROR = void 0;
15
+ exports.SessionLifecycle = void 0;
16
16
  exports.sessionError = sessionError;
17
- exports.ADT_SESSION_ERROR = {
18
- NOT_CONNECTED: 'ADT_NOT_CONNECTED',
19
- SESSION_REPLACED: 'ADT_SESSION_REPLACED',
20
- RELEASE_PENDING: 'ADT_RELEASE_PENDING',
21
- };
17
+ const interfaces_1 = require("@mcp-abap-adt/interfaces");
22
18
  function sessionError(code, message) {
23
19
  const error = new Error(message ?? code);
24
20
  error.code = code;
@@ -134,7 +130,7 @@ class SessionLifecycle {
134
130
  }
135
131
  assertUsable() {
136
132
  if (!this.admits) {
137
- throw sessionError(exports.ADT_SESSION_ERROR.NOT_CONNECTED);
133
+ throw sessionError(interfaces_1.ADT_SESSION_ERROR.NOT_CONNECTED);
138
134
  }
139
135
  }
140
136
  /** Asserts usability and counts the request in, in one synchronous step. */
@@ -161,7 +157,7 @@ class SessionLifecycle {
161
157
  */
162
158
  beginWindow(label) {
163
159
  if (this.state !== 'connected' || this.teardownPending) {
164
- throw sessionError(exports.ADT_SESSION_ERROR.NOT_CONNECTED);
160
+ throw sessionError(interfaces_1.ADT_SESSION_ERROR.NOT_CONNECTED);
165
161
  }
166
162
  const token = Symbol(label);
167
163
  this.windows.set(token, { label, abandoned: false });
@@ -258,6 +254,10 @@ class SessionLifecycle {
258
254
  transition(kind, run) {
259
255
  const joinable = kind === 'connect' || kind === 'disconnect';
260
256
  if (joinable && this.tailKind === kind && this.tailPromise) {
257
+ // Joining shares the ANSWER, not just the execution: a joiner that got a
258
+ // resolved promise carrying nothing would have to invent a result, and
259
+ // for a teardown that means reporting no abandoned locks when there were
260
+ // some. Callers of one kind ask the same question, so they get one reply.
261
261
  return this.tailPromise;
262
262
  }
263
263
  const queued = this.tail.then(run, run);
package/docs/INDEX.md ADDED
@@ -0,0 +1,106 @@
1
+ # Documentation Index
2
+
3
+ **Package:** `@mcp-abap-adt/connection`
4
+ **Version:** see [CHANGELOG.md](../CHANGELOG.md) — kept there rather than
5
+ duplicated here, where it went stale by eight minor versions.
6
+
7
+ ## Package Structure
8
+
9
+ ```
10
+ mcp-abap-connection/
11
+ ├── README.md # Main package documentation
12
+ ├── CHANGELOG.md # Version history and changes
13
+ ├── docs/ # Detailed documentation
14
+ │ ├── INDEX.md # This file - documentation overview
15
+ │ ├── INSTALLATION.md # Setup and installation guide
16
+ │ ├── USAGE.md # API documentation and examples
17
+ │ ├── MIGRATION-2.0.md # Moving to the explicit session lifecycle
18
+ │ ├── SCOPE.md # What this package does and does not own
19
+ │ ├── STATEFUL_SESSION_GUIDE.md # Stateful requests and lock windows
20
+ │ └── JWT_AUTH_TOOLS.md # CLI tool for authentication
21
+ ├── examples/ # Working code examples
22
+ │ ├── README.md # Examples overview
23
+ │ └── basic-connection.js # Simple connection example
24
+ ├── bin/ # CLI tools
25
+ │ └── sap-abap-auth.js # JWT authentication CLI
26
+ └── src/ # Source code
27
+ ├── connection/ # Connection classes
28
+ ├── config/ # Configuration utilities
29
+ ├── utils/ # Helper functions
30
+ └── __tests__/ # Unit tests
31
+ ```
32
+
33
+ ## Quick Links
34
+
35
+ ### Getting Started
36
+ - 📦 [Installation Guide](./INSTALLATION.md) - How to install and set up the package
37
+ - 📚 [Usage Guide](./USAGE.md) - Basic usage and comprehensive API documentation
38
+ - 📖 [Main README](../README.md) - Package overview and quick start
39
+ - 🧭 [Scope and Boundaries](./SCOPE.md) - What this package does (and does not), sibling packages, and why there is no RFC to cloud
40
+
41
+ ### Core Features
42
+ - 🔑 [JWT Auth Tools](./JWT_AUTH_TOOLS.md) - CLI tool for browser-based authentication
43
+
44
+ ### Version Information
45
+ - 📋 [CHANGELOG](../CHANGELOG.md) - Complete version history, including what the latest release changed
46
+
47
+ ### Examples
48
+ - 📁 [Examples Overview](../examples/README.md) - All available examples
49
+ - 🔌 [Basic Connection](../examples/basic-connection.js) - Simple connection setup
50
+
51
+ ## Documentation by Topic
52
+
53
+ ### Authentication
54
+ - **Basic Auth**: [USAGE.md - Basic Authentication](./USAGE.md#basic-authentication-on-premise)
55
+ - **JWT/OAuth2**: [USAGE.md - JWT Authentication](./USAGE.md#jwt-authentication-cloudbtp)
56
+ - **Token Refresh**: Handled by `@mcp-abap-adt/auth-broker` package (removed in 0.2.0)
57
+ - **CLI Tool**: [JWT_AUTH_TOOLS.md](./JWT_AUTH_TOOLS.md)
58
+
59
+ ### Session Management
60
+ - **Overview**: [USAGE.md - Session Management](./USAGE.md#session-management)
61
+ - **Stateful Mode**: Use `setSessionType('stateful')` for session headers
62
+ - **Session State Persistence**: Handled by `@mcp-abap-adt/auth-broker` package
63
+ - **API Methods**:
64
+ - `getSessionId()` - Get current session ID (auto-generated UUID)
65
+ - `setSessionType()` - Switch between stateful/stateless modes
66
+
67
+ ### API Reference
68
+ - **Connection Interface**: [USAGE.md - API Reference](./USAGE.md#api-reference)
69
+ - **Configuration Types**: [USAGE.md - Configuration Types](./USAGE.md#configuration-types)
70
+ - **Factory Function**: `createAbapConnection()`
71
+ - **Connection Classes**: `BaseAbapConnection`, `JwtAbapConnection`
72
+
73
+ ## Version Highlights
74
+
75
+ See [CHANGELOG.md](../CHANGELOG.md). This section used to restate it and drifted
76
+ eight minor versions behind — a second copy of a changelog is a changelog that
77
+ is wrong.
78
+
79
+ ## Documentation Standards
80
+
81
+ ### File Organization
82
+ - **README.md** - Package overview, quick start, basic API
83
+ - **CHANGELOG.md** - All changes, following [Keep a Changelog](https://keepachangelog.com/)
84
+ - **docs/** - Detailed documentation, tutorials, guides
85
+ - **examples/** - Working code examples with README
86
+
87
+ ### Naming Conventions
88
+ - `UPPERCASE.md` - Main documentation files (README, CHANGELOG)
89
+ - `PascalCase.md` - Detailed guides in docs/ folder
90
+ - `kebab-case.js` - Example files
91
+
92
+ ### Content Guidelines
93
+ - Keep README concise, link to detailed docs
94
+ - Include working code examples
95
+ - Document environment variables and configuration
96
+ - Provide troubleshooting sections
97
+ - Show both success and error handling
98
+
99
+ ## Contributing Documentation
100
+
101
+ When adding new features:
102
+ 1. Update CHANGELOG.md with changes
103
+ 2. Add usage examples to USAGE.md
104
+ 3. Create working examples in examples/
105
+ 4. Update README.md if API changes
106
+ 5. Add troubleshooting to relevant guide