@mcp-abap-adt/adt-clients 3.11.3 → 3.12.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.
@@ -51,9 +51,9 @@ function buildBatchPayload(parts, boundary = createBatchBoundary()) {
51
51
  innerRequest,
52
52
  ].join('\r\n');
53
53
  })
54
- .join('');
54
+ .join('\r\n');
55
55
  return {
56
56
  boundary,
57
- body: `${multipartParts}--${boundary}--\r\n`,
57
+ body: `${multipartParts}\r\n--${boundary}--\r\n`,
58
58
  };
59
59
  }
@@ -7,6 +7,7 @@ export declare function clearAcceptCache(): void;
7
7
  export declare function setAcceptCorrectionEnabled(enabled?: boolean): void;
8
8
  export declare function getAcceptCorrectionEnabled(): boolean;
9
9
  export declare function extractSupportedAccept(error: unknown): string[];
10
+ export declare function extractSupportedContentType(error: unknown): string[];
10
11
  export declare function wrapConnectionAcceptNegotiation(connection: IAbapConnection, logger?: ILogger): void;
11
12
  export declare function makeAdtRequestWithAcceptNegotiation<T = unknown, D = unknown>(connection: IAbapConnection, request: IAbapRequestOptions, options?: IAcceptNegotiationOptions): Promise<IAdtResponse<T, D>>;
12
13
  //# sourceMappingURL=acceptNegotiation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"acceptNegotiation.d.ts","sourceRoot":"","sources":["../../src/utils/acceptNegotiation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,OAAO,EACR,MAAM,0BAA0B,CAAC;AASlC,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAElE;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAKpD;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAoC/D;AAcD,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,eAAe,EAC3B,MAAM,CAAC,EAAE,OAAO,GACf,IAAI,CAkBN;AAED,wBAAsB,mCAAmC,CACvD,CAAC,GAAG,OAAO,EACX,CAAC,GAAG,OAAO,EAEX,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,yBAAyB,GAClC,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CA8C7B"}
