@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.
- package/README.md +384 -0
- package/dist/adt-client.js +364 -0
- package/dist/cli/activate.js +113 -0
- package/dist/cli/init.js +333 -0
- package/dist/cli/remove.js +80 -0
- package/dist/cli/status.js +229 -0
- package/dist/cli/systems.js +68 -0
- package/dist/cli.js +81 -0
- package/dist/index.js +1318 -0
- package/dist/knowledge/abap/abap-dictionary.md +199 -0
- package/dist/knowledge/abap/abap-sql.md +296 -0
- package/dist/knowledge/abap/amdp.md +273 -0
- package/dist/knowledge/abap/clean-code.md +293 -0
- package/dist/knowledge/abap/cloud-background-processing.md +250 -0
- package/dist/knowledge/abap/cloud-communication.md +265 -0
- package/dist/knowledge/abap/cloud-development.md +176 -0
- package/dist/knowledge/abap/cloud-extensibility.md +252 -0
- package/dist/knowledge/abap/cloud-released-apis.md +261 -0
- package/dist/knowledge/abap/constructor-expressions.md +289 -0
- package/dist/knowledge/abap/enhancements.md +232 -0
- package/dist/knowledge/abap/exceptions.md +271 -0
- package/dist/knowledge/abap/internal-tables.md +205 -0
- package/dist/knowledge/abap/object-orientation.md +298 -0
- package/dist/knowledge/abap/performance.md +216 -0
- package/dist/knowledge/abap/rap-abstract-entities.md +206 -0
- package/dist/knowledge/abap/rap-business-events.md +216 -0
- package/dist/knowledge/abap/rap-draft.md +191 -0
- package/dist/knowledge/abap/rap-eml.md +453 -0
- package/dist/knowledge/abap/rap-end-to-end.md +486 -0
- package/dist/knowledge/abap/rap-feature-control.md +185 -0
- package/dist/knowledge/abap/rap-numbering.md +280 -0
- package/dist/knowledge/abap/rap-service-exposure.md +163 -0
- package/dist/knowledge/abap/rap-unmanaged.md +468 -0
- package/dist/knowledge/abap/string-processing.md +180 -0
- package/dist/knowledge/abap/unit-testing.md +303 -0
- package/dist/knowledge/abap-cds/access-control.md +241 -0
- package/dist/knowledge/abap-cds/annotations.md +331 -0
- package/dist/knowledge/abap-cds/associations.md +254 -0
- package/dist/knowledge/abap-cds/expressions.md +230 -0
- package/dist/knowledge/abap-cds/functions.md +245 -0
- package/dist/knowledge/abap-cds/metadata-extensions.md +294 -0
- package/dist/knowledge/cap/authentication.md +278 -0
- package/dist/knowledge/cap/cdl-syntax.md +247 -0
- package/dist/knowledge/cap/cql-queries.md +266 -0
- package/dist/knowledge/cap/deployment.md +343 -0
- package/dist/knowledge/cap/event-handlers.md +287 -0
- package/dist/knowledge/cap/fiori-integration.md +303 -0
- package/dist/knowledge/cap/service-definitions.md +287 -0
- package/dist/knowledge/fiori/annotations.md +347 -0
- package/dist/knowledge/fiori/deployment.md +340 -0
- package/dist/knowledge/fiori/fiori-elements.md +332 -0
- package/dist/knowledge/fiori/fiori-side-effects.md +107 -0
- package/dist/knowledge/fiori/fiori-valuelist.md +144 -0
- package/dist/knowledge/fiori/ui5-controllers.md +358 -0
- package/dist/knowledge/fiori/ui5-data-binding.md +311 -0
- package/dist/knowledge/fiori/ui5-fragments-dialogs.md +330 -0
- package/dist/knowledge/fiori/ui5-manifest.md +411 -0
- package/dist/knowledge/fiori/ui5-routing.md +303 -0
- package/dist/knowledge/fiori/ui5-xml-views.md +294 -0
- package/dist/logger.js +114 -0
- package/dist/system-profile.js +207 -0
- package/dist/tools/abap-doc.js +72 -0
- package/dist/tools/abapgit.js +161 -0
- package/dist/tools/activate.js +68 -0
- package/dist/tools/atc-check.js +117 -0
- package/dist/tools/auth-object.js +56 -0
- package/dist/tools/breakpoints.js +76 -0
- package/dist/tools/call-hierarchy.js +84 -0
- package/dist/tools/cds-annotations.js +98 -0
- package/dist/tools/cds-dependencies.js +65 -0
- package/dist/tools/check.js +47 -0
- package/dist/tools/code-completion.js +70 -0
- package/dist/tools/code-coverage.js +111 -0
- package/dist/tools/create-amdp.js +111 -0
- package/dist/tools/create-dcl.js +81 -0
- package/dist/tools/create-transport.js +38 -0
- package/dist/tools/create.js +285 -0
- package/dist/tools/data-preview.js +37 -0
- package/dist/tools/delete.js +45 -0
- package/dist/tools/deploy-bsp.js +298 -0
- package/dist/tools/discovery.js +59 -0
- package/dist/tools/element-info.js +93 -0
- package/dist/tools/enhancements.js +186 -0
- package/dist/tools/extract-method.js +44 -0
- package/dist/tools/function-group.js +59 -0
- package/dist/tools/knowledge.js +275 -0
- package/dist/tools/lock-object.js +75 -0
- package/dist/tools/message-class.js +67 -0
- package/dist/tools/navigate.js +80 -0
- package/dist/tools/number-range.js +57 -0
- package/dist/tools/object-documentation.js +43 -0
- package/dist/tools/object-structure.js +78 -0
- package/dist/tools/object-versions.js +57 -0
- package/dist/tools/package-contents.js +60 -0
- package/dist/tools/pretty-printer.js +35 -0
- package/dist/tools/publish-binding.js +49 -0
- package/dist/tools/quick-fix.js +69 -0
- package/dist/tools/read.js +167 -0
- package/dist/tools/refactor-rename.js +60 -0
- package/dist/tools/release-transport.js +24 -0
- package/dist/tools/released-apis.js +51 -0
- package/dist/tools/repository-tree.js +90 -0
- package/dist/tools/scaffold-rap.js +642 -0
- package/dist/tools/search.js +73 -0
- package/dist/tools/shared/data-format.js +101 -0
- package/dist/tools/sql-console.js +17 -0
- package/dist/tools/system-info.js +270 -0
- package/dist/tools/traces.js +66 -0
- package/dist/tools/transport-contents.js +83 -0
- package/dist/tools/transports.js +67 -0
- package/dist/tools/unit-test.js +135 -0
- package/dist/tools/where-used.js +59 -0
- package/dist/tools/write.js +101 -0
- package/package.json +49 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.abapKnowledge = abapKnowledge;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
// Domain display labels
|
|
40
|
+
const DOMAIN_LABELS = {
|
|
41
|
+
abap: "ABAP Core",
|
|
42
|
+
"abap-cds": "ABAP CDS",
|
|
43
|
+
fiori: "Fiori / UI5",
|
|
44
|
+
cap: "CAP (Node.js)",
|
|
45
|
+
};
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Knowledge Store (loaded once at module init)
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
const topics = [];
|
|
50
|
+
let loaded = false;
|
|
51
|
+
function resolveKnowledgeDir() {
|
|
52
|
+
// In dist/tools/knowledge.js → ../../knowledge (if copied to dist/)
|
|
53
|
+
const fromDist = path.join(__dirname, "..", "knowledge");
|
|
54
|
+
if (fs.existsSync(fromDist))
|
|
55
|
+
return fromDist;
|
|
56
|
+
// Fallback: project root src/knowledge/
|
|
57
|
+
return path.join(process.cwd(), "src", "knowledge");
|
|
58
|
+
}
|
|
59
|
+
function extractHeaders(content) {
|
|
60
|
+
return content
|
|
61
|
+
.split("\n")
|
|
62
|
+
.filter((l) => l.startsWith("## "))
|
|
63
|
+
.map((l) => l.replace(/^#+\s*/, "").toLowerCase())
|
|
64
|
+
.join(" ");
|
|
65
|
+
}
|
|
66
|
+
function loadKnowledgeBase() {
|
|
67
|
+
if (loaded)
|
|
68
|
+
return;
|
|
69
|
+
const baseDir = resolveKnowledgeDir();
|
|
70
|
+
if (!fs.existsSync(baseDir)) {
|
|
71
|
+
loaded = true;
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const domains = fs
|
|
75
|
+
.readdirSync(baseDir, { withFileTypes: true })
|
|
76
|
+
.filter((d) => d.isDirectory())
|
|
77
|
+
.map((d) => d.name);
|
|
78
|
+
for (const domain of domains) {
|
|
79
|
+
const domainDir = path.join(baseDir, domain);
|
|
80
|
+
const files = fs
|
|
81
|
+
.readdirSync(domainDir)
|
|
82
|
+
.filter((f) => f.endsWith(".md"))
|
|
83
|
+
.sort();
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
const id = file.replace(/\.md$/, "");
|
|
86
|
+
const content = fs.readFileSync(path.join(domainDir, file), "utf-8");
|
|
87
|
+
const firstLine = content.split("\n")[0] || "";
|
|
88
|
+
// Parse: "# Title — scope description"
|
|
89
|
+
const match = firstLine.match(/^#\s+(.+?)\s+—\s+(.+)$/);
|
|
90
|
+
const title = match ? match[1].trim() : id;
|
|
91
|
+
const scope = match ? match[2].trim() : "";
|
|
92
|
+
const headers = extractHeaders(content);
|
|
93
|
+
const keywords = `${title} ${scope} ${headers} ${id.replace(/-/g, " ")}`.toLowerCase();
|
|
94
|
+
topics.push({ id, domain, title, scope, content, keywords });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
loaded = true;
|
|
98
|
+
}
|
|
99
|
+
// Auto-load on module import
|
|
100
|
+
loadKnowledgeBase();
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Actions
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
function knowledgeList(domain) {
|
|
105
|
+
const filtered = domain ? topics.filter((t) => t.domain === domain) : topics;
|
|
106
|
+
if (filtered.length === 0) {
|
|
107
|
+
return domain
|
|
108
|
+
? `Nenhum tópico encontrado no domínio "${domain}".`
|
|
109
|
+
: "Base de conhecimento vazia.";
|
|
110
|
+
}
|
|
111
|
+
// Group by domain
|
|
112
|
+
const grouped = new Map();
|
|
113
|
+
for (const t of filtered) {
|
|
114
|
+
const list = grouped.get(t.domain) || [];
|
|
115
|
+
list.push(t);
|
|
116
|
+
grouped.set(t.domain, list);
|
|
117
|
+
}
|
|
118
|
+
const lines = [];
|
|
119
|
+
lines.push(`📚 Base de Conhecimento SAP — ${filtered.length} guias disponíveis\n`);
|
|
120
|
+
for (const [dom, items] of grouped) {
|
|
121
|
+
const label = DOMAIN_LABELS[dom] || dom;
|
|
122
|
+
lines.push(`## ${label} (${items.length})`);
|
|
123
|
+
for (const t of items) {
|
|
124
|
+
const scopePart = t.scope ? ` — ${t.scope}` : "";
|
|
125
|
+
lines.push(` • ${t.domain}/${t.id}: ${t.title}${scopePart}`);
|
|
126
|
+
}
|
|
127
|
+
lines.push("");
|
|
128
|
+
}
|
|
129
|
+
lines.push("💡 Use action='get' com topics=['domain/id'] para ler o conteúdo completo.");
|
|
130
|
+
lines.push("💡 Use action='search' com query='palavra-chave' para buscar por assunto.");
|
|
131
|
+
return lines.join("\n");
|
|
132
|
+
}
|
|
133
|
+
function knowledgeGet(topicIds) {
|
|
134
|
+
if (!topicIds || topicIds.length === 0) {
|
|
135
|
+
return "Erro: informe ao menos um tópico. Use action='list' para ver os disponíveis.";
|
|
136
|
+
}
|
|
137
|
+
if (topicIds.length > 5) {
|
|
138
|
+
return "Erro: máximo 5 tópicos por chamada para não sobrecarregar o contexto. Selecione os mais relevantes.";
|
|
139
|
+
}
|
|
140
|
+
const results = [];
|
|
141
|
+
const notFound = [];
|
|
142
|
+
for (const rawId of topicIds) {
|
|
143
|
+
const topic = resolveTopic(rawId);
|
|
144
|
+
if (topic) {
|
|
145
|
+
results.push(topic.content);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
notFound.push(rawId);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (notFound.length > 0) {
|
|
152
|
+
const suggestions = notFound.map((id) => {
|
|
153
|
+
const similar = findSimilar(id, 3);
|
|
154
|
+
if (similar.length > 0) {
|
|
155
|
+
return ` "${id}" → você quis dizer: ${similar.map((s) => s.domain + "/" + s.id).join(", ")}?`;
|
|
156
|
+
}
|
|
157
|
+
return ` "${id}" → não encontrado`;
|
|
158
|
+
});
|
|
159
|
+
results.push(`\n⚠️ Tópicos não encontrados:\n${suggestions.join("\n")}`);
|
|
160
|
+
}
|
|
161
|
+
return results.join("\n\n---\n\n");
|
|
162
|
+
}
|
|
163
|
+
function knowledgeSearch(query, domain) {
|
|
164
|
+
if (!query || query.trim().length === 0) {
|
|
165
|
+
return "Erro: informe um termo de busca. Ex: query='draft', query='annotations', query='BAPI'.";
|
|
166
|
+
}
|
|
167
|
+
const queryLower = query.toLowerCase();
|
|
168
|
+
const queryWords = queryLower.split(/\s+/).filter((w) => w.length > 1);
|
|
169
|
+
const candidates = domain ? topics.filter((t) => t.domain === domain) : topics;
|
|
170
|
+
// Score each topic
|
|
171
|
+
const scored = candidates.map((t) => {
|
|
172
|
+
let score = 0;
|
|
173
|
+
for (const word of queryWords) {
|
|
174
|
+
// Exact ID match (highest weight)
|
|
175
|
+
if (t.id === word || t.id.includes(word))
|
|
176
|
+
score += 10;
|
|
177
|
+
// Title match
|
|
178
|
+
if (t.title.toLowerCase().includes(word))
|
|
179
|
+
score += 8;
|
|
180
|
+
// Scope match
|
|
181
|
+
if (t.scope.toLowerCase().includes(word))
|
|
182
|
+
score += 6;
|
|
183
|
+
// Keyword/header match
|
|
184
|
+
if (t.keywords.includes(word))
|
|
185
|
+
score += 3;
|
|
186
|
+
// Content match (count occurrences, capped)
|
|
187
|
+
const contentLower = t.content.toLowerCase();
|
|
188
|
+
const occurrences = contentLower.split(word).length - 1;
|
|
189
|
+
score += Math.min(occurrences, 5) * 1;
|
|
190
|
+
}
|
|
191
|
+
return { topic: t, score };
|
|
192
|
+
});
|
|
193
|
+
// Filter and sort
|
|
194
|
+
const matches = scored
|
|
195
|
+
.filter((s) => s.score > 0)
|
|
196
|
+
.sort((a, b) => b.score - a.score)
|
|
197
|
+
.slice(0, 10);
|
|
198
|
+
if (matches.length === 0) {
|
|
199
|
+
return `Nenhum resultado para "${query}". Use action='list' para ver todos os tópicos disponíveis.`;
|
|
200
|
+
}
|
|
201
|
+
const lines = [];
|
|
202
|
+
lines.push(`🔍 Busca: "${query}" — ${matches.length} resultado(s)\n`);
|
|
203
|
+
for (const { topic: t, score } of matches) {
|
|
204
|
+
const relevance = score >= 15 ? "🟢" : score >= 8 ? "🟡" : "⚪";
|
|
205
|
+
const scopePart = t.scope ? ` — ${t.scope}` : "";
|
|
206
|
+
lines.push(`${relevance} ${t.domain}/${t.id}: ${t.title}${scopePart}`);
|
|
207
|
+
// Show first relevant snippet (first ## section that matches)
|
|
208
|
+
const snippet = extractSnippet(t.content, queryWords);
|
|
209
|
+
if (snippet) {
|
|
210
|
+
lines.push(` 📎 ${snippet}`);
|
|
211
|
+
}
|
|
212
|
+
lines.push("");
|
|
213
|
+
}
|
|
214
|
+
lines.push("💡 Use action='get' com topics=['domain/id'] para ler o conteúdo completo.");
|
|
215
|
+
return lines.join("\n");
|
|
216
|
+
}
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// Helpers
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
function resolveTopic(rawId) {
|
|
221
|
+
// Try domain-qualified: "abap/rap-eml"
|
|
222
|
+
if (rawId.includes("/")) {
|
|
223
|
+
const [domain, id] = rawId.split("/", 2);
|
|
224
|
+
return topics.find((t) => t.domain === domain && t.id === id);
|
|
225
|
+
}
|
|
226
|
+
// Bare ID: try exact match (unique across domains)
|
|
227
|
+
const exactMatches = topics.filter((t) => t.id === rawId);
|
|
228
|
+
if (exactMatches.length === 1)
|
|
229
|
+
return exactMatches[0];
|
|
230
|
+
// Multiple matches (e.g. "annotations" exists in abap-cds and fiori)
|
|
231
|
+
// Return first but this is ambiguous — search would have shown domain-qualified IDs
|
|
232
|
+
if (exactMatches.length > 1)
|
|
233
|
+
return exactMatches[0];
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
function findSimilar(rawId, max) {
|
|
237
|
+
const search = rawId.replace(/^[^/]+\//, "").toLowerCase();
|
|
238
|
+
return topics
|
|
239
|
+
.filter((t) => t.id.includes(search) || t.keywords.includes(search))
|
|
240
|
+
.slice(0, max);
|
|
241
|
+
}
|
|
242
|
+
function extractSnippet(content, queryWords) {
|
|
243
|
+
const lines = content.split("\n");
|
|
244
|
+
for (const line of lines) {
|
|
245
|
+
const lower = line.toLowerCase();
|
|
246
|
+
if (line.startsWith("#"))
|
|
247
|
+
continue;
|
|
248
|
+
if (line.startsWith("```"))
|
|
249
|
+
continue;
|
|
250
|
+
if (line.trim().length < 10)
|
|
251
|
+
continue;
|
|
252
|
+
if (queryWords.some((w) => lower.includes(w))) {
|
|
253
|
+
const cleaned = line.replace(/^\s*[-*•]\s*/, "").trim();
|
|
254
|
+
return cleaned.length > 120 ? cleaned.substring(0, 117) + "..." : cleaned;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return "";
|
|
258
|
+
}
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// Main dispatcher
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
async function abapKnowledge(input) {
|
|
263
|
+
// Ensure loaded (no-op if already loaded)
|
|
264
|
+
loadKnowledgeBase();
|
|
265
|
+
switch (input.action) {
|
|
266
|
+
case "list":
|
|
267
|
+
return knowledgeList(input.domain);
|
|
268
|
+
case "get":
|
|
269
|
+
return knowledgeGet(input.topics || []);
|
|
270
|
+
case "search":
|
|
271
|
+
return knowledgeSearch(input.query || "", input.domain);
|
|
272
|
+
default:
|
|
273
|
+
return `Ação "${input.action}" inválida. Use: list, get ou search.`;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapLockObject = abapLockObject;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapLockObject(input) {
|
|
6
|
+
const { object_name } = input;
|
|
7
|
+
const name = object_name.toUpperCase();
|
|
8
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
9
|
+
const response = await adt_client_js_1.http.get(`/ddic/lockobjects/sources/${name.toLowerCase()}`, {
|
|
10
|
+
headers: { Accept: "application/vnd.sap.adt.lockobjects.v1+xml" },
|
|
11
|
+
responseType: "text",
|
|
12
|
+
validateStatus: (status) => status < 500,
|
|
13
|
+
});
|
|
14
|
+
if (response.status === 404) {
|
|
15
|
+
throw new Error(`Lock object ${name} não encontrado.`);
|
|
16
|
+
}
|
|
17
|
+
const xml = response.data;
|
|
18
|
+
// Parse lock object details
|
|
19
|
+
const descMatch = xml.match(/adtcore:description\s*=\s*"([^"]*)"/i);
|
|
20
|
+
const modeMatch = xml.match(/(?:lock:)?lockMode\s*=\s*"([^"]*)"/i);
|
|
21
|
+
// Parse lock parameters
|
|
22
|
+
const params = [];
|
|
23
|
+
const paramRegex = /<(?:lock:)?(?:parameter|lockParameter)[^>]*>/gi;
|
|
24
|
+
let match;
|
|
25
|
+
while ((match = paramRegex.exec(xml)) !== null) {
|
|
26
|
+
const el = match[0];
|
|
27
|
+
const nameMatch = el.match(/(?:adtcore:)?name\s*=\s*"([^"]*)"/i);
|
|
28
|
+
const typeMatch = el.match(/(?:adtcore:)?(?:type|dataType)\s*=\s*"([^"]*)"/i);
|
|
29
|
+
const descMatch2 = el.match(/(?:adtcore:)?description\s*=\s*"([^"]*)"/i);
|
|
30
|
+
if (nameMatch) {
|
|
31
|
+
params.push({
|
|
32
|
+
name: nameMatch[1],
|
|
33
|
+
type: typeMatch?.[1] ?? "",
|
|
34
|
+
description: descMatch2?.[1] ?? "",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Parse tables
|
|
39
|
+
const tables = [];
|
|
40
|
+
const tableRegex = /<(?:lock:)?(?:table|lockTable)[^>]*>/gi;
|
|
41
|
+
while ((match = tableRegex.exec(xml)) !== null) {
|
|
42
|
+
const el = match[0];
|
|
43
|
+
const nameMatch = el.match(/(?:adtcore:)?name\s*=\s*"([^"]*)"/i);
|
|
44
|
+
const modeMatch2 = el.match(/(?:lock:)?(?:lockMode|mode)\s*=\s*"([^"]*)"/i);
|
|
45
|
+
if (nameMatch) {
|
|
46
|
+
tables.push({
|
|
47
|
+
name: nameMatch[1],
|
|
48
|
+
lockMode: modeMatch2?.[1] ?? "",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const lines = [];
|
|
53
|
+
lines.push(`Lock Object: ${name}`);
|
|
54
|
+
lines.push("═".repeat(50));
|
|
55
|
+
if (descMatch)
|
|
56
|
+
lines.push(`Descrição: ${descMatch[1]}`);
|
|
57
|
+
if (modeMatch)
|
|
58
|
+
lines.push(`Modo: ${modeMatch[1]}`);
|
|
59
|
+
if (tables.length > 0) {
|
|
60
|
+
lines.push(`\nTabelas (${tables.length}):`);
|
|
61
|
+
for (const t of tables) {
|
|
62
|
+
const mode = t.lockMode ? ` (modo: ${t.lockMode})` : "";
|
|
63
|
+
lines.push(` ${t.name}${mode}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (params.length > 0) {
|
|
67
|
+
lines.push(`\nParâmetros (${params.length}):`);
|
|
68
|
+
for (const p of params) {
|
|
69
|
+
const type = p.type ? ` [${p.type}]` : "";
|
|
70
|
+
const desc = p.description ? ` — ${p.description}` : "";
|
|
71
|
+
lines.push(` ${p.name}${type}${desc}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapMessageClass = abapMessageClass;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function parseMessages(xml) {
|
|
6
|
+
const messages = [];
|
|
7
|
+
// Formato ADT: elementos com número e texto
|
|
8
|
+
const msgRegex = /<(?:[a-zA-Z0-9_:]*)?message[^>]*?(?:number|msgno)\s*=\s*"([^"]*)"[^>]*>/gi;
|
|
9
|
+
let match;
|
|
10
|
+
while ((match = msgRegex.exec(xml)) !== null) {
|
|
11
|
+
const element = match[0];
|
|
12
|
+
const number = match[1];
|
|
13
|
+
const textMatch = element.match(/(?:shortText|text|description)\s*=\s*"([^"]*)"/i);
|
|
14
|
+
messages.push({
|
|
15
|
+
number,
|
|
16
|
+
shortText: textMatch?.[1] ?? "",
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
// Fallback: tentar formato alternativo com sub-elementos
|
|
20
|
+
if (messages.length === 0) {
|
|
21
|
+
const altRegex = /<(?:[a-zA-Z0-9_:]*)?message[^>]*>([\s\S]*?)<\/(?:[a-zA-Z0-9_:]*)?message>/gi;
|
|
22
|
+
while ((match = altRegex.exec(xml)) !== null) {
|
|
23
|
+
const content = match[0] + match[1];
|
|
24
|
+
const numMatch = content.match(/(?:number|msgno)\s*=\s*"([^"]*)"/i);
|
|
25
|
+
const textMatch = content.match(/(?:shortText|text)[^>]*>([^<]*)</i);
|
|
26
|
+
if (numMatch) {
|
|
27
|
+
messages.push({
|
|
28
|
+
number: numMatch[1],
|
|
29
|
+
shortText: textMatch?.[1]?.trim() ?? "",
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return messages;
|
|
35
|
+
}
|
|
36
|
+
function formatMessages(className, messages) {
|
|
37
|
+
if (messages.length === 0) {
|
|
38
|
+
return `Classe de mensagem ${className}: nenhuma mensagem encontrada.`;
|
|
39
|
+
}
|
|
40
|
+
const lines = [
|
|
41
|
+
`Classe de mensagem ${className} (${messages.length} mensagens)`,
|
|
42
|
+
"─".repeat(60),
|
|
43
|
+
`${"Nº".padEnd(8)} Texto`,
|
|
44
|
+
"─".repeat(60),
|
|
45
|
+
];
|
|
46
|
+
// Ordenar por número
|
|
47
|
+
const sorted = [...messages].sort((a, b) => a.number.localeCompare(b.number));
|
|
48
|
+
for (const msg of sorted) {
|
|
49
|
+
lines.push(`${msg.number.padEnd(8)} ${msg.shortText}`);
|
|
50
|
+
}
|
|
51
|
+
return lines.join("\n");
|
|
52
|
+
}
|
|
53
|
+
async function abapMessageClass(input) {
|
|
54
|
+
const { message_class_name } = input;
|
|
55
|
+
const name = message_class_name.toUpperCase();
|
|
56
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
57
|
+
const response = await adt_client_js_1.http.get(`/messageclass/${name}`, {
|
|
58
|
+
headers: { Accept: "application/vnd.sap.adt.mc.messageclass+xml" },
|
|
59
|
+
responseType: "text",
|
|
60
|
+
validateStatus: (status) => status < 500,
|
|
61
|
+
});
|
|
62
|
+
if (response.status === 404) {
|
|
63
|
+
throw new Error(`Classe de mensagem ${name} não encontrada.`);
|
|
64
|
+
}
|
|
65
|
+
const messages = parseMessages(response.data);
|
|
66
|
+
return formatMessages(name, messages);
|
|
67
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapNavigate = abapNavigate;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapNavigate(input) {
|
|
6
|
+
const { uri } = input;
|
|
7
|
+
const csrf = await (0, adt_client_js_1.ensureSession)();
|
|
8
|
+
const response = await adt_client_js_1.http.post("/navigation/target", "", {
|
|
9
|
+
params: { uri },
|
|
10
|
+
headers: {
|
|
11
|
+
Accept: "application/xml",
|
|
12
|
+
"Content-Type": "text/plain",
|
|
13
|
+
"X-CSRF-Token": csrf,
|
|
14
|
+
},
|
|
15
|
+
responseType: "text",
|
|
16
|
+
validateStatus: (s) => s < 500,
|
|
17
|
+
});
|
|
18
|
+
if (response.status >= 400) {
|
|
19
|
+
const msg = typeof response.data === "string"
|
|
20
|
+
? (response.data.match(/<message[^>]*>([^<]+)<\/message>/i)?.[1] ?? `HTTP ${response.status}`)
|
|
21
|
+
: `HTTP ${response.status}`;
|
|
22
|
+
throw new Error(msg);
|
|
23
|
+
}
|
|
24
|
+
const xml = response.data;
|
|
25
|
+
// Parse navigation targets
|
|
26
|
+
const targets = [];
|
|
27
|
+
const targetRegex = /<(?:adtcore:)?objectReference[^>]*>/gi;
|
|
28
|
+
let match;
|
|
29
|
+
while ((match = targetRegex.exec(xml)) !== null) {
|
|
30
|
+
const element = match[0];
|
|
31
|
+
const nameMatch = element.match(/adtcore:name\s*=\s*"([^"]*)"/i);
|
|
32
|
+
const typeMatch = element.match(/adtcore:type\s*=\s*"([^"]*)"/i);
|
|
33
|
+
const uriMatch = element.match(/adtcore:uri\s*=\s*"([^"]*)"/i);
|
|
34
|
+
const lineMatch = element.match(/line\s*=\s*"([^"]*)"/i);
|
|
35
|
+
const colMatch = element.match(/column\s*=\s*"([^"]*)"/i);
|
|
36
|
+
if (nameMatch || uriMatch) {
|
|
37
|
+
targets.push({
|
|
38
|
+
name: nameMatch?.[1] ?? "",
|
|
39
|
+
type: typeMatch?.[1] ?? "",
|
|
40
|
+
uri: uriMatch?.[1] ?? "",
|
|
41
|
+
line: lineMatch?.[1] ?? "",
|
|
42
|
+
column: colMatch?.[1] ?? "",
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Fallback: parse generic target elements
|
|
47
|
+
if (targets.length === 0) {
|
|
48
|
+
const fallbackRegex = /<(?:navigation:)?target[^>]*>/gi;
|
|
49
|
+
while ((match = fallbackRegex.exec(xml)) !== null) {
|
|
50
|
+
const element = match[0];
|
|
51
|
+
const uriMatch = element.match(/uri\s*=\s*"([^"]*)"/i);
|
|
52
|
+
const nameMatch = element.match(/name\s*=\s*"([^"]*)"/i);
|
|
53
|
+
const typeMatch = element.match(/type\s*=\s*"([^"]*)"/i);
|
|
54
|
+
const lineMatch = element.match(/line\s*=\s*"([^"]*)"/i);
|
|
55
|
+
const colMatch = element.match(/column\s*=\s*"([^"]*)"/i);
|
|
56
|
+
if (uriMatch || nameMatch) {
|
|
57
|
+
targets.push({
|
|
58
|
+
name: nameMatch?.[1] ?? "",
|
|
59
|
+
type: typeMatch?.[1] ?? "",
|
|
60
|
+
uri: uriMatch?.[1] ?? "",
|
|
61
|
+
line: lineMatch?.[1] ?? "",
|
|
62
|
+
column: colMatch?.[1] ?? "",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (targets.length === 0) {
|
|
68
|
+
return "Nenhum alvo de navegação encontrado para o URI informado.";
|
|
69
|
+
}
|
|
70
|
+
const lines = [];
|
|
71
|
+
lines.push(`Navegação: ${targets.length} alvo(s) encontrado(s)`);
|
|
72
|
+
lines.push("─".repeat(60));
|
|
73
|
+
for (const t of targets) {
|
|
74
|
+
const pos = t.line ? ` (linha ${t.line}${t.column ? `:${t.column}` : ""})` : "";
|
|
75
|
+
lines.push(` ${t.name || t.uri} [${t.type}]${pos}`);
|
|
76
|
+
if (t.uri)
|
|
77
|
+
lines.push(` URI: ${t.uri}`);
|
|
78
|
+
}
|
|
79
|
+
return lines.join("\n");
|
|
80
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapNumberRange = abapNumberRange;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapNumberRange(input) {
|
|
6
|
+
const { object_name } = input;
|
|
7
|
+
const name = object_name.toUpperCase();
|
|
8
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
9
|
+
const response = await adt_client_js_1.http.get(`/numberranges/objects/${name.toLowerCase()}`, {
|
|
10
|
+
headers: { Accept: "text/html" },
|
|
11
|
+
responseType: "text",
|
|
12
|
+
validateStatus: (status) => status < 500,
|
|
13
|
+
});
|
|
14
|
+
if (response.status === 404) {
|
|
15
|
+
throw new Error(`Number range ${name} não encontrado.`);
|
|
16
|
+
}
|
|
17
|
+
const xml = response.data;
|
|
18
|
+
// Parse number range header
|
|
19
|
+
const descMatch = xml.match(/adtcore:description\s*=\s*"([^"]*)"/i);
|
|
20
|
+
const domainMatch = xml.match(/(?:nr:)?(?:domain|numberLength)\s*=\s*"([^"]*)"/i);
|
|
21
|
+
// Parse intervals
|
|
22
|
+
const intervals = [];
|
|
23
|
+
const intRegex = /<(?:nr:)?(?:interval|numberRangeInterval)[^>]*>/gi;
|
|
24
|
+
let match;
|
|
25
|
+
while ((match = intRegex.exec(xml)) !== null) {
|
|
26
|
+
const el = match[0];
|
|
27
|
+
const subMatch = el.match(/(?:subObject|number|nr)\s*=\s*"([^"]*)"/i);
|
|
28
|
+
const fromMatch = el.match(/(?:from|fromNumber|lowValue)\s*=\s*"([^"]*)"/i);
|
|
29
|
+
const toMatch = el.match(/(?:to|toNumber|highValue)\s*=\s*"([^"]*)"/i);
|
|
30
|
+
const curMatch = el.match(/(?:current|currentNumber)\s*=\s*"([^"]*)"/i);
|
|
31
|
+
const extMatch = el.match(/(?:external|isExternal)\s*=\s*"([^"]*)"/i);
|
|
32
|
+
intervals.push({
|
|
33
|
+
subObject: subMatch?.[1] ?? "",
|
|
34
|
+
from: fromMatch?.[1] ?? "",
|
|
35
|
+
to: toMatch?.[1] ?? "",
|
|
36
|
+
current: curMatch?.[1] ?? "",
|
|
37
|
+
external: extMatch?.[1] === "true" || extMatch?.[1] === "X",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const lines = [];
|
|
41
|
+
lines.push(`Number Range: ${name}`);
|
|
42
|
+
lines.push("═".repeat(50));
|
|
43
|
+
if (descMatch)
|
|
44
|
+
lines.push(`Descrição: ${descMatch[1]}`);
|
|
45
|
+
if (domainMatch)
|
|
46
|
+
lines.push(`Domínio/Comprimento: ${domainMatch[1]}`);
|
|
47
|
+
if (intervals.length > 0) {
|
|
48
|
+
lines.push(`\nIntervalos (${intervals.length}):`);
|
|
49
|
+
lines.push("─".repeat(50));
|
|
50
|
+
for (const i of intervals) {
|
|
51
|
+
const ext = i.external ? " [EXTERNO]" : " [INTERNO]";
|
|
52
|
+
const cur = i.current ? ` (atual: ${i.current})` : "";
|
|
53
|
+
lines.push(` ${i.subObject || "-"}: ${i.from} → ${i.to}${cur}${ext}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapObjectDocumentation = abapObjectDocumentation;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
async function abapObjectDocumentation(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 xml = await (0, adt_client_js_1.adtGetXmlWithParams)("/documentation/objectDocumentation", {
|
|
11
|
+
uri: objectUri,
|
|
12
|
+
});
|
|
13
|
+
// Extract documentation content
|
|
14
|
+
let docText = "";
|
|
15
|
+
// Try structured documentation
|
|
16
|
+
const longTextMatch = xml.match(/<(?:documentation:)?longText[^>]*>([\s\S]*?)<\/(?:documentation:)?longText>/i);
|
|
17
|
+
const shortTextMatch = xml.match(/<(?:documentation:)?shortText[^>]*>([\s\S]*?)<\/(?:documentation:)?shortText>/i);
|
|
18
|
+
const descMatch = xml.match(/<(?:documentation:)?description[^>]*>([\s\S]*?)<\/(?:documentation:)?description>/i);
|
|
19
|
+
if (longTextMatch) {
|
|
20
|
+
docText = longTextMatch[1].replace(/<[^>]+>/g, "").trim();
|
|
21
|
+
}
|
|
22
|
+
else if (shortTextMatch) {
|
|
23
|
+
docText = shortTextMatch[1].replace(/<[^>]+>/g, "").trim();
|
|
24
|
+
}
|
|
25
|
+
else if (descMatch) {
|
|
26
|
+
docText = descMatch[1].replace(/<[^>]+>/g, "").trim();
|
|
27
|
+
}
|
|
28
|
+
// Fallback: extract any readable text from HTML-like content
|
|
29
|
+
if (!docText) {
|
|
30
|
+
const htmlContent = xml.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
31
|
+
if (htmlContent.length > 10) {
|
|
32
|
+
docText = htmlContent.slice(0, 2000);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (!docText) {
|
|
36
|
+
return `Nenhuma documentação encontrada para ${name} (${object_type}).`;
|
|
37
|
+
}
|
|
38
|
+
const lines = [];
|
|
39
|
+
lines.push(`Documentação: ${name} (${object_type})`);
|
|
40
|
+
lines.push("═".repeat(60));
|
|
41
|
+
lines.push(docText);
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapObjectStructure = abapObjectStructure;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function parseObjectStructure(xml) {
|
|
6
|
+
const elements = [];
|
|
7
|
+
// Formato ADT: <objectStructureElement ... adtcore:name="X" adtcore:type="Y" ... visibility="Z" />
|
|
8
|
+
const regex = /<[^>]*?(?:objectStructureElement|include)[^>]*?adtcore:name\s*=\s*"([^"]*)"[^>]*?adtcore:type\s*=\s*"([^"]*)"[^>]*>/gi;
|
|
9
|
+
let match;
|
|
10
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
11
|
+
const element = match[0];
|
|
12
|
+
const name = match[1];
|
|
13
|
+
const type = match[2];
|
|
14
|
+
const visMatch = element.match(/visibility\s*=\s*"([^"]*)"/i);
|
|
15
|
+
const visibility = visMatch?.[1] ?? "";
|
|
16
|
+
elements.push({ name, type, visibility });
|
|
17
|
+
}
|
|
18
|
+
// Fallback: tentar extrair de elementos com atributos diferentes
|
|
19
|
+
if (elements.length === 0) {
|
|
20
|
+
const fallbackRegex = /<[^>]*?adtcore:name\s*=\s*"([^"]*)"[^>]*?adtcore:type\s*=\s*"([^"]*)"[^>]*>/gi;
|
|
21
|
+
while ((match = fallbackRegex.exec(xml)) !== null) {
|
|
22
|
+
const element = match[0];
|
|
23
|
+
const name = match[1];
|
|
24
|
+
const type = match[2];
|
|
25
|
+
// Ignorar o próprio objeto raiz
|
|
26
|
+
if (type === "CLAS/OC" || type === "INTF/OI")
|
|
27
|
+
continue;
|
|
28
|
+
const visMatch = element.match(/visibility\s*=\s*"([^"]*)"/i);
|
|
29
|
+
elements.push({
|
|
30
|
+
name,
|
|
31
|
+
type,
|
|
32
|
+
visibility: visMatch?.[1] ?? "",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return elements;
|
|
37
|
+
}
|
|
38
|
+
function formatStructure(objectName, elements) {
|
|
39
|
+
if (elements.length === 0) {
|
|
40
|
+
return `${objectName}: nenhum elemento encontrado na estrutura.`;
|
|
41
|
+
}
|
|
42
|
+
// Agrupar por tipo
|
|
43
|
+
const groups = {};
|
|
44
|
+
for (const el of elements) {
|
|
45
|
+
const key = el.type || "other";
|
|
46
|
+
if (!groups[key])
|
|
47
|
+
groups[key] = [];
|
|
48
|
+
groups[key].push(el);
|
|
49
|
+
}
|
|
50
|
+
const lines = [`Estrutura de ${objectName} (${elements.length} elementos)`, "─".repeat(60)];
|
|
51
|
+
for (const [type, items] of Object.entries(groups)) {
|
|
52
|
+
lines.push("");
|
|
53
|
+
lines.push(`▸ ${type} (${items.length})`);
|
|
54
|
+
for (const item of items) {
|
|
55
|
+
const vis = item.visibility ? `[${item.visibility}]` : "";
|
|
56
|
+
lines.push(` ${item.name.padEnd(40)} ${vis}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return lines.join("\n");
|
|
60
|
+
}
|
|
61
|
+
async function abapObjectStructure(input) {
|
|
62
|
+
const { object_type, object_name } = input;
|
|
63
|
+
const name = object_name.toUpperCase();
|
|
64
|
+
let basePath;
|
|
65
|
+
if (object_type === "CLAS/OC") {
|
|
66
|
+
basePath = "oo/classes";
|
|
67
|
+
}
|
|
68
|
+
else if (object_type === "INTF/OI") {
|
|
69
|
+
basePath = "oo/interfaces";
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new Error("abap_object_structure suporta apenas CLAS/OC e INTF/OI.");
|
|
73
|
+
}
|
|
74
|
+
const path = `/${basePath}/${name}/objectstructure`;
|
|
75
|
+
const xml = await (0, adt_client_js_1.adtGetXml)(path);
|
|
76
|
+
const elements = parseObjectStructure(xml);
|
|
77
|
+
return formatStructure(name, elements);
|
|
78
|
+
}
|