abap-local-client 1.4.6 → 1.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abap-local-client",
3
- "version": "1.4.6",
3
+ "version": "1.4.9",
4
4
  "description": "SAP Local Client — WebSocket bridge with Basic, SPNEGO, X.509, SAML & SNC authentication",
5
5
  "main": "sso-sap-client.mjs",
6
6
  "type": "module",
@@ -483,7 +483,7 @@ class SAPClient {
483
483
  return { success: !hasErrors && response.status >= 200 && response.status < 300, status: response.status, statusText: response.statusText, headers: response.headers, data: { raw: response.data, ...extractedData }, ...(errorMessages.length > 0 && { error: `SAP validation failed: ${errorMessages.join('; ')}`, errorDetails: errorMessages }) };
484
484
  } catch (err) {
485
485
  console.error(` ❌ RFC error: ${err.message}`);
486
- return { success: false, status: 0, error: err.message, data: null };
486
+ return { success: false, status: 0, statusText: 'RFC Error', error: err.message, data: { raw: null } };
487
487
  }
488
488
  }
489
489
 
@@ -583,11 +583,27 @@ class SAPClient {
583
583
  console.error(` ❌ RESPONSE HEADERS:`);
584
584
  for (const [k, v] of Object.entries(error.response.headers)) console.error(` ❌ ${k}: ${v}`);
585
585
  }
586
- const resBody = typeof error.response?.data === 'string'
586
+ const rawBody = typeof error.response?.data === 'string'
587
587
  ? error.response.data
588
- : JSON.stringify(error.response?.data);
589
- if (resBody) console.error(` ❌ RESPONSE BODY:\n${resBody}`);
590
- return { success: false, status, error: error.message, data: error.response?.data };
588
+ : (error.response?.data ? JSON.stringify(error.response.data) : null);
589
+ if (rawBody) console.error(` ❌ RESPONSE BODY:\n${rawBody}`);
590
+
591
+ // Extract the real SAP error message from the XML body (exc:exception, etc.)
592
+ const { hasErrors, errorMessages } = parseSAPResponse(rawBody || '');
593
+ const sapError = hasErrors ? errorMessages.join('; ') : null;
594
+ // Precedence: parsed SAP message > raw XML body > Axios generic message
595
+ // Sending rawBody as fallback ensures the MCP server always gets the full SAP response
596
+ // even when parseSAPResponse doesn't recognise the XML format.
597
+ const errorMsg = sapError || rawBody || error.message;
598
+ if (sapError) console.error(` ❌ SAP error: ${sapError}`);
599
+
600
+ return {
601
+ success: false,
602
+ status,
603
+ statusText: error.response?.statusText,
604
+ error: errorMsg,
605
+ data: { raw: rawBody, ...(hasErrors && { errorMessages }) },
606
+ };
591
607
  }
592
608
  }
593
609
 
package/xml-utils.js CHANGED
@@ -50,8 +50,22 @@ export function parseSAPResponse(xmlString) {
50
50
  }
51
51
  }
52
52
 
53
- // Add other error patterns here if needed (e.g., activation errors, etc.)
54
-
53
+ // exc:exception generic SAP ADT error (e.g. SQL query syntax errors, 4xx responses)
54
+ const exc = parsed['exc:exception'];
55
+ if (exc) {
56
+ const msg = exc['message'] || exc['localizedMessage'] || exc['@_message'] || '';
57
+ const typeId = exc['type']?.['@_id'] || exc['namespace']?.['@_id'] || '';
58
+ const text = [msg, typeId].filter(Boolean).join(' — ');
59
+ if (text) return { hasErrors: true, errorMessages: [text], parsedData: parsed };
60
+ }
61
+
62
+ // iaex:exception — another SAP error envelope
63
+ const iaex = parsed['iaex:exception'];
64
+ if (iaex) {
65
+ const msg = iaex['iaex:message'] || iaex['message'] || '';
66
+ if (msg) return { hasErrors: true, errorMessages: [String(msg)], parsedData: parsed };
67
+ }
68
+
55
69
  return { hasErrors: false, errorMessages: [], parsedData: parsed };
