@linkup-ai/abap-ai 0.1.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 (119) hide show
  1. package/README.md +389 -0
  2. package/dist/adt-client.js +383 -0
  3. package/dist/cli/activate.js +127 -0
  4. package/dist/cli/init.js +559 -0
  5. package/dist/cli/link.js +148 -0
  6. package/dist/cli/remove.js +83 -0
  7. package/dist/cli/status.js +231 -0
  8. package/dist/cli/systems.js +72 -0
  9. package/dist/cli.js +92 -0
  10. package/dist/index.js +1442 -0
  11. package/dist/knowledge/abap/abap-dictionary.md +199 -0
  12. package/dist/knowledge/abap/abap-sql.md +296 -0
  13. package/dist/knowledge/abap/amdp.md +273 -0
  14. package/dist/knowledge/abap/clean-code.md +293 -0
  15. package/dist/knowledge/abap/cloud-background-processing.md +250 -0
  16. package/dist/knowledge/abap/cloud-communication.md +265 -0
  17. package/dist/knowledge/abap/cloud-development.md +176 -0
  18. package/dist/knowledge/abap/cloud-extensibility.md +252 -0
  19. package/dist/knowledge/abap/cloud-released-apis.md +261 -0
  20. package/dist/knowledge/abap/constructor-expressions.md +289 -0
  21. package/dist/knowledge/abap/enhancements.md +232 -0
  22. package/dist/knowledge/abap/exceptions.md +271 -0
  23. package/dist/knowledge/abap/internal-tables.md +205 -0
  24. package/dist/knowledge/abap/object-orientation.md +298 -0
  25. package/dist/knowledge/abap/performance.md +216 -0
  26. package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
  27. package/dist/knowledge/abap/rap-business-events.md +216 -0
  28. package/dist/knowledge/abap/rap-draft.md +191 -0
  29. package/dist/knowledge/abap/rap-eml.md +453 -0
  30. package/dist/knowledge/abap/rap-end-to-end.md +486 -0
  31. package/dist/knowledge/abap/rap-feature-control.md +185 -0
  32. package/dist/knowledge/abap/rap-numbering.md +280 -0
  33. package/dist/knowledge/abap/rap-service-exposure.md +163 -0
  34. package/dist/knowledge/abap/rap-unmanaged.md +468 -0
  35. package/dist/knowledge/abap/string-processing.md +180 -0
  36. package/dist/knowledge/abap/unit-testing.md +303 -0
  37. package/dist/knowledge/abap-cds/access-control.md +241 -0
  38. package/dist/knowledge/abap-cds/annotations.md +331 -0
  39. package/dist/knowledge/abap-cds/associations.md +254 -0
  40. package/dist/knowledge/abap-cds/expressions.md +230 -0
  41. package/dist/knowledge/abap-cds/functions.md +245 -0
  42. package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
  43. package/dist/knowledge/cap/authentication.md +278 -0
  44. package/dist/knowledge/cap/cdl-syntax.md +247 -0
  45. package/dist/knowledge/cap/cql-queries.md +266 -0
  46. package/dist/knowledge/cap/deployment.md +343 -0
  47. package/dist/knowledge/cap/event-handlers.md +287 -0
  48. package/dist/knowledge/cap/fiori-integration.md +303 -0
  49. package/dist/knowledge/cap/service-definitions.md +287 -0
  50. package/dist/knowledge/fiori/annotations.md +347 -0
  51. package/dist/knowledge/fiori/deployment.md +340 -0
  52. package/dist/knowledge/fiori/fiori-elements.md +332 -0
  53. package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
  54. package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
  55. package/dist/knowledge/fiori/ui5-controllers.md +358 -0
  56. package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
  57. package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
  58. package/dist/knowledge/fiori/ui5-manifest.md +411 -0
  59. package/dist/knowledge/fiori/ui5-routing.md +303 -0
  60. package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
  61. package/dist/license-guard.js +81 -0
  62. package/dist/logger.js +114 -0
  63. package/dist/postinstall.js +165 -0
  64. package/dist/security-audit.js +136 -0
  65. package/dist/security-policy.js +322 -0
  66. package/dist/system-profile.js +207 -0
  67. package/dist/tools/abap-doc.js +72 -0
  68. package/dist/tools/abapgit.js +161 -0
  69. package/dist/tools/activate.js +71 -0
  70. package/dist/tools/atc-check.js +117 -0
  71. package/dist/tools/auth-object.js +56 -0
  72. package/dist/tools/breakpoints.js +76 -0
  73. package/dist/tools/call-hierarchy.js +84 -0
  74. package/dist/tools/cds-annotations.js +98 -0
  75. package/dist/tools/cds-dependencies.js +65 -0
  76. package/dist/tools/check.js +47 -0
  77. package/dist/tools/code-completion.js +70 -0
  78. package/dist/tools/code-coverage.js +111 -0
  79. package/dist/tools/create-amdp.js +111 -0
  80. package/dist/tools/create-dcl.js +81 -0
  81. package/dist/tools/create-transport.js +38 -0
  82. package/dist/tools/create.js +285 -0
  83. package/dist/tools/data-preview.js +37 -0
  84. package/dist/tools/delete.js +45 -0
  85. package/dist/tools/deploy-bsp.js +298 -0
  86. package/dist/tools/discovery.js +59 -0
  87. package/dist/tools/element-info.js +93 -0
  88. package/dist/tools/enhancements.js +186 -0
  89. package/dist/tools/extract-method.js +44 -0
  90. package/dist/tools/function-group.js +59 -0
  91. package/dist/tools/knowledge.js +275 -0
  92. package/dist/tools/lock-object.js +75 -0
  93. package/dist/tools/message-class.js +67 -0
  94. package/dist/tools/navigate.js +80 -0
  95. package/dist/tools/number-range.js +57 -0
  96. package/dist/tools/object-documentation.js +43 -0
  97. package/dist/tools/object-structure.js +78 -0
  98. package/dist/tools/object-versions.js +57 -0
  99. package/dist/tools/package-contents.js +60 -0
  100. package/dist/tools/pretty-printer.js +35 -0
  101. package/dist/tools/publish-binding.js +49 -0
  102. package/dist/tools/quick-fix.js +69 -0
  103. package/dist/tools/read.js +172 -0
  104. package/dist/tools/refactor-rename.js +60 -0
  105. package/dist/tools/release-transport.js +24 -0
  106. package/dist/tools/released-apis.js +51 -0
  107. package/dist/tools/repository-tree.js +90 -0
  108. package/dist/tools/scaffold-rap.js +642 -0
  109. package/dist/tools/search.js +73 -0
  110. package/dist/tools/shared/data-format.js +101 -0
  111. package/dist/tools/sql-console.js +17 -0
  112. package/dist/tools/system-info.js +271 -0
  113. package/dist/tools/traces.js +66 -0
  114. package/dist/tools/transport-contents.js +83 -0
  115. package/dist/tools/transports.js +68 -0
  116. package/dist/tools/unit-test.js +135 -0
  117. package/dist/tools/where-used.js +59 -0
  118. package/dist/tools/write.js +120 -0
  119. package/package.json +50 -0
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCdsAnnotations = abapCdsAnnotations;
4
+ exports.abapAnnotationPropagation = abapAnnotationPropagation;
5
+ const adt_client_js_1 = require("../adt-client.js");
6
+ async function abapCdsAnnotations(input) {
7
+ const { filter } = input;
8
+ const params = {};
9
+ if (filter)
10
+ params.filter = filter;
11
+ await (0, adt_client_js_1.ensureSession)();
12
+ const response = await adt_client_js_1.http.get("/ddic/cds/annotation/definitions", {
13
+ params,
14
+ headers: { Accept: "application/vnd.sap.adt.cds.annotation.definitions.v2+xml" },
15
+ responseType: "text",
16
+ validateStatus: (status) => status < 500,
17
+ });
18
+ if (response.status >= 400) {
19
+ throw new Error(`Erro ao listar anotações CDS (HTTP ${response.status})`);
20
+ }
21
+ const xml = response.data;
22
+ // O SAP retorna definições de anotações em formato CDS DDL dentro de CDATA
23
+ // Extrair o conteúdo CDATA
24
+ const cdataMatch = xml.match(/<!\[CDATA\[([\s\S]*?)\]\]>/);
25
+ const source = cdataMatch?.[1] ?? xml;
26
+ // Parse annotations do CDS DDL: "annotation AnnotationName {"
27
+ const annotations = [];
28
+ const annoRegex = /(?:@Scope:\s*\[([^\]]*)\]\s*)?annotation\s+(\w+)/gi;
29
+ let match;
30
+ while ((match = annoRegex.exec(source)) !== null) {
31
+ const scope = match[1] ?? "";
32
+ const name = match[2];
33
+ annotations.push({ name, scope: scope.replace(/#/g, "").trim() });
34
+ }
35
+ // Filtrar se necessário
36
+ const filtered = filter
37
+ ? annotations.filter((a) => a.name.toLowerCase().includes(filter.toLowerCase()))
38
+ : annotations;
39
+ if (filtered.length === 0) {
40
+ return filter
41
+ ? `Nenhuma anotação CDS encontrada com filtro "${filter}".`
42
+ : "Nenhuma anotação CDS retornada. O endpoint pode não estar disponível neste sistema.";
43
+ }
44
+ const lines = [];
45
+ lines.push(`Anotações CDS${filter ? ` (filtro: ${filter})` : ""} — ${filtered.length} encontradas`);
46
+ lines.push("─".repeat(70));
47
+ for (const a of filtered) {
48
+ const scope = a.scope ? ` [${a.scope}]` : "";
49
+ lines.push(` @${a.name}${scope}`);
50
+ }
51
+ return lines.join("\n");
52
+ }
53
+ async function abapAnnotationPropagation(input) {
54
+ const { object_name } = input;
55
+ const name = object_name.toUpperCase();
56
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)("DDLS/DF");
57
+ const uri = `/sap/bc/adt/${adtPath}/${name}`;
58
+ await (0, adt_client_js_1.ensureSession)();
59
+ const propResponse = await adt_client_js_1.http.get("/ddic/ddl/annotations/propagation", {
60
+ params: { uri },
61
+ headers: { Accept: "application/xml" },
62
+ responseType: "text",
63
+ validateStatus: (status) => status < 500,
64
+ });
65
+ if (propResponse.status >= 400) {
66
+ throw new Error(`Annotation propagation não disponível (HTTP ${propResponse.status})`);
67
+ }
68
+ const xml = propResponse.data;
69
+ // Parse propagation info
70
+ const propagations = [];
71
+ const propRegex = /<(?:propagation:)?entry[^>]*>([\s\S]*?)<\/(?:propagation:)?entry>/gi;
72
+ let match;
73
+ while ((match = propRegex.exec(xml)) !== null) {
74
+ const content = match[0];
75
+ const annoMatch = content.match(/annotation\s*=\s*"([^"]*)"/i);
76
+ const srcMatch = content.match(/source\s*=\s*"([^"]*)"/i);
77
+ const propMatch = content.match(/propagated\s*=\s*"([^"]*)"/i);
78
+ if (annoMatch) {
79
+ propagations.push({
80
+ annotation: annoMatch[1],
81
+ source: srcMatch?.[1] ?? "",
82
+ propagated: propMatch?.[1] === "true",
83
+ });
84
+ }
85
+ }
86
+ if (propagations.length === 0) {
87
+ return `Nenhuma informação de propagação de anotações para ${name}.`;
88
+ }
89
+ const lines = [];
90
+ lines.push(`Propagação de Anotações: ${name}`);
91
+ lines.push("─".repeat(60));
92
+ for (const p of propagations) {
93
+ const status = p.propagated ? "PROPAGADA" : "LOCAL";
94
+ const src = p.source ? ` (de: ${p.source})` : "";
95
+ lines.push(` @${p.annotation} — ${status}${src}`);
96
+ }
97
+ return lines.join("\n");
98
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCdsDependencies = abapCdsDependencies;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapCdsDependencies(input) {
6
+ const { object_name, direction = "both" } = input;
7
+ const name = object_name.toUpperCase();
8
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)("DDLS/DF");
9
+ const uri = `/sap/bc/adt/${adtPath}/${name}`;
10
+ const results = [];
11
+ if (direction === "uses" || direction === "both") {
12
+ // O que essa CDS consome
13
+ const xml = await (0, adt_client_js_1.adtGetXmlWithParams)("/repository/informationsystem/objectreferences", {
14
+ uri,
15
+ direction: "uses",
16
+ });
17
+ const refs = parseReferences(xml);
18
+ results.push({ dir: "Dependências (o que consome)", refs });
19
+ }
20
+ if (direction === "used_by" || direction === "both") {
21
+ // Quem consome essa CDS
22
+ const xml = await (0, adt_client_js_1.adtGetXmlWithParams)("/repository/informationsystem/objectreferences", {
23
+ uri,
24
+ direction: "usedBy",
25
+ });
26
+ const refs = parseReferences(xml);
27
+ results.push({ dir: "Dependentes (quem consome)", refs });
28
+ }
29
+ const lines = [];
30
+ lines.push(`Dependências CDS: ${name}`);
31
+ lines.push("═".repeat(60));
32
+ for (const r of results) {
33
+ lines.push(`\n${r.dir}: ${r.refs.length} referência(s)`);
34
+ lines.push("─".repeat(50));
35
+ if (r.refs.length === 0) {
36
+ lines.push(" (nenhuma)");
37
+ }
38
+ else {
39
+ for (const ref of r.refs) {
40
+ const desc = ref.description ? ` — ${ref.description}` : "";
41
+ lines.push(` ${ref.name} [${ref.type}]${desc}`);
42
+ }
43
+ }
44
+ }
45
+ return lines.join("\n");
46
+ }
47
+ function parseReferences(xml) {
48
+ const refs = [];
49
+ const refRegex = /<(?:adtcore:)?objectReference[^>]*>/gi;
50
+ let match;
51
+ while ((match = refRegex.exec(xml)) !== null) {
52
+ const el = match[0];
53
+ const nameMatch = el.match(/adtcore:name\s*=\s*"([^"]*)"/i);
54
+ const typeMatch = el.match(/adtcore:type\s*=\s*"([^"]*)"/i);
55
+ const descMatch = el.match(/adtcore:description\s*=\s*"([^"]*)"/i);
56
+ if (nameMatch) {
57
+ refs.push({
58
+ name: nameMatch[1],
59
+ type: typeMatch?.[1] ?? "",
60
+ description: descMatch?.[1] ?? "",
61
+ });
62
+ }
63
+ }
64
+ return refs;
65
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCheck = abapCheck;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ function parseCheckResponse(xml) {
6
+ const messages = [];
7
+ const msgRegex = /<chkl:message[^>]*type="([^"]*)"[^>]*>[\s\S]*?<chkl:shortText[^>]*>([\s\S]*?)<\/chkl:shortText>[\s\S]*?(?:<chkl:uri[^>]*line="(\d+)")?/g;
8
+ let match;
9
+ while ((match = msgRegex.exec(xml)) !== null) {
10
+ messages.push({ type: match[1], text: match[2].trim(), line: match[3] });
11
+ }
12
+ const hasErrors = messages.some((m) => m.type === "E" || m.type === "A");
13
+ return { hasErrors, messages };
14
+ }
15
+ async function abapCheck(input) {
16
+ const { object_type, object_name } = input;
17
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
18
+ const objectUri = `/sap/bc/adt/${adtPath}/${object_name.toUpperCase()}`;
19
+ const csrf = await (0, adt_client_js_1.ensureSession)();
20
+ const payload = `<?xml version="1.0" encoding="UTF-8"?>
21
+ <adtcore:objectReferences xmlns:adtcore="http://www.sap.com/adt/core">
22
+ <adtcore:objectReference adtcore:uri="${objectUri}" adtcore:type="${object_type}" adtcore:name="${object_name.toUpperCase()}"/>
23
+ </adtcore:objectReferences>`;
24
+ const response = await adt_client_js_1.http.post("/activation", payload, {
25
+ headers: {
26
+ "Content-Type": "application/xml",
27
+ "X-CSRF-Token": csrf,
28
+ },
29
+ params: { method: "activate", preauditRequested: "true" },
30
+ responseType: "text",
31
+ // Aceita 200 (check OK) e 400 (erros de sintaxe)
32
+ validateStatus: (s) => s === 200 || s === 400,
33
+ });
34
+ const { hasErrors, messages } = parseCheckResponse(response.data);
35
+ if (messages.length === 0) {
36
+ return `${object_name.toUpperCase()}: sem erros de sintaxe. (Objeto foi ativado se estava inativo.)`;
37
+ }
38
+ const lines = messages.map((m) => {
39
+ const prefix = m.type === "E" || m.type === "A" ? "ERRO" : m.type === "W" ? "AVISO" : "INFO";
40
+ const loc = m.line ? ` (linha ${m.line})` : "";
41
+ return ` [${prefix}]${loc} ${m.text}`;
42
+ });
43
+ if (hasErrors) {
44
+ return `${object_name.toUpperCase()}: ${messages.filter((m) => m.type === "E" || m.type === "A").length} erro(s) encontrado(s):\n${lines.join("\n")}`;
45
+ }
46
+ return `${object_name.toUpperCase()}: sem erros (${messages.length} aviso(s)):\n${lines.join("\n")}`;
47
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCodeCompletion = abapCodeCompletion;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ function resolveSourceUri(objectType, objectName, classInclude) {
6
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)(objectType);
7
+ const name = objectName.toLowerCase();
8
+ if (objectType === "CLAS/OC" && classInclude && classInclude !== "main") {
9
+ return `/sap/bc/adt/${adtPath}/${name}/includes/${classInclude}/source/main`;
10
+ }
11
+ return `/sap/bc/adt/${adtPath}/${name}/source/main`;
12
+ }
13
+ async function abapCodeCompletion(input) {
14
+ const { object_type, object_name, line, column, class_include } = input;
15
+ const uri = resolveSourceUri(object_type, object_name, class_include);
16
+ const csrf = await (0, adt_client_js_1.ensureSession)();
17
+ const response = await adt_client_js_1.http.post("/abapsource/codecompletion/proposal", "", {
18
+ params: { uri, line: String(line), column: String(column) },
19
+ headers: {
20
+ Accept: "application/vnd.sap.as+xml",
21
+ "Content-Type": "text/plain",
22
+ "X-CSRF-Token": csrf,
23
+ },
24
+ responseType: "text",
25
+ validateStatus: (status) => status < 500,
26
+ });
27
+ if (response.status >= 400) {
28
+ const msg = typeof response.data === "string"
29
+ ? (response.data.match(/<message[^>]*>([^<]+)<\/message>/i)?.[1] ?? `HTTP ${response.status}`)
30
+ : `HTTP ${response.status}`;
31
+ throw new Error(msg);
32
+ }
33
+ const xml = response.data;
34
+ if (!xml || xml.trim().length === 0) {
35
+ return `Nenhuma sugestão de code completion para ${object_name} na posição ${line}:${column}.`;
36
+ }
37
+ // Parse proposals - formato SAP ABAP XML
38
+ const proposals = [];
39
+ // Pattern: <PROPOSALS><PROPOSAL><IDENTIFIER>name</IDENTIFIER>...</PROPOSAL>
40
+ const propRegex = /<PROPOSAL>([\s\S]*?)<\/PROPOSAL>/gi;
41
+ let match;
42
+ while ((match = propRegex.exec(xml)) !== null) {
43
+ const block = match[1];
44
+ const id = block.match(/<IDENTIFIER>([^<]*)<\/IDENTIFIER>/)?.[1] ?? "";
45
+ const desc = block.match(/<DESCRIPTION>([^<]*)<\/DESCRIPTION>/)?.[1] ?? "";
46
+ if (id)
47
+ proposals.push({ name: id, description: desc });
48
+ }
49
+ // Fallback: formato XML attributes
50
+ if (proposals.length === 0) {
51
+ const attrRegex = /(?:name|identifier)\s*=\s*"([^"]*)"/gi;
52
+ while ((match = attrRegex.exec(xml)) !== null) {
53
+ proposals.push({ name: match[1], description: "" });
54
+ }
55
+ }
56
+ if (proposals.length === 0) {
57
+ return `Nenhuma sugestão de code completion para ${object_name} na posição ${line}:${column}.`;
58
+ }
59
+ const lines = [];
60
+ lines.push(`Code Completion: ${object_name} (linha ${line}, coluna ${column}) — ${proposals.length} sugestões`);
61
+ lines.push("─".repeat(80));
62
+ for (const p of proposals.slice(0, 50)) {
63
+ const desc = p.description ? ` — ${p.description}` : "";
64
+ lines.push(` ${p.name}${desc}`);
65
+ }
66
+ if (proposals.length > 50) {
67
+ lines.push(` ... +${proposals.length - 50} sugestões omitidas`);
68
+ }
69
+ return lines.join("\n");
70
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCodeCoverage = abapCodeCoverage;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapCodeCoverage(input) {
6
+ const { object_type, object_name } = input;
7
+ const name = object_name.toUpperCase();
8
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
9
+ const objectUri = `/sap/bc/adt/${adtPath}/${name}`;
10
+ const body = `<?xml version="1.0" encoding="UTF-8"?>
11
+ <aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
12
+ <external>
13
+ <coverage active="true">
14
+ <format>statement</format>
15
+ </coverage>
16
+ </external>
17
+ <options>
18
+ <uriType value="semantic"/>
19
+ <testDeterminationStrategy sameProgram="true" assignedTests="false" publicApi="false"/>
20
+ <testRiskLevels harmless="true" dangerous="true" critical="true"/>
21
+ <testDurations short="true" medium="true" long="true"/>
22
+ </options>
23
+ <adtcore:objectSets xmlns:adtcore="http://www.sap.com/adt/core">
24
+ <objectSet kind="inclusive">
25
+ <adtcore:objectReferences>
26
+ <adtcore:objectReference adtcore:uri="${objectUri}" adtcore:name="${name}"/>
27
+ </adtcore:objectReferences>
28
+ </objectSet>
29
+ </adtcore:objectSets>
30
+ </aunit:runConfiguration>`;
31
+ const { data, status } = await (0, adt_client_js_1.adtPostXml)("/abapunit/testruns", body);
32
+ if (status >= 400) {
33
+ throw new Error(`Code coverage falhou (HTTP ${status}): ${data.slice(0, 500)}`);
34
+ }
35
+ // Parse test results
36
+ const testResults = [];
37
+ const classRegex = /<testClass[^>]*adtcore:name\s*=\s*"([^"]*)"[^>]*>([\s\S]*?)<\/testClass>/gi;
38
+ let classMatch;
39
+ while ((classMatch = classRegex.exec(data)) !== null) {
40
+ const className = classMatch[1];
41
+ const classContent = classMatch[2];
42
+ const methodRegex = /<testMethod[^>]*adtcore:name\s*=\s*"([^"]*)"[^>]*>([\s\S]*?)<\/testMethod>/gi;
43
+ let methodMatch;
44
+ while ((methodMatch = methodRegex.exec(classContent)) !== null) {
45
+ const methodName = methodMatch[1];
46
+ const methodContent = methodMatch[2];
47
+ const hasAlert = /<alert/i.test(methodContent);
48
+ const alertKind = methodContent.match(/kind\s*=\s*"([^"]*)"/i);
49
+ let mStatus = "PASSED";
50
+ if (hasAlert) {
51
+ if (alertKind && /fail|exception|error/i.test(alertKind[1])) {
52
+ mStatus = "FAILED";
53
+ }
54
+ else {
55
+ mStatus = "WARNING";
56
+ }
57
+ }
58
+ testResults.push({ className, method: methodName, status: mStatus });
59
+ }
60
+ }
61
+ // Parse coverage data
62
+ const coverageLines = [];
63
+ const stmtRegex = /<statement[^>]*?(?:executed|covered)\s*=\s*"([^"]*)"[^>]*?line\s*=\s*"([^"]*)"[^>]*>/gi;
64
+ let stmtMatch;
65
+ while ((stmtMatch = stmtRegex.exec(data)) !== null) {
66
+ coverageLines.push({
67
+ line: parseInt(stmtMatch[2], 10),
68
+ executed: stmtMatch[1] === "true" || stmtMatch[1] === "1",
69
+ });
70
+ }
71
+ // Fallback: try alternative coverage format
72
+ if (coverageLines.length === 0) {
73
+ const covRegex = /<(?:coverage:)?line[^>]*?nr\s*=\s*"([^"]*)"[^>]*?executed\s*=\s*"([^"]*)"[^>]*>/gi;
74
+ while ((stmtMatch = covRegex.exec(data)) !== null) {
75
+ coverageLines.push({
76
+ line: parseInt(stmtMatch[1], 10),
77
+ executed: stmtMatch[2] === "true" || stmtMatch[2] === "X",
78
+ });
79
+ }
80
+ }
81
+ // Format output
82
+ const lines = [];
83
+ lines.push(`Code Coverage: ${name}`);
84
+ lines.push("═".repeat(60));
85
+ // Test results summary
86
+ const passed = testResults.filter(t => t.status === "PASSED").length;
87
+ const failed = testResults.filter(t => t.status === "FAILED").length;
88
+ lines.push(`\nTestes: ${passed} passed, ${failed} failed, ${testResults.length} total`);
89
+ for (const t of testResults) {
90
+ const icon = t.status === "PASSED" ? "OK" : t.status === "FAILED" ? "FAIL" : "WARN";
91
+ lines.push(` [${icon}] ${t.className}->${t.method}`);
92
+ }
93
+ // Coverage summary
94
+ if (coverageLines.length > 0) {
95
+ const covered = coverageLines.filter(l => l.executed).length;
96
+ const total = coverageLines.length;
97
+ const pct = total > 0 ? Math.round((covered / total) * 100) : 0;
98
+ lines.push(`\nCobertura: ${covered}/${total} statements (${pct}%)`);
99
+ const uncovered = coverageLines.filter(l => !l.executed).map(l => l.line);
100
+ if (uncovered.length > 0 && uncovered.length <= 50) {
101
+ lines.push(`Linhas não cobertas: ${uncovered.join(", ")}`);
102
+ }
103
+ else if (uncovered.length > 50) {
104
+ lines.push(`Linhas não cobertas: ${uncovered.slice(0, 50).join(", ")} ... (+${uncovered.length - 50})`);
105
+ }
106
+ }
107
+ else {
108
+ lines.push(`\nCobertura: dados de coverage não retornados pelo servidor.`);
109
+ }
110
+ return lines.join("\n");
111
+ }
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCreateAmdp = abapCreateAmdp;
4
+ const create_js_1 = require("./create.js");
5
+ const write_js_1 = require("./write.js");
6
+ const activate_js_1 = require("./activate.js");
7
+ // ---------------------------------------------------------------------------
8
+ // Templates
9
+ // ---------------------------------------------------------------------------
10
+ function buildAmdpClass(input) {
11
+ const { class_name, method_name, method_type, importing = [], exporting_table, cds_entity, } = input;
12
+ const cls = class_name.toLowerCase();
13
+ const meth = method_name.toLowerCase();
14
+ const importingParams = importing
15
+ .map((p) => ` VALUE(${p.name}) TYPE ${p.type}`)
16
+ .join("\n");
17
+ if (method_type === "table_function") {
18
+ // AMDP Table Function — used with CDS table function
19
+ return `CLASS ${cls} DEFINITION PUBLIC FINAL CREATE PUBLIC.
20
+ PUBLIC SECTION.
21
+ INTERFACES if_amdp_marker_hdb.
22
+ CLASS-METHODS ${meth}
23
+ FOR TABLE FUNCTION ${cds_entity || class_name.toUpperCase().replace(/^ZCL_/, "ZTF_")}.
24
+ ENDCLASS.
25
+
26
+ CLASS ${cls} IMPLEMENTATION.
27
+ METHOD ${meth} BY DATABASE FUNCTION FOR HDB
28
+ LANGUAGE SQLSCRIPT
29
+ OPTIONS READ-ONLY
30
+ USING ${cds_entity ? cds_entity.toLowerCase() : "/* tabela base */"}.
31
+
32
+ -- TODO: Implementar SQLScript
33
+ RETURN
34
+ SELECT *
35
+ FROM ${cds_entity ? cds_entity.toLowerCase() : "/* tabela base */"};
36
+
37
+ ENDMETHOD.
38
+ ENDCLASS.`;
39
+ }
40
+ // AMDP Procedure
41
+ const exportingLine = exporting_table
42
+ ? `\n EXPORTING\n VALUE(et_result) TYPE ${exporting_table}`
43
+ : "";
44
+ return `CLASS ${cls} DEFINITION PUBLIC FINAL CREATE PUBLIC.
45
+ PUBLIC SECTION.
46
+ INTERFACES if_amdp_marker_hdb.
47
+ METHODS ${meth}
48
+ ${importingParams ? " IMPORTING\n" + importingParams : ""}${exportingLine}.
49
+ ENDCLASS.
50
+
51
+ CLASS ${cls} IMPLEMENTATION.
52
+ METHOD ${meth} BY DATABASE PROCEDURE FOR HDB
53
+ LANGUAGE SQLSCRIPT
54
+ OPTIONS READ-ONLY
55
+ USING /* tabelas usadas */.
56
+
57
+ -- TODO: Implementar SQLScript
58
+ et_result = SELECT * FROM /* tabela */;
59
+
60
+ ENDMETHOD.
61
+ ENDCLASS.`;
62
+ }
63
+ // ---------------------------------------------------------------------------
64
+ // Main
65
+ // ---------------------------------------------------------------------------
66
+ async function abapCreateAmdp(input) {
67
+ const { class_name, method_name, method_type, description, package: pkg = "$TMP", transport_request } = input;
68
+ const name = class_name.toUpperCase();
69
+ const steps = [];
70
+ try {
71
+ await (0, create_js_1.abapCreate)({
72
+ object_type: "CLAS/OC",
73
+ object_name: name,
74
+ description: description || `AMDP ${method_type} - ${method_name}`,
75
+ package: pkg,
76
+ transport_request,
77
+ });
78
+ steps.push(`✓ Classe ${name} criada`);
79
+ const source = buildAmdpClass(input);
80
+ await (0, write_js_1.abapWrite)({
81
+ object_type: "CLAS/OC",
82
+ object_name: name,
83
+ source,
84
+ });
85
+ steps.push(`✓ Source AMDP gravado (${method_type})`);
86
+ await (0, activate_js_1.abapActivate)({
87
+ object_type: "CLAS/OC",
88
+ object_name: name,
89
+ });
90
+ steps.push(`✓ Classe ${name} ativada`);
91
+ steps.push("");
92
+ if (method_type === "table_function") {
93
+ steps.push("📋 Próximos passos:");
94
+ steps.push(" 1. Criar CDS Table Function referenciando esta classe");
95
+ steps.push(" 2. Implementar SQLScript no método");
96
+ steps.push(" → abap_knowledge(action:'get', topics:['abap/amdp'])");
97
+ }
98
+ else {
99
+ steps.push("📋 Próximos passos:");
100
+ steps.push(" 1. Ajustar tabelas no USING e implementar SQLScript");
101
+ steps.push(" 2. Chamar via CALL METHOD ou READ ENTITIES");
102
+ steps.push(" → abap_knowledge(action:'get', topics:['abap/amdp'])");
103
+ }
104
+ return steps.join("\n");
105
+ }
106
+ catch (err) {
107
+ const message = err instanceof Error ? err.message : String(err);
108
+ steps.push(`✗ ERRO: ${message}`);
109
+ return steps.join("\n");
110
+ }
111
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCreateDcl = abapCreateDcl;
4
+ const create_js_1 = require("./create.js");
5
+ const write_js_1 = require("./write.js");
6
+ const activate_js_1 = require("./activate.js");
7
+ // ---------------------------------------------------------------------------
8
+ // Template builder
9
+ // ---------------------------------------------------------------------------
10
+ function buildDclSource(input) {
11
+ const { dcl_name, cds_entity, auth_type, auth_mappings = [], literal_conditions = [], with_user_aspect } = input;
12
+ const conditions = [];
13
+ if (auth_type === "pfcg" || auth_type === "mapping") {
14
+ for (const mapping of auth_mappings) {
15
+ const actvt = mapping.actvt || "03";
16
+ const cdsFields = mapping.cds_fields.join(", ");
17
+ const authFields = [...mapping.auth_fields, `ACTVT = '${actvt}'`].join(", ");
18
+ conditions.push(`(${cdsFields}) = aspect pfcg_auth(${mapping.auth_object}, ${authFields})`);
19
+ }
20
+ }
21
+ for (const lit of literal_conditions) {
22
+ conditions.push(lit);
23
+ }
24
+ if (with_user_aspect) {
25
+ conditions.push("created_by ?= aspect user");
26
+ }
27
+ // If no conditions, use a simple grant all
28
+ if (conditions.length === 0) {
29
+ conditions.push("1 = 1");
30
+ }
31
+ const whereClause = conditions.length === 1
32
+ ? ` where ${conditions[0]}`
33
+ : ` where ${conditions.join("\n and ")}`;
34
+ return `@EndUserText.label: 'Access Control for ${cds_entity}'
35
+ @MappingRole: true
36
+ define role ${dcl_name} {
37
+ grant select on ${cds_entity}
38
+ ${whereClause};
39
+ }`;
40
+ }
41
+ // ---------------------------------------------------------------------------
42
+ // Main
43
+ // ---------------------------------------------------------------------------
44
+ async function abapCreateDcl(input) {
45
+ const { dcl_name, cds_entity, package: pkg = "$TMP", transport_request } = input;
46
+ const name = dcl_name.toUpperCase();
47
+ const steps = [];
48
+ try {
49
+ // DCL objects are created as DDLS/DF (they are CDS sources)
50
+ await (0, create_js_1.abapCreate)({
51
+ object_type: "DDLS/DF",
52
+ object_name: name,
53
+ description: `Access Control for ${cds_entity}`,
54
+ package: pkg,
55
+ transport_request,
56
+ });
57
+ steps.push(`✓ DCL ${name} criado`);
58
+ const source = buildDclSource(input);
59
+ await (0, write_js_1.abapWrite)({
60
+ object_type: "DDLS/DF",
61
+ object_name: name,
62
+ source,
63
+ });
64
+ steps.push(`✓ Source DCL gravado`);
65
+ await (0, activate_js_1.abapActivate)({
66
+ object_type: "DDLS/DF",
67
+ object_name: name,
68
+ });
69
+ steps.push(`✓ DCL ${name} ativado`);
70
+ steps.push("");
71
+ steps.push(`📋 DCL ${name} protege a entidade ${cds_entity}`);
72
+ steps.push(` Verifique que ${cds_entity} tem @AccessControl.authorizationCheck: #CHECK`);
73
+ steps.push(` Teste autorizações com SU53 após acesso negado`);
74
+ return steps.join("\n");
75
+ }
76
+ catch (err) {
77
+ const message = err instanceof Error ? err.message : String(err);
78
+ steps.push(`✗ ERRO: ${message}`);
79
+ return steps.join("\n");
80
+ }
81
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapCreateTransport = abapCreateTransport;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapCreateTransport(input) {
6
+ const { description, type = "W", target = "" } = input;
7
+ const body = `<?xml version="1.0" encoding="UTF-8"?>
8
+ <tm:root xmlns:tm="http://www.sap.com/cts/adt/tm">
9
+ <tm:request tm:desc="${description}" tm:type="${type}" tm:target="${target}" tm:cts_project=""/>
10
+ </tm:root>`;
11
+ const { data, status, headers } = await (0, adt_client_js_1.adtPostXml)("/cts/transportrequests", body, undefined, "application/vnd.sap.adt.transportorganizer.v1+xml");
12
+ if (status >= 400) {
13
+ const msgMatch = data.match(/<(?:message|shortText)[^>]*>([\s\S]*?)<\/(?:message|shortText)>/i);
14
+ throw new Error(`Erro ao criar transporte (HTTP ${status}): ${msgMatch?.[1] ?? data.slice(0, 500)}`);
15
+ }
16
+ // Extrair número da ordem do response ou Location header
17
+ let trId = "";
18
+ const location = headers["location"] ?? "";
19
+ const locMatch = location.match(/transportrequests\/([A-Z0-9]+)/i);
20
+ if (locMatch) {
21
+ trId = locMatch[1];
22
+ }
23
+ else {
24
+ const idMatch = data.match(/(?:tm:)?(?:number|id)\s*=\s*"([^"]*)"/i);
25
+ trId = idMatch?.[1] ?? "";
26
+ }
27
+ // Fallback: buscar no corpo
28
+ if (!trId) {
29
+ const bodyMatch = data.match(/([A-Z]{3,4}K\d{6})/);
30
+ trId = bodyMatch?.[1] ?? "???";
31
+ }
32
+ const typeLabel = type === "W" ? "Workbench" : type === "K" ? "Customizing" : type;
33
+ return `Ordem de transporte criada com sucesso!\n` +
34
+ `Número: ${trId}\n` +
35
+ `Tipo: ${typeLabel}\n` +
36
+ `Descrição: ${description}` +
37
+ (target ? `\nDestino: ${target}` : "");
38
+ }