@linkup-ai/abap-ai 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 (114) hide show
  1. package/README.md +384 -0
  2. package/dist/adt-client.js +364 -0
  3. package/dist/cli/activate.js +113 -0
  4. package/dist/cli/init.js +333 -0
  5. package/dist/cli/remove.js +80 -0
  6. package/dist/cli/status.js +229 -0
  7. package/dist/cli/systems.js +68 -0
  8. package/dist/cli.js +81 -0
  9. package/dist/index.js +1318 -0
  10. package/dist/knowledge/abap/abap-dictionary.md +199 -0
  11. package/dist/knowledge/abap/abap-sql.md +296 -0
  12. package/dist/knowledge/abap/amdp.md +273 -0
  13. package/dist/knowledge/abap/clean-code.md +293 -0
  14. package/dist/knowledge/abap/cloud-background-processing.md +250 -0
  15. package/dist/knowledge/abap/cloud-communication.md +265 -0
  16. package/dist/knowledge/abap/cloud-development.md +176 -0
  17. package/dist/knowledge/abap/cloud-extensibility.md +252 -0
  18. package/dist/knowledge/abap/cloud-released-apis.md +261 -0
  19. package/dist/knowledge/abap/constructor-expressions.md +289 -0
  20. package/dist/knowledge/abap/enhancements.md +232 -0
  21. package/dist/knowledge/abap/exceptions.md +271 -0
  22. package/dist/knowledge/abap/internal-tables.md +205 -0
  23. package/dist/knowledge/abap/object-orientation.md +298 -0
  24. package/dist/knowledge/abap/performance.md +216 -0
  25. package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
  26. package/dist/knowledge/abap/rap-business-events.md +216 -0
  27. package/dist/knowledge/abap/rap-draft.md +191 -0
  28. package/dist/knowledge/abap/rap-eml.md +453 -0
  29. package/dist/knowledge/abap/rap-end-to-end.md +486 -0
  30. package/dist/knowledge/abap/rap-feature-control.md +185 -0
  31. package/dist/knowledge/abap/rap-numbering.md +280 -0
  32. package/dist/knowledge/abap/rap-service-exposure.md +163 -0
  33. package/dist/knowledge/abap/rap-unmanaged.md +468 -0
  34. package/dist/knowledge/abap/string-processing.md +180 -0
  35. package/dist/knowledge/abap/unit-testing.md +303 -0
  36. package/dist/knowledge/abap-cds/access-control.md +241 -0
  37. package/dist/knowledge/abap-cds/annotations.md +331 -0
  38. package/dist/knowledge/abap-cds/associations.md +254 -0
  39. package/dist/knowledge/abap-cds/expressions.md +230 -0
  40. package/dist/knowledge/abap-cds/functions.md +245 -0
  41. package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
  42. package/dist/knowledge/cap/authentication.md +278 -0
  43. package/dist/knowledge/cap/cdl-syntax.md +247 -0
  44. package/dist/knowledge/cap/cql-queries.md +266 -0
  45. package/dist/knowledge/cap/deployment.md +343 -0
  46. package/dist/knowledge/cap/event-handlers.md +287 -0
  47. package/dist/knowledge/cap/fiori-integration.md +303 -0
  48. package/dist/knowledge/cap/service-definitions.md +287 -0
  49. package/dist/knowledge/fiori/annotations.md +347 -0
  50. package/dist/knowledge/fiori/deployment.md +340 -0
  51. package/dist/knowledge/fiori/fiori-elements.md +332 -0
  52. package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
  53. package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
  54. package/dist/knowledge/fiori/ui5-controllers.md +358 -0
  55. package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
  56. package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
  57. package/dist/knowledge/fiori/ui5-manifest.md +411 -0
  58. package/dist/knowledge/fiori/ui5-routing.md +303 -0
  59. package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
  60. package/dist/logger.js +114 -0
  61. package/dist/system-profile.js +207 -0
  62. package/dist/tools/abap-doc.js +72 -0
  63. package/dist/tools/abapgit.js +161 -0
  64. package/dist/tools/activate.js +68 -0
  65. package/dist/tools/atc-check.js +117 -0
  66. package/dist/tools/auth-object.js +56 -0
  67. package/dist/tools/breakpoints.js +76 -0
  68. package/dist/tools/call-hierarchy.js +84 -0
  69. package/dist/tools/cds-annotations.js +98 -0
  70. package/dist/tools/cds-dependencies.js +65 -0
  71. package/dist/tools/check.js +47 -0
  72. package/dist/tools/code-completion.js +70 -0
  73. package/dist/tools/code-coverage.js +111 -0
  74. package/dist/tools/create-amdp.js +111 -0
  75. package/dist/tools/create-dcl.js +81 -0
  76. package/dist/tools/create-transport.js +38 -0
  77. package/dist/tools/create.js +285 -0
  78. package/dist/tools/data-preview.js +37 -0
  79. package/dist/tools/delete.js +45 -0
  80. package/dist/tools/deploy-bsp.js +298 -0
  81. package/dist/tools/discovery.js +59 -0
  82. package/dist/tools/element-info.js +93 -0
  83. package/dist/tools/enhancements.js +186 -0
  84. package/dist/tools/extract-method.js +44 -0
  85. package/dist/tools/function-group.js +59 -0
  86. package/dist/tools/knowledge.js +275 -0
  87. package/dist/tools/lock-object.js +75 -0
  88. package/dist/tools/message-class.js +67 -0
  89. package/dist/tools/navigate.js +80 -0
  90. package/dist/tools/number-range.js +57 -0
  91. package/dist/tools/object-documentation.js +43 -0
  92. package/dist/tools/object-structure.js +78 -0
  93. package/dist/tools/object-versions.js +57 -0
  94. package/dist/tools/package-contents.js +60 -0
  95. package/dist/tools/pretty-printer.js +35 -0
  96. package/dist/tools/publish-binding.js +49 -0
  97. package/dist/tools/quick-fix.js +69 -0
  98. package/dist/tools/read.js +167 -0
  99. package/dist/tools/refactor-rename.js +60 -0
  100. package/dist/tools/release-transport.js +24 -0
  101. package/dist/tools/released-apis.js +51 -0
  102. package/dist/tools/repository-tree.js +90 -0
  103. package/dist/tools/scaffold-rap.js +642 -0
  104. package/dist/tools/search.js +73 -0
  105. package/dist/tools/shared/data-format.js +101 -0
  106. package/dist/tools/sql-console.js +17 -0
  107. package/dist/tools/system-info.js +270 -0
  108. package/dist/tools/traces.js +66 -0
  109. package/dist/tools/transport-contents.js +83 -0
  110. package/dist/tools/transports.js +67 -0
  111. package/dist/tools/unit-test.js +135 -0
  112. package/dist/tools/where-used.js +59 -0
  113. package/dist/tools/write.js +101 -0
  114. package/package.json +49 -0
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapSearchTool = void 0;
4
+ exports.abapSearch = abapSearch;
5
+ const adt_client_js_1 = require("../adt-client.js");
6
+ function parseSearchResponse(xml) {
7
+ const results = [];
8
+ const regex = /adtcore:uri="([^"]+)"\s+adtcore:type="([^"]+)"\s+adtcore:name="([^"]+)"(?:\s+adtcore:packageName="([^"]*)")?(?:[^>]*adtcore:description="([^"]*)")?/g;
9
+ let match;
10
+ while ((match = regex.exec(xml)) !== null) {
11
+ results.push({
12
+ uri: match[1],
13
+ type: match[2],
14
+ name: match[3],
15
+ package: match[4] ?? "",
16
+ description: match[5],
17
+ });
18
+ }
19
+ return results;
20
+ }
21
+ function formatResults(results) {
22
+ if (results.length === 0)
23
+ return "Nenhum objeto encontrado.";
24
+ const lines = results.map((r) => {
25
+ const desc = r.description ? ` ${r.description}` : "";
26
+ return `${r.name.padEnd(40)} ${r.type.padEnd(10)} ${r.package}${desc}`;
27
+ });
28
+ const header = `${"Nome".padEnd(40)} ${"Tipo".padEnd(10)} Pacote`;
29
+ const separator = "-".repeat(80);
30
+ return [header, separator, ...lines].join("\n");
31
+ }
32
+ async function abapSearch(input) {
33
+ const { query, object_type, max_results = 20 } = input;
34
+ await (0, adt_client_js_1.ensureSession)();
35
+ const params = {
36
+ operation: "quickSearch",
37
+ query: query.toUpperCase(),
38
+ maxResults: String(max_results),
39
+ };
40
+ if (object_type) {
41
+ params.objectType = object_type;
42
+ }
43
+ const response = await adt_client_js_1.http.get("/repository/informationsystem/search", {
44
+ params,
45
+ headers: { Accept: "application/xml" },
46
+ responseType: "text",
47
+ });
48
+ const results = parseSearchResponse(response.data);
49
+ return formatResults(results);
50
+ }
51
+ exports.abapSearchTool = {
52
+ name: "abap_search",
53
+ description: "Pesquisa objetos ABAP no sistema SAP por nome (suporta wildcards com *). Retorna nome, tipo e pacote.",
54
+ inputSchema: {
55
+ type: "object",
56
+ properties: {
57
+ query: {
58
+ type: "string",
59
+ description: "Termo de busca. Suporta wildcard (*). Exemplos: 'ZR_SD_*', 'ZTESTEDENIS', 'Z*PEDIDO*'.",
60
+ },
61
+ object_type: {
62
+ type: "string",
63
+ description: "Filtrar por tipo de objeto (opcional). Exemplos: PROG/P, CLAS/OC, TABL/DT.",
64
+ enum: ["PROG/P", "CLAS/OC", "FUGR/FF", "DOMA/D", "DTEL/D", "TABL/DT", "INTF/OI", "DDLS/DF", "DDLX/MX", "SRVD/SRV", "BDEF/BDO", "SRVB/SVB"],
65
+ },
66
+ max_results: {
67
+ type: "number",
68
+ description: "Número máximo de resultados (padrão: 20, máximo recomendado: 100).",
69
+ },
70
+ },
71
+ required: ["query"],
72
+ },
73
+ };
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ /**
3
+ * Funções compartilhadas para parsing e formatação de data preview XML.
4
+ * Usadas por data-preview.ts e sql-console.ts.
5
+ *
6
+ * O ADT retorna dados em formato column-based:
7
+ * <columns>
8
+ * <metadata name="COL1" description="Desc" type="C" .../>
9
+ * <dataSet>
10
+ * <data>val1</data>
11
+ * <data>val2</data>
12
+ * </dataSet>
13
+ * </columns>
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.parseDataPreview = parseDataPreview;
17
+ exports.formatDataPreview = formatDataPreview;
18
+ function parseDataPreview(xml) {
19
+ const columns = [];
20
+ const columnData = [];
21
+ // Total rows
22
+ const totalMatch = xml.match(/<dataPreview:totalRows>(\d+)<\/dataPreview:totalRows>/);
23
+ const totalRows = totalMatch ? parseInt(totalMatch[1], 10) : 0;
24
+ // Query time
25
+ const timeMatch = xml.match(/<dataPreview:queryExecutionTime>([^<]+)<\/dataPreview:queryExecutionTime>/);
26
+ const queryTime = timeMatch?.[1];
27
+ // Table name
28
+ const nameMatch = xml.match(/<dataPreview:name>([^<]+)<\/dataPreview:name>/);
29
+ const tableName = nameMatch?.[1];
30
+ // Parse each <dataPreview:columns> block
31
+ const colBlockRegex = /<dataPreview:columns>([\s\S]*?)<\/dataPreview:columns>/gi;
32
+ let match;
33
+ while ((match = colBlockRegex.exec(xml)) !== null) {
34
+ const block = match[1];
35
+ // Metadata
36
+ const metaMatch = block.match(/<dataPreview:metadata([^/]*)\/>/i);
37
+ if (metaMatch) {
38
+ const attrs = metaMatch[1];
39
+ const name = /dataPreview:name="([^"]*)"/.exec(attrs)?.[1] ?? "";
40
+ const desc = /dataPreview:description="([^"]*)"/.exec(attrs)?.[1] ?? "";
41
+ const type = /dataPreview:colType="([^"]*)"/.exec(attrs)?.[1]
42
+ ?? /dataPreview:type="([^"]*)"/.exec(attrs)?.[1] ?? "";
43
+ const length = parseInt(/dataPreview:length="([^"]*)"/.exec(attrs)?.[1] ?? "0", 10);
44
+ const isKey = /dataPreview:keyAttribute="true"/.test(attrs);
45
+ columns.push({ name, description: desc, type, length, isKey });
46
+ }
47
+ // Data values for this column
48
+ const values = [];
49
+ const dataRegex = /<dataPreview:data\s*\/?>([^<]*)<\/dataPreview:data>|<dataPreview:data\s*\/>/gi;
50
+ let dataMatch;
51
+ // More robust: get all data tags including empty ones
52
+ const dataContent = block.match(/<dataPreview:dataSet>([\s\S]*?)<\/dataPreview:dataSet>/i)?.[1] ?? "";
53
+ const dataTagRegex = /<dataPreview:data\b[^>]*>([^<]*)<\/dataPreview:data>|<dataPreview:data\s*\/>/gi;
54
+ while ((dataMatch = dataTagRegex.exec(dataContent)) !== null) {
55
+ values.push(dataMatch[1]?.trim() ?? "");
56
+ }
57
+ columnData.push(values);
58
+ }
59
+ // Transpose column-based data to row-based
60
+ const numRows = columnData.length > 0 ? Math.max(...columnData.map((d) => d.length)) : 0;
61
+ const rows = [];
62
+ for (let r = 0; r < numRows; r++) {
63
+ const row = [];
64
+ for (let c = 0; c < columnData.length; c++) {
65
+ row.push(columnData[c][r] ?? "");
66
+ }
67
+ rows.push(row);
68
+ }
69
+ return { columns, rows, totalRows: numRows > 0 ? numRows : totalRows, queryTime, tableName };
70
+ }
71
+ function formatDataPreview(title, result) {
72
+ if (result.columns.length === 0 && result.rows.length === 0) {
73
+ return `${title}: nenhum dado retornado pelo data preview.`;
74
+ }
75
+ const lines = [];
76
+ const queryInfo = result.queryTime ? ` | Query: ${result.queryTime}s` : "";
77
+ lines.push(`Data Preview: ${result.tableName ?? title} (${result.totalRows} linhas${queryInfo})`);
78
+ lines.push("─".repeat(80));
79
+ if (result.columns.length > 0) {
80
+ const colNames = result.columns.map((c) => c.name);
81
+ const widths = colNames.map((col, i) => {
82
+ let max = col.length;
83
+ for (const row of result.rows) {
84
+ if (row[i] && row[i].length > max)
85
+ max = row[i].length;
86
+ }
87
+ return Math.min(max, 30);
88
+ });
89
+ const header = colNames.map((col, i) => col.padEnd(widths[i])).join(" | ");
90
+ lines.push(header);
91
+ lines.push(widths.map((w) => "─".repeat(w)).join("─┼─"));
92
+ for (const row of result.rows) {
93
+ const line = colNames.map((_, i) => {
94
+ const val = row[i] ?? "";
95
+ return val.substring(0, widths[i]).padEnd(widths[i]);
96
+ }).join(" | ");
97
+ lines.push(line);
98
+ }
99
+ }
100
+ return lines.join("\n");
101
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapSqlConsole = abapSqlConsole;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ const data_format_js_1 = require("./shared/data-format.js");
6
+ async function abapSqlConsole(input) {
7
+ const { sql, max_rows = 100 } = input;
8
+ const { data, status } = await (0, adt_client_js_1.adtPostText)("/datapreview/freestyle", sql, { rowNumber: String(max_rows) }, "application/vnd.sap.adt.datapreview.table.v1+xml");
9
+ if (status >= 400) {
10
+ // Extrair mensagem de erro do XML
11
+ const msgMatch = data.match(/<(?:message|exception)[^>]*>([\s\S]*?)<\/(?:message|exception)>/i);
12
+ const errorMsg = msgMatch?.[1]?.trim() ?? data.slice(0, 500);
13
+ throw new Error(`SQL Console erro (HTTP ${status}): ${errorMsg}`);
14
+ }
15
+ const result = (0, data_format_js_1.parseDataPreview)(data);
16
+ return (0, data_format_js_1.formatDataPreview)(`SQL Console (${max_rows} max rows)`, result);
17
+ }
@@ -0,0 +1,270 @@
1
+ "use strict";
2
+ /**
3
+ * abap_system_info — auto-detecta características do sistema SAP alvo.
4
+ *
5
+ * Probe via ADT REST APIs para determinar:
6
+ * - Tipo do sistema (S/4HANA on-prem, BTP, ECC)
7
+ * - Release e versão SAP_BASIS
8
+ * - Plataforma ABAP (Standard vs Cloud)
9
+ * - Topologia do gateway (Hub vs Embedded)
10
+ * - Tipo do mandante (Development vs Customizing)
11
+ * - Quirks (ETag, conversion exits, lock behavior)
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.abapSystemInfo = abapSystemInfo;
15
+ const adt_client_js_1 = require("../adt-client.js");
16
+ const system_profile_js_1 = require("../system-profile.js");
17
+ async function abapSystemInfo() {
18
+ await (0, adt_client_js_1.ensureSession)();
19
+ const results = [];
20
+ results.push("=== SAP System Profile Detection ===\n");
21
+ // 1. Probe /sap/bc/adt/discovery para release e capabilities
22
+ let discoveryXml = "";
23
+ try {
24
+ const resp = await adt_client_js_1.http.get("/discovery", {
25
+ headers: { Accept: "application/atomsvc+xml" },
26
+ responseType: "text",
27
+ validateStatus: (s) => s < 500,
28
+ });
29
+ discoveryXml = resp.data;
30
+ }
31
+ catch {
32
+ results.push("WARN: Não foi possível acessar /discovery");
33
+ }
34
+ // 2. Probe /sap/bc/adt/core/discovery para SID
35
+ let coreDiscoveryXml = "";
36
+ try {
37
+ const resp = await adt_client_js_1.http.get("/core/discovery", {
38
+ headers: { Accept: "application/xml" },
39
+ responseType: "text",
40
+ validateStatus: (s) => s < 500,
41
+ });
42
+ coreDiscoveryXml = resp.data;
43
+ }
44
+ catch {
45
+ // Não disponível em todos os sistemas
46
+ }
47
+ // Extrair informações do discovery XML
48
+ const systemId = extractAttr(coreDiscoveryXml, "systemId") ||
49
+ extractAttr(discoveryXml, "systemId") || "";
50
+ const generatorVersion = extractAttr(discoveryXml, "version") || "";
51
+ results.push(`System ID: ${systemId || "(não detectado)"}`);
52
+ results.push(`ADT Version: ${generatorVersion || "(não detectado)"}`);
53
+ // Detectar SAP_BASIS version via heurística nas collections do discovery
54
+ const sapBasisVersion = detectBasisVersionFromCollections(discoveryXml, generatorVersion);
55
+ results.push(`SAP_BASIS: ${sapBasisVersion}`);
56
+ // Detectar plataforma ABAP (Cloud vs Standard)
57
+ const hasCloudCollections = discoveryXml.includes("abapLanguageVersion") ||
58
+ discoveryXml.includes("cloudDevelopment");
59
+ const abapPlatform = hasCloudCollections ? "CLOUD" : "STANDARD";
60
+ results.push(`ABAP Platform: ${abapPlatform}`);
61
+ // Detectar tipo do sistema
62
+ const systemType = detectSystemType(discoveryXml, sapBasisVersion, abapPlatform);
63
+ results.push(`System Type: ${systemType}`);
64
+ // Detectar release S/4HANA
65
+ const release = detectRelease(sapBasisVersion);
66
+ results.push(`Release: ${release}`);
67
+ // 3. Probe ETag behavior
68
+ const etagQuirk = await probeEtagBehavior();
69
+ results.push(`ETag requires manual fix: ${etagQuirk}`);
70
+ // 4. Probe client role via publish endpoint
71
+ const clientRole = await probeClientRole();
72
+ results.push(`Client Role: ${clientRole}`);
73
+ // 5. Probe lock behavior para DDLS
74
+ const lockQuirk = await probeLockBehavior();
75
+ results.push(`Lock returns 406 for DDLS: ${lockQuirk}`);
76
+ // Montar profile baseado no preset + detecções
77
+ const presetName = systemType === "BTP_ABAP_ENV" ? "BTP_ABAP_ENV" :
78
+ systemType === "ON_PREMISE_ECC" ? "ON_PREMISE_ECC" :
79
+ "ON_PREMISE_S4";
80
+ const profile = (0, system_profile_js_1.getPreset)(presetName);
81
+ // Sobrescrever com valores detectados
82
+ profile.system.systemId = systemId;
83
+ profile.system.release = release;
84
+ profile.system.sapBasisVersion = sapBasisVersion;
85
+ profile.system.abapPlatform = abapPlatform;
86
+ profile.system.type = systemType;
87
+ profile.client.role = clientRole;
88
+ profile.client.allowLocalPublish = clientRole === "DEVELOPMENT";
89
+ profile.quirks.etagRequiresManualFix = etagQuirk;
90
+ profile.quirks.lockReturns406ForDDLS = lockQuirk;
91
+ // Capabilities derivadas da versão
92
+ const basisNum = parseInt(sapBasisVersion, 10) || 0;
93
+ profile.capabilities.cdsViewEntity = basisNum >= 756;
94
+ profile.capabilities.cdsProjection = basisNum >= 756;
95
+ profile.capabilities.rapManaged = basisNum >= 754;
96
+ profile.capabilities.rapDraftEnabled = basisNum >= 757;
97
+ profile.capabilities.releasedApiOnly = abapPlatform === "CLOUD";
98
+ // Aplicar profile globalmente
99
+ (0, system_profile_js_1.setProfile)(profile);
100
+ results.push("\n=== Profile aplicado ===");
101
+ results.push(JSON.stringify(profile, null, 2));
102
+ return results.join("\n");
103
+ }
104
+ // ─── Probes ──────────────────────────────────────────────────────
105
+ async function probeEtagBehavior() {
106
+ // Tenta GET + PUT num programa standard para verificar ETag mismatch
107
+ try {
108
+ const getResp = await adt_client_js_1.http.get("/programs/programs/rs_about/source/main", {
109
+ headers: { Accept: "text/plain" },
110
+ responseType: "text",
111
+ validateStatus: (s) => s < 500,
112
+ });
113
+ if (getResp.status !== 200)
114
+ return false;
115
+ const etag = getResp.headers["etag"];
116
+ if (!etag)
117
+ return false;
118
+ // Verifica se o ETag é numérico puro (format novo) vs contém charset (format antigo)
119
+ // No on-premise, o GET retorna ETag com sufixo diferente do esperado pelo PUT
120
+ // Heurística: se o ETag é só dígitos e tem mais de 14 chars, provavelmente tem quirk
121
+ const isNumericOnly = /^\d+$/.test(etag);
122
+ return isNumericOnly && etag.length > 14;
123
+ }
124
+ catch {
125
+ return false;
126
+ }
127
+ }
128
+ async function probeClientRole() {
129
+ try {
130
+ const csrf = await (0, adt_client_js_1.ensureSession)();
131
+ // Tenta publish de um binding que não existe — o erro revela o tipo do mandante
132
+ const resp = await adt_client_js_1.http.post("/businessservices/odatav4/publishjobs", `<?xml version="1.0" encoding="UTF-8"?>
133
+ <publish:publishRequest xmlns:publish="http://www.sap.com/adt/businessservices/odatav4">
134
+ <publish:serviceName>ZNONEXISTENT_PROBE_12345</publish:serviceName>
135
+ <publish:serviceVersion>0001</publish:serviceVersion>
136
+ </publish:publishRequest>`, {
137
+ headers: {
138
+ "Content-Type": "application/xml",
139
+ "X-CSRF-Token": csrf,
140
+ },
141
+ validateStatus: () => true,
142
+ });
143
+ const body = typeof resp.data === "string" ? resp.data : "";
144
+ if (body.includes("Customizing Client") || body.includes("customizing client")) {
145
+ return "CUSTOMIZING";
146
+ }
147
+ if (body.includes("not allowed") && body.includes("Production")) {
148
+ return "PRODUCTION";
149
+ }
150
+ // Se retornou erro de "not found" ou similar, o mandante permite publish
151
+ return "DEVELOPMENT";
152
+ }
153
+ catch {
154
+ return "UNKNOWN";
155
+ }
156
+ }
157
+ async function probeLockBehavior() {
158
+ try {
159
+ const csrf = await (0, adt_client_js_1.ensureSession)();
160
+ // Tenta lock de um objeto DDLS qualquer
161
+ const resp = await adt_client_js_1.http.post("/ddic/ddl/sources/i_businesspartner", null, {
162
+ headers: { "X-CSRF-Token": csrf },
163
+ params: { _action: "LOCK", accessMode: "MODIFY" },
164
+ validateStatus: () => true,
165
+ });
166
+ // Se retorna 406, confirma o quirk
167
+ if (resp.status === 406)
168
+ return true;
169
+ // Se conseguiu lock, faz unlock
170
+ if (resp.status < 300) {
171
+ await adt_client_js_1.http.post("/ddic/ddl/sources/i_businesspartner", null, {
172
+ headers: { "X-CSRF-Token": csrf },
173
+ params: { _action: "UNLOCK" },
174
+ validateStatus: () => true,
175
+ });
176
+ }
177
+ return false;
178
+ }
179
+ catch {
180
+ return false;
181
+ }
182
+ }
183
+ // ─── Helpers de parsing ──────────────────────────────────────────
184
+ function extractAttr(xml, attr) {
185
+ const regex = new RegExp(`${attr}="([^"]*)"`, "i");
186
+ const match = xml.match(regex);
187
+ return match?.[1] || "";
188
+ }
189
+ function extractBetween(xml, start, end) {
190
+ const si = xml.indexOf(start);
191
+ if (si < 0)
192
+ return "";
193
+ const ei = xml.indexOf(end, si + start.length);
194
+ if (ei < 0)
195
+ return "";
196
+ return xml.substring(si + start.length, ei).trim();
197
+ }
198
+ function detectBasisVersionFromCollections(discoveryXml, generatorVersion) {
199
+ // Tenta extrair do generator version (formato "SAP NetWeaver AS ABAP 758.0")
200
+ const vMatch = generatorVersion.match(/(\d{3,4})\.\d/);
201
+ if (vMatch)
202
+ return vMatch[1];
203
+ // Heurística baseada na presença de collections específicas no discovery XML
204
+ // Cada feature apareceu em uma versão específica do SAP_BASIS
205
+ const features = [
206
+ // [pattern no XML, versão mínima do BASIS]
207
+ ["abapLanguageVersion", "758"], // ABAP Cloud/Steampunk (S/4 2023)
208
+ ["behaviordefinitions/codecompletion", "757"], // RAP code completion (S/4 2022)
209
+ ["behaviordefinitions", "754"], // RAP BDEF support (S/4 1909)
210
+ ["ddic/ddlx/sources", "756"], // Metadata Extensions (S/4 2021)
211
+ ["ddic/srvd/sources", "754"], // Service Definitions (S/4 1909)
212
+ ["ServiceBindings", "754"], // Service Bindings
213
+ ["ddic/ddl/sources", "750"], // CDS views (S/4 1511+)
214
+ ["oo/classes", "700"], // Basic ABAP OO
215
+ ];
216
+ let detectedVersion = "750"; // default conservador
217
+ for (const [pattern, version] of features) {
218
+ if (discoveryXml.includes(pattern)) {
219
+ const current = parseInt(detectedVersion, 10);
220
+ const candidate = parseInt(version, 10);
221
+ if (candidate > current) {
222
+ detectedVersion = version;
223
+ }
224
+ }
225
+ }
226
+ return detectedVersion;
227
+ }
228
+ function detectSystemType(discoveryXml, basisVersion, platform) {
229
+ if (platform === "CLOUD")
230
+ return "BTP_ABAP_ENV";
231
+ const bv = parseInt(basisVersion, 10) || 0;
232
+ // BW tem collections específicas
233
+ if (discoveryXml.includes("bw/modelingtools") || discoveryXml.includes("bw/querydesigner")) {
234
+ return "SAP_BW";
235
+ }
236
+ // S/4HANA = BASIS 750+ com certas capabilities
237
+ if (bv >= 750) {
238
+ // Verifica se tem RAP-related collections (mais provável S/4)
239
+ if (discoveryXml.includes("behaviordefinitions") || discoveryXml.includes("ServiceBindings")) {
240
+ return "ON_PREMISE_S4";
241
+ }
242
+ }
243
+ // ECC ou sistema antigo
244
+ if (bv < 750)
245
+ return "ON_PREMISE_ECC";
246
+ // Default para on-premise S/4
247
+ return "ON_PREMISE_S4";
248
+ }
249
+ function detectRelease(basisVersion) {
250
+ const bv = parseInt(basisVersion, 10) || 0;
251
+ if (bv >= 758)
252
+ return "2023";
253
+ if (bv >= 757)
254
+ return "2022";
255
+ if (bv >= 756)
256
+ return "2021";
257
+ if (bv >= 755)
258
+ return "2020";
259
+ if (bv >= 754)
260
+ return "1909";
261
+ if (bv >= 753)
262
+ return "1809";
263
+ if (bv >= 752)
264
+ return "1709";
265
+ if (bv >= 751)
266
+ return "1610";
267
+ if (bv >= 750)
268
+ return "1511";
269
+ return basisVersion;
270
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapTraces = abapTraces;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapTraces(input) {
6
+ const { action = "list", object_type, object_name, user } = input;
7
+ if (action === "list") {
8
+ const xml = await (0, adt_client_js_1.adtGetXml)("/runtime/traces");
9
+ // Parse trace entries
10
+ const traces = [];
11
+ const traceRegex = /<(?:trace:)?(?:trace|entry)[^>]*>([\s\S]*?)<\/(?:trace:)?(?:trace|entry)>/gi;
12
+ let match;
13
+ while ((match = traceRegex.exec(xml)) !== null) {
14
+ const content = match[0];
15
+ const idMatch = content.match(/(?:trace:)?id\s*=\s*"([^"]*)"/i);
16
+ const dateMatch = content.match(/(?:trace:)?(?:date|timestamp)\s*=\s*"([^"]*)"/i);
17
+ const userMatch = content.match(/(?:trace:)?(?:user|owner)\s*=\s*"([^"]*)"/i);
18
+ const descMatch = content.match(/(?:trace:)?description\s*=\s*"([^"]*)"/i);
19
+ const statusMatch = content.match(/(?:trace:)?status\s*=\s*"([^"]*)"/i);
20
+ if (idMatch || dateMatch) {
21
+ traces.push({
22
+ id: idMatch?.[1] ?? "",
23
+ date: dateMatch?.[1] ?? "",
24
+ user: userMatch?.[1] ?? "",
25
+ description: descMatch?.[1] ?? "",
26
+ status: statusMatch?.[1] ?? "",
27
+ });
28
+ }
29
+ }
30
+ if (traces.length === 0) {
31
+ return "Nenhum trace ABAP encontrado.";
32
+ }
33
+ const lines = [];
34
+ lines.push(`ABAP Traces (SAT): ${traces.length} trace(s)`);
35
+ lines.push("═".repeat(60));
36
+ for (const t of traces) {
37
+ const status = t.status ? ` [${t.status}]` : "";
38
+ lines.push(` ${t.id} | ${t.date} | ${t.user}${status}`);
39
+ if (t.description)
40
+ lines.push(` ${t.description}`);
41
+ }
42
+ return lines.join("\n");
43
+ }
44
+ else if (action === "create") {
45
+ if (!object_type || !object_name) {
46
+ throw new Error("object_type e object_name são obrigatórios para criar um trace.");
47
+ }
48
+ const name = object_name.toUpperCase();
49
+ const adtPath = (0, adt_client_js_1.resolveAdtPath)(object_type);
50
+ const objectUri = `/sap/bc/adt/${adtPath}/${name}`;
51
+ const body = `<?xml version="1.0" encoding="UTF-8"?>
52
+ <trace:traceConfiguration xmlns:trace="http://www.sap.com/adt/runtime/traces"
53
+ xmlns:adtcore="http://www.sap.com/adt/core">
54
+ <adtcore:objectReference adtcore:uri="${objectUri}" adtcore:name="${name}"/>
55
+ ${user ? `<trace:user>${user}</trace:user>` : ""}
56
+ </trace:traceConfiguration>`;
57
+ const { data, status } = await (0, adt_client_js_1.adtPostXml)("/runtime/traces", body);
58
+ if (status >= 400) {
59
+ const msgMatch = data.match(/<(?:message|shortText)[^>]*>([\s\S]*?)<\/(?:message|shortText)>/i);
60
+ throw new Error(`Criação de trace falhou (HTTP ${status}): ${msgMatch?.[1] ?? data.slice(0, 500)}`);
61
+ }
62
+ return `Configuração de trace criada para ${name} (${object_type}).` +
63
+ (user ? `\nUsuário: ${user}` : "");
64
+ }
65
+ throw new Error(`Ação desconhecida: ${action}. Use "list" ou "create".`);
66
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.abapTransportContents = abapTransportContents;
4
+ const adt_client_js_1 = require("../adt-client.js");
5
+ async function abapTransportContents(input) {
6
+ const { transport_request } = input;
7
+ const trId = transport_request.toUpperCase();
8
+ await (0, adt_client_js_1.ensureSession)();
9
+ const response = await adt_client_js_1.http.get(`/cts/transportrequests/${trId}`, {
10
+ headers: { Accept: "application/vnd.sap.adt.transportorganizer.v1+xml" },
11
+ responseType: "text",
12
+ validateStatus: (status) => status < 500,
13
+ });
14
+ if (response.status >= 400) {
15
+ throw new Error(`Transporte ${trId} não encontrado (HTTP ${response.status}).`);
16
+ }
17
+ const xml = response.data;
18
+ // Parse transport header
19
+ const descMatch = xml.match(/(?:tm:)?desc(?:ription)?\s*=\s*"([^"]*)"/i);
20
+ const ownerMatch = xml.match(/(?:tm:)?owner\s*=\s*"([^"]*)"/i);
21
+ const targetMatch = xml.match(/(?:tm:)?target(?:System)?\s*=\s*"([^"]*)"/i);
22
+ const statusMatch = xml.match(/(?:tm:)?status\s*=\s*"([^"]*)"/i);
23
+ // Parse objects
24
+ const objects = [];
25
+ const objRegex = /<(?:tm:)?(?:abapObject|object)[^>]*>/gi;
26
+ let match;
27
+ while ((match = objRegex.exec(xml)) !== null) {
28
+ const el = match[0];
29
+ const typeMatch = el.match(/(?:adtcore:type|tm:type|type)\s*=\s*"([^"]*)"/i);
30
+ const nameMatch = el.match(/(?:adtcore:name|tm:name|tm:objName|name)\s*=\s*"([^"]*)"/i);
31
+ const pgmidMatch = el.match(/(?:tm:pgmid|pgmid)\s*=\s*"([^"]*)"/i);
32
+ if (nameMatch) {
33
+ objects.push({
34
+ pgmid: pgmidMatch?.[1] ?? "R3TR",
35
+ type: typeMatch?.[1] ?? "",
36
+ name: nameMatch[1],
37
+ });
38
+ }
39
+ }
40
+ // Parse tasks
41
+ const tasks = [];
42
+ const taskRegex = /<(?:tm:)?task[^>]*>/gi;
43
+ while ((match = taskRegex.exec(xml)) !== null) {
44
+ const el = match[0];
45
+ const idMatch = el.match(/(?:tm:)?(?:number|id)\s*=\s*"([^"]*)"/i);
46
+ const tOwner = el.match(/(?:tm:)?owner\s*=\s*"([^"]*)"/i);
47
+ const tDesc = el.match(/(?:tm:)?desc(?:ription)?\s*=\s*"([^"]*)"/i);
48
+ if (idMatch) {
49
+ tasks.push({
50
+ id: idMatch[1],
51
+ owner: tOwner?.[1] ?? "",
52
+ desc: tDesc?.[1] ?? "",
53
+ });
54
+ }
55
+ }
56
+ const lines = [];
57
+ lines.push(`Transporte: ${trId}`);
58
+ lines.push("═".repeat(60));
59
+ if (descMatch)
60
+ lines.push(`Descrição: ${descMatch[1]}`);
61
+ if (ownerMatch)
62
+ lines.push(`Owner: ${ownerMatch[1]}`);
63
+ if (targetMatch)
64
+ lines.push(`Destino: ${targetMatch[1]}`);
65
+ if (statusMatch)
66
+ lines.push(`Status: ${statusMatch[1]}`);
67
+ if (tasks.length > 0) {
68
+ lines.push(`\nTasks (${tasks.length}):`);
69
+ for (const t of tasks) {
70
+ lines.push(` ${t.id} — ${t.owner} — ${t.desc}`);
71
+ }
72
+ }
73
+ if (objects.length > 0) {
74
+ lines.push(`\nObjetos (${objects.length}):`);
75
+ for (const o of objects) {
76
+ lines.push(` ${o.pgmid} ${o.type} ${o.name}`);
77
+ }
78
+ }
79
+ else {
80
+ lines.push(`\nNenhum objeto na ordem.`);
81
+ }
82
+ return lines.join("\n");
83
+ }