56
70
  } catch (error) {
57
71
  console.error(' ⚠️ XML parsing failed:', error.message);
package/xml-utils.mjs CHANGED
@@ -7,7 +7,8 @@ const xmlParser = new XMLParser({
7
7
  });
8
8
 
9
9
  /**
10
- * Parse SAP XML response and check for errors
10
+ * Parse SAP XML response and check for errors.
11
+ * Handles: chkrun syntax errors, exc:exception envelope, adtcore activation errors.
11
12
  * Returns: { hasErrors: boolean, errorMessages: string[], parsedData: object }
12
13
  */
13
14
  export function parseSAPResponse(xmlString) {
@@ -17,14 +18,14 @@ export function parseSAPResponse(xmlString) {
17
18
 
18
19
  try {
19
20
  const parsed = xmlParser.parse(xmlString);
20
-
21
- // Check for syntax check errors (chkrun:checkMessage with type="E")
21
+
22
+ // ── 1. Syntax check errors (chkrun:checkMessage with type="E") ──────────
22
23
  if (parsed['chkrun:checkRunReports']) {
23
24
  const report = parsed['chkrun:checkRunReports']['chkrun:checkReport'];
24
25
  const messageList = report?.['chkrun:checkMessageList'];
25
-
26
+
26
27
  if (messageList && messageList['chkrun:checkMessage']) {
27
- const messages = Array.isArray(messageList['chkrun:checkMessage'])
28
+ const messages = Array.isArray(messageList['chkrun:checkMessage'])
28
29
  ? messageList['chkrun:checkMessage']
29
30
  : [messageList['chkrun:checkMessage']];
30
31
 
@@ -34,9 +35,7 @@ export function parseSAPResponse(xmlString) {
34
35
  let line = null;
35
36
  if (msg['@_chkrun:uri'] && msg['@_chkrun:uri'].includes('#start=')) {
36
37
  const match = msg['@_chkrun:uri'].match(/#start=(\d+),(\d+)/);
37
- if (match) {
38
- line = parseInt(match[1]);
39
- }
38
+ if (match) line = parseInt(match[1]);
40
39
  }
41
40
  const text = msg['@_chkrun:shortText'] || 'Unknown error';
42
41
  const lineInfo = line ? ` (line ${line})` : '';
@@ -50,11 +49,43 @@ export function parseSAPResponse(xmlString) {
50
49
  }
51
50
  }
52
51
 
53
- // Add other error patterns here if needed (e.g., activation errors, etc.)
54
-
52
+ // ── 2. exc:exception SAP ADT error envelope ────────────────────────────
53
+ // Used by: SQL queries, create object, activate, and most 4xx responses
54
+ // Formats seen:
55
+ // <exc:exception xmlns:exc="http://www.sap.com/abapxml/types/exception">
56
+ // <message>Syntax error in ABAP Open SQL: ...</message>
57
+ // </exc:exception>
58
+ // <exc:exception xmlns:exc="http://www.sap.com/abapxml/types/communicationframework">
59
+ // <message lang="EN">Verificacao da condicao fracassou.</message>
60
+ // <localizedMessage lang="PT">...</localizedMessage>
61
+ // </exc:exception>
62
+ const excKey = Object.keys(parsed).find(k => k === 'exc:exception' || k.endsWith(':exception'));
63
+ if (excKey) {
64
+ const exc = parsed[excKey];
65
+ let msg = null;
66
+
67
+ const msgNode = exc?.message;
68
+ if (typeof msgNode === 'string') {
69
+ msg = msgNode;
70
+ } else if (typeof msgNode === 'object' && msgNode !== null) {
71
+ const msgs = Array.isArray(msgNode) ? msgNode : [msgNode];
72
+ const en = msgs.find(m => m['@_lang'] === 'EN' || m['@_xml:lang'] === 'EN');
73
+ msg = (en ? en['#text'] : msgs[0]?.['#text']) || null;
74
+ }
75
+
76
+ // fallback: localizedMessage
77
+ if (!msg) {
78
+ const loc = exc?.localizedMessage;
79
+ if (typeof loc === 'string') msg = loc;
80
+ else if (typeof loc === 'object' && loc !== null) msg = loc?.['#text'] || null;
81
+ }
82
+
83
+ if (msg) return { hasErrors: true, errorMessages: [msg], parsedData: parsed };
84
+ }
85
+
55
86
  return { hasErrors: false, errorMessages: [], parsedData: parsed };
56
- } catch (error) {
57
- console.error(' ⚠️ XML parsing failed:', error.message);
87
+ } catch (err) {
88
+ console.error(' XML parsing failed:', err.message);
58
89
  return { hasErrors: false, errorMessages: [], parsedData: null };
59
90
  }
60
91
  }