1
+ {"version":3,"file":"acceptNegotiation.d.ts","sourceRoot":"","sources":["../../src/utils/acceptNegotiation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,OAAO,EACR,MAAM,0BAA0B,CAAC;AAUlC,MAAM,WAAW,yBAAyB;IACxC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAGvC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAElE;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAKpD;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAoC/D;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAsCpE;AAcD,wBAAgB,+BAA+B,CAC7C,UAAU,EAAE,eAAe,EAC3B,MAAM,CAAC,EAAE,OAAO,GACf,IAAI,CAkBN;AAED,wBAAsB,mCAAmC,CACvD,CAAC,GAAG,OAAO,EACX,CAAC,GAAG,OAAO,EAEX,UAAU,EAAE,eAAe,EAC3B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,CAAC,EAAE,yBAAyB,GAClC,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CA6E7B"}
@@ -4,13 +4,16 @@ exports.clearAcceptCache = clearAcceptCache;
4
4
  exports.setAcceptCorrectionEnabled = setAcceptCorrectionEnabled;
5
5
  exports.getAcceptCorrectionEnabled = getAcceptCorrectionEnabled;
6
6
  exports.extractSupportedAccept = extractSupportedAccept;
7
+ exports.extractSupportedContentType = extractSupportedContentType;
7
8
  exports.wrapConnectionAcceptNegotiation = wrapConnectionAcceptNegotiation;
8
9
  exports.makeAdtRequestWithAcceptNegotiation = makeAdtRequestWithAcceptNegotiation;
9
10
  const acceptCache = new Map();
11
+ const contentTypeCache = new Map();
10
12
  const baseRequestMap = new WeakMap();
11
13
  let acceptCorrectionOverride;
12
14
  function clearAcceptCache() {
13
15
  acceptCache.clear();
16
+ contentTypeCache.clear();
14
17
  }
15
18
  function setAcceptCorrectionEnabled(enabled) {
16
19
  acceptCorrectionOverride = enabled;
@@ -19,7 +22,7 @@ function getAcceptCorrectionEnabled() {
19
22
  if (acceptCorrectionOverride !== undefined) {
20
23
  return acceptCorrectionOverride;
21
24
  }
22
- return process.env.ADT_ACCEPT_CORRECTION === 'true';
25
+ return process.env.ADT_ACCEPT_CORRECTION !== 'false';
23
26
  }
24
27
  function extractSupportedAccept(error) {
25
28
  const types = new Set();
@@ -53,6 +56,39 @@ function extractSupportedAccept(error) {
53
56
  }
54
57
  return Array.from(types).filter(Boolean);
55
58
  }
59
+ function extractSupportedContentType(error) {
60
+ const e = error;
61
+ if (e?.response?.status !== 415) {
62
+ return [];
63
+ }
64
+ const types = new Set();
65
+ const headers = (e?.response?.headers || {});
66
+ const headerCandidates = [
67
+ headers['content-type'],
68
+ headers['x-sap-adt-supported-content-type'],
69
+ headers['supported-content-type'],
70
+ ];
71
+ for (const value of headerCandidates) {
72
+ if (typeof value === 'string') {
73
+ value
74
+ .split(',')
75
+ .map((entry) => entry.trim())
76
+ .filter(Boolean)
77
+ .forEach((entry) => {
78
+ types.add(entry);
79
+ });
80
+ }
81
+ }
82
+ const data = e?.response?.data;
83
+ const text = typeof data === 'string' ? data : data ? JSON.stringify(data) : '';
84
+ if (text) {
85
+ const matches = text.match(/[a-zA-Z0-9.+-]+\/[a-zA-Z0-9.+-]+(?:;[^,\s]+)?/g) || [];
86
+ for (const match of matches) {
87
+ types.add(match);
88
+ }
89
+ }
90
+ return Array.from(types).filter(Boolean);
91
+ }
56
92
  function buildCacheKey(request) {
57
93
  return `${request.method.toUpperCase()} ${request.url}`;
58
94
  }
@@ -78,6 +114,12 @@ async function makeAdtRequestWithAcceptNegotiation(connection, request, options)
78
114
  if (cachedAccept) {
79
115
  headers.Accept = cachedAccept;
80
116
  }
117
+ const cachedContentType = enableCorrection
118
+ ? contentTypeCache.get(cacheKey)
119
+ : undefined;
120
+ if (cachedContentType) {
121
+ headers['Content-Type'] = cachedContentType;
122
+ }
81
123
  const baseRequest = getBaseRequest(connection);
82
124
  try {
83
125
  return await baseRequest({
@@ -104,6 +146,23 @@ async function makeAdtRequestWithAcceptNegotiation(connection, request, options)
104
146
  }
105
147
  }
106
148
  }
149
+ if (e.response?.status === 415) {
150
+ const supported = extractSupportedContentType(error);
151
+ if (supported.length > 0) {
152
+ logger?.warn?.(`Content-Type not supported for ${request.url}. Supported Content-Type: ${supported.join(', ')}`);
153
+ }
154
+ if (enableCorrection && supported.length > 0) {
155
+ const nextContentType = supported[0];
156
+ if (headers['Content-Type'] !== nextContentType) {
157
+ contentTypeCache.set(cacheKey, nextContentType);
158
+ logger?.warn?.(`Retrying ${request.url} with corrected Content-Type: ${nextContentType}`);
159
+ return await baseRequest({
160
+ ...request,
161
+ headers: { ...headers, 'Content-Type': nextContentType },
162
+ });
163
+ }
164
+ }
165
+ }
107
166
  throw error;
108
167
  }
109
168
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/adt-clients",
3
- "version": "3.11.3",
3
+ "version": "3.12.0",
4
4
  "description": "ADT clients for SAP ABAP systems - AdtClient and AdtRuntimeClient",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -60,7 +60,8 @@
60
60
  "shared:teardown": "npx jest --testPathIgnorePatterns node_modules --testPathPatterns admin/shared-deps/teardown",
61
61
  "shared:check": "npx jest --testPathIgnorePatterns node_modules --testPathPatterns admin/shared-deps/check",
62
62
  "pretest": "npm run test:check:integration",
63
- "prepublishOnly": "npm run build"
63
+ "prepublishOnly": "npm run build",
64
+ "prepare": "husky"
64
65
  },
65
66
  "engines": {
66
67
  "node": ">=18.0.0"
@@ -81,6 +82,7 @@
81
82
  "@types/jest": "^30.0.0",
82
83
  "@types/node": "^25.3.3",
83
84
  "dotenv": "^17.3.1",
85
+ "husky": "^9.1.7",
84
86
  "jest": "^30.0.5",
85
87
  "jest-util": "^30.2.0",
86
88
  "ts-jest": "^29.4.1",