@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,294 @@
|
|
|
1
|
+
# UI5 XML Views — controls, layouts, formatters
|
|
2
|
+
|
|
3
|
+
## View Structure
|
|
4
|
+
|
|
5
|
+
```xml
|
|
6
|
+
<mvc:View
|
|
7
|
+
controllerName="com.myapp.controller.Main"
|
|
8
|
+
xmlns:mvc="sap.ui.core.mvc"
|
|
9
|
+
xmlns="sap.m"
|
|
10
|
+
xmlns:f="sap.f"
|
|
11
|
+
xmlns:l="sap.ui.layout"
|
|
12
|
+
xmlns:core="sap.ui.core">
|
|
13
|
+
|
|
14
|
+
<Page id="mainPage" title="{i18n>appTitle}">
|
|
15
|
+
<!-- content here -->
|
|
16
|
+
</Page>
|
|
17
|
+
|
|
18
|
+
</mvc:View>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Page Layouts
|
|
22
|
+
|
|
23
|
+
```xml
|
|
24
|
+
<!-- Basic Page -->
|
|
25
|
+
<Page id="page" title="Orders" showNavButton="true" navButtonPress=".onNavBack">
|
|
26
|
+
<headerContent>
|
|
27
|
+
<Button icon="sap-icon://add" press=".onCreate" type="Emphasized"/>
|
|
28
|
+
</headerContent>
|
|
29
|
+
<content>
|
|
30
|
+
<!-- main content -->
|
|
31
|
+
</content>
|
|
32
|
+
<footer>
|
|
33
|
+
<OverflowToolbar>
|
|
34
|
+
<ToolbarSpacer/>
|
|
35
|
+
<Button text="Save" type="Emphasized" press=".onSave"/>
|
|
36
|
+
<Button text="Cancel" press=".onCancel"/>
|
|
37
|
+
</OverflowToolbar>
|
|
38
|
+
</footer>
|
|
39
|
+
</Page>
|
|
40
|
+
|
|
41
|
+
<!-- Dynamic Page (sap.f) — modern, collapsible header -->
|
|
42
|
+
<f:DynamicPage id="dynamicPage" headerExpanded="true">
|
|
43
|
+
<f:title>
|
|
44
|
+
<f:DynamicPageTitle>
|
|
45
|
+
<f:heading>
|
|
46
|
+
<Title text="{Name}"/>
|
|
47
|
+
</f:heading>
|
|
48
|
+
<f:actions>
|
|
49
|
+
<Button text="Edit" type="Emphasized" press=".onEdit"/>
|
|
50
|
+
</f:actions>
|
|
51
|
+
</f:DynamicPageTitle>
|
|
52
|
+
</f:title>
|
|
53
|
+
<f:header>
|
|
54
|
+
<f:DynamicPageHeader>
|
|
55
|
+
<FlexBox wrap="Wrap">
|
|
56
|
+
<ObjectAttribute title="Status" text="{Status}"/>
|
|
57
|
+
<ObjectAttribute title="Created" text="{CreatedAt}"/>
|
|
58
|
+
</FlexBox>
|
|
59
|
+
</f:DynamicPageHeader>
|
|
60
|
+
</f:header>
|
|
61
|
+
<f:content>
|
|
62
|
+
<!-- main content -->
|
|
63
|
+
</f:content>
|
|
64
|
+
</f:DynamicPage>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Layout Controls
|
|
68
|
+
|
|
69
|
+
```xml
|
|
70
|
+
<!-- VBox / HBox -->
|
|
71
|
+
<VBox class="sapUiSmallMargin">
|
|
72
|
+
<HBox justifyContent="SpaceBetween" alignItems="Center">
|
|
73
|
+
<Label text="Name"/>
|
|
74
|
+
<Input value="{Name}" width="300px"/>
|
|
75
|
+
</HBox>
|
|
76
|
+
</VBox>
|
|
77
|
+
|
|
78
|
+
<!-- FlexBox -->
|
|
79
|
+
<FlexBox direction="Column" justifyContent="Center" alignItems="Stretch">
|
|
80
|
+
<items>
|
|
81
|
+
<Text text="{Description}"/>
|
|
82
|
+
</items>
|
|
83
|
+
</FlexBox>
|
|
84
|
+
|
|
85
|
+
<!-- Grid (sap.ui.layout) -->
|
|
86
|
+
<l:Grid defaultSpan="L6 M6 S12">
|
|
87
|
+
<l:content>
|
|
88
|
+
<Input value="{Field1}"/>
|
|
89
|
+
<Input value="{Field2}"/>
|
|
90
|
+
</l:content>
|
|
91
|
+
</l:Grid>
|
|
92
|
+
|
|
93
|
+
<!-- Form (sap.ui.layout.form) — responsive form layout -->
|
|
94
|
+
<f:SimpleForm xmlns:f="sap.ui.layout.form"
|
|
95
|
+
editable="true" layout="ResponsiveGridLayout"
|
|
96
|
+
labelSpanXL="3" labelSpanL="3" labelSpanM="3" labelSpanS="12"
|
|
97
|
+
columnsXL="2" columnsL="2" columnsM="1">
|
|
98
|
+
<f:content>
|
|
99
|
+
<core:Title text="General"/>
|
|
100
|
+
<Label text="Order ID"/>
|
|
101
|
+
<Input value="{OrderID}" editable="false"/>
|
|
102
|
+
<Label text="Customer"/>
|
|
103
|
+
<Input value="{CustomerName}"/>
|
|
104
|
+
<Label text="Date"/>
|
|
105
|
+
<DatePicker value="{path: 'OrderDate', type: 'sap.ui.model.type.Date', formatOptions: {style: 'medium'}}"/>
|
|
106
|
+
|
|
107
|
+
<core:Title text="Pricing"/>
|
|
108
|
+
<Label text="Amount"/>
|
|
109
|
+
<Input value="{Amount}" type="Number"/>
|
|
110
|
+
<Label text="Currency"/>
|
|
111
|
+
<Select selectedKey="{Currency}" items="{/Currencies}">
|
|
112
|
+
<core:Item key="{Key}" text="{Text}"/>
|
|
113
|
+
</Select>
|
|
114
|
+
</f:content>
|
|
115
|
+
</f:SimpleForm>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Input Controls
|
|
119
|
+
|
|
120
|
+
```xml
|
|
121
|
+
<!-- Text input -->
|
|
122
|
+
<Input value="{Name}" placeholder="Enter name" maxLength="40"
|
|
123
|
+
liveChange=".onNameChange" valueLiveUpdate="true"/>
|
|
124
|
+
|
|
125
|
+
<!-- Number -->
|
|
126
|
+
<Input value="{Amount}" type="Number" description="EUR"/>
|
|
127
|
+
|
|
128
|
+
<!-- TextArea -->
|
|
129
|
+
<TextArea value="{Description}" rows="4" growing="true" growingMaxLines="8"/>
|
|
130
|
+
|
|
131
|
+
<!-- DatePicker -->
|
|
132
|
+
<DatePicker value="{OrderDate}" valueFormat="yyyyMMdd" displayFormat="dd.MM.yyyy"/>
|
|
133
|
+
|
|
134
|
+
<!-- DateRangeSelection -->
|
|
135
|
+
<DateRangeSelection dateValue="{StartDate}" secondDateValue="{EndDate}"/>
|
|
136
|
+
|
|
137
|
+
<!-- TimePicker -->
|
|
138
|
+
<TimePicker value="{StartTime}" valueFormat="HHmmss" displayFormat="HH:mm"/>
|
|
139
|
+
|
|
140
|
+
<!-- Select (dropdown) -->
|
|
141
|
+
<Select selectedKey="{Status}" forceSelection="false">
|
|
142
|
+
<core:Item key="O" text="Open"/>
|
|
143
|
+
<core:Item key="A" text="Accepted"/>
|
|
144
|
+
<core:Item key="X" text="Rejected"/>
|
|
145
|
+
</Select>
|
|
146
|
+
|
|
147
|
+
<!-- ComboBox (dropdown with type-ahead) -->
|
|
148
|
+
<ComboBox selectedKey="{Category}" items="{/Categories}">
|
|
149
|
+
<core:Item key="{Key}" text="{Name}"/>
|
|
150
|
+
</ComboBox>
|
|
151
|
+
|
|
152
|
+
<!-- MultiComboBox -->
|
|
153
|
+
<MultiComboBox selectedKeys="{Tags}" items="{/AvailableTags}">
|
|
154
|
+
<core:Item key="{Key}" text="{Name}"/>
|
|
155
|
+
</MultiComboBox>
|
|
156
|
+
|
|
157
|
+
<!-- Switch -->
|
|
158
|
+
<Switch state="{IsActive}" customTextOn="Yes" customTextOff="No"/>
|
|
159
|
+
|
|
160
|
+
<!-- CheckBox -->
|
|
161
|
+
<CheckBox selected="{IsUrgent}" text="Urgent"/>
|
|
162
|
+
|
|
163
|
+
<!-- RadioButtonGroup -->
|
|
164
|
+
<RadioButtonGroup selectedIndex="{PriorityIndex}">
|
|
165
|
+
<RadioButton text="Low"/>
|
|
166
|
+
<RadioButton text="Medium"/>
|
|
167
|
+
<RadioButton text="High"/>
|
|
168
|
+
</RadioButtonGroup>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Display Controls
|
|
172
|
+
|
|
173
|
+
```xml
|
|
174
|
+
<!-- ObjectHeader -->
|
|
175
|
+
<ObjectHeader title="{Name}" number="{Amount}" numberUnit="{Currency}"
|
|
176
|
+
statuses="{statuses}">
|
|
177
|
+
<ObjectAttribute title="Customer" text="{CustomerName}"/>
|
|
178
|
+
<ObjectAttribute title="Date" text="{OrderDate}"/>
|
|
179
|
+
<ObjectStatus text="{StatusText}" state="{StatusState}"/>
|
|
180
|
+
</ObjectHeader>
|
|
181
|
+
|
|
182
|
+
<!-- ObjectListItem -->
|
|
183
|
+
<ObjectListItem title="{Name}" number="{Amount}" numberUnit="{Currency}"
|
|
184
|
+
type="Navigation" press=".onItemPress">
|
|
185
|
+
<firstStatus>
|
|
186
|
+
<ObjectStatus text="{StatusText}" state="{StatusState}"/>
|
|
187
|
+
</firstStatus>
|
|
188
|
+
<attributes>
|
|
189
|
+
<ObjectAttribute text="{CustomerName}"/>
|
|
190
|
+
</attributes>
|
|
191
|
+
</ObjectListItem>
|
|
192
|
+
|
|
193
|
+
<!-- IconTabBar -->
|
|
194
|
+
<IconTabBar select=".onTabSelect">
|
|
195
|
+
<items>
|
|
196
|
+
<IconTabFilter text="All" count="{/AllCount}" key="all"/>
|
|
197
|
+
<IconTabFilter text="Open" count="{/OpenCount}" key="open"
|
|
198
|
+
iconColor="Positive"/>
|
|
199
|
+
<IconTabFilter text="Rejected" count="{/RejectedCount}" key="rejected"
|
|
200
|
+
iconColor="Negative"/>
|
|
201
|
+
</items>
|
|
202
|
+
</IconTabBar>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Table
|
|
206
|
+
|
|
207
|
+
```xml
|
|
208
|
+
<Table id="orderTable" items="{/Orders}" growing="true" growingThreshold="20"
|
|
209
|
+
mode="SingleSelectMaster" selectionChange=".onSelectionChange"
|
|
210
|
+
sticky="ColumnHeaders,HeaderToolbar">
|
|
211
|
+
<headerToolbar>
|
|
212
|
+
<OverflowToolbar>
|
|
213
|
+
<Title text="Orders ({= ${/Orders}.length})"/>
|
|
214
|
+
<ToolbarSpacer/>
|
|
215
|
+
<SearchField search=".onSearch" width="300px"/>
|
|
216
|
+
<Button icon="sap-icon://add" press=".onCreate" type="Emphasized"/>
|
|
217
|
+
</OverflowToolbar>
|
|
218
|
+
</headerToolbar>
|
|
219
|
+
<columns>
|
|
220
|
+
<Column><Text text="Order ID"/></Column>
|
|
221
|
+
<Column><Text text="Customer"/></Column>
|
|
222
|
+
<Column hAlign="End"><Text text="Amount"/></Column>
|
|
223
|
+
<Column><Text text="Status"/></Column>
|
|
224
|
+
</columns>
|
|
225
|
+
<items>
|
|
226
|
+
<ColumnListItem type="Navigation" press=".onItemPress">
|
|
227
|
+
<Text text="{OrderID}"/>
|
|
228
|
+
<Text text="{CustomerName}"/>
|
|
229
|
+
<ObjectNumber number="{Amount}" unit="{Currency}"/>
|
|
230
|
+
<ObjectStatus text="{StatusText}" state="{StatusState}"/>
|
|
231
|
+
</ColumnListItem>
|
|
232
|
+
</items>
|
|
233
|
+
</Table>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## List
|
|
237
|
+
|
|
238
|
+
```xml
|
|
239
|
+
<List id="list" items="{/Items}" mode="Delete" delete=".onDelete"
|
|
240
|
+
noDataText="No items found">
|
|
241
|
+
<StandardListItem title="{Name}" description="{Description}"
|
|
242
|
+
icon="sap-icon://product" info="{Status}" infoState="{InfoState}"
|
|
243
|
+
type="Navigation" press=".onItemPress"/>
|
|
244
|
+
</List>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Inline Formatting
|
|
248
|
+
|
|
249
|
+
```xml
|
|
250
|
+
<!-- Expression binding -->
|
|
251
|
+
<Text text="{= ${Amount} > 1000 ? 'High' : 'Normal'}"/>
|
|
252
|
+
<Input editable="{= ${Status} !== 'A'}"/>
|
|
253
|
+
<ObjectStatus text="{StatusText}"
|
|
254
|
+
state="{= ${Status} === 'A' ? 'Success' : ${Status} === 'X' ? 'Error' : 'Warning'}"/>
|
|
255
|
+
|
|
256
|
+
<!-- Visibility -->
|
|
257
|
+
<Button text="Approve" visible="{= ${Status} === 'O'}"/>
|
|
258
|
+
|
|
259
|
+
<!-- CSS class binding -->
|
|
260
|
+
<Text text="{Amount}" class="{= ${Amount} < 0 ? 'negativeAmount' : ''}"/>
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Common Namespaces
|
|
264
|
+
|
|
265
|
+
| Prefix | Namespace | Controls |
|
|
266
|
+
|--------|-----------|----------|
|
|
267
|
+
| (default) | `sap.m` | Page, Table, List, Input, Button, Dialog |
|
|
268
|
+
| `f` | `sap.f` | DynamicPage, FlexibleColumnLayout, Avatar |
|
|
269
|
+
| `l` | `sap.ui.layout` | Grid, Splitter, HorizontalLayout |
|
|
270
|
+
| `core` | `sap.ui.core` | Item, Title, Icon, Fragment |
|
|
271
|
+
| `mvc` | `sap.ui.core.mvc` | View |
|
|
272
|
+
| `form` | `sap.ui.layout.form` | SimpleForm, Form |
|
|
273
|
+
| `unified` | `sap.ui.unified` | FileUploader, Calendar |
|
|
274
|
+
| `table` | `sap.ui.table` | Table (desktop grid), TreeTable |
|
|
275
|
+
|
|
276
|
+
## Rules
|
|
277
|
+
- Always set `id` on controls you access from controller (`this.byId("id")`)
|
|
278
|
+
- Use `sap.m.Table` (responsive) for mobile-first; `sap.ui.table.Table` for desktop-heavy data grids
|
|
279
|
+
- Use `growing="true"` on Table/List for lazy loading (not load all data)
|
|
280
|
+
- Use `SimpleForm` with `ResponsiveGridLayout` for responsive form layout
|
|
281
|
+
- `DynamicPage` (sap.f) is preferred over `Page` (sap.m) for modern apps
|
|
282
|
+
- Expression binding `{= ... }` for simple conditions; formatter function for complex logic
|
|
283
|
+
- Prefix namespace only needed once per view (`xmlns:f="sap.f"`)
|
|
284
|
+
|
|
285
|
+
## Anti-Patterns
|
|
286
|
+
| Anti-Pattern | Correct |
|
|
287
|
+
|---|---|
|
|
288
|
+
| JS creating controls programmatically | XML views for declarative UI |
|
|
289
|
+
| Controls without `id` that are accessed from controller | Always set meaningful `id` |
|
|
290
|
+
| `sap.ui.table.Table` for mobile app | Use `sap.m.Table` (responsive) |
|
|
291
|
+
| Hardcoded text in XML view | Use i18n: `text="{i18n>key}"` |
|
|
292
|
+
| Complex logic in expression binding | Use formatter function in controller |
|
|
293
|
+
| Loading all data without `growing` | `growing="true" growingThreshold="20"` |
|
|
294
|
+
| Nested VBox/HBox 5+ levels deep | Use Grid or SimpleForm for complex layouts |
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logger — registro local de chamadas ADT.
|
|
4
|
+
*
|
|
5
|
+
* Grava cada chamada com: método, path, timestamp, duração, status HTTP.
|
|
6
|
+
* Permite resumo de uso e export para análise.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.logCall = logCall;
|
|
10
|
+
exports.startTimer = startTimer;
|
|
11
|
+
exports.getSessionSummary = getSessionSummary;
|
|
12
|
+
exports.readTodayLog = readTodayLog;
|
|
13
|
+
exports.formatSummary = formatSummary;
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
// ─── Config ───────────────────────────────────────────────────────
|
|
17
|
+
const LOG_DIR = process.env.ABAP_AI_LOG_DIR || (0, path_1.join)(process.env.HOME || "~", ".abap-ai", "logs");
|
|
18
|
+
const LOG_ENABLED = process.env.ABAP_AI_LOG !== "false";
|
|
19
|
+
// ─── Estado ───────────────────────────────────────────────────────
|
|
20
|
+
const sessionEntries = [];
|
|
21
|
+
const sessionStart = new Date();
|
|
22
|
+
// ─── Funções ──────────────────────────────────────────────────────
|
|
23
|
+
function ensureLogDir() {
|
|
24
|
+
if (!(0, fs_1.existsSync)(LOG_DIR)) {
|
|
25
|
+
(0, fs_1.mkdirSync)(LOG_DIR, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function logFileName() {
|
|
29
|
+
const date = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
|
30
|
+
return (0, path_1.join)(LOG_DIR, `adt-${date}.jsonl`);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Registra uma chamada ADT no log.
|
|
34
|
+
*/
|
|
35
|
+
function logCall(entry) {
|
|
36
|
+
sessionEntries.push(entry);
|
|
37
|
+
if (!LOG_ENABLED)
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
ensureLogDir();
|
|
41
|
+
(0, fs_1.appendFileSync)(logFileName(), JSON.stringify(entry) + "\n", "utf-8");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Log silencioso — não deve quebrar operações ADT
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Cria um timer para medir duração de chamadas.
|
|
49
|
+
*/
|
|
50
|
+
function startTimer() {
|
|
51
|
+
const start = Date.now();
|
|
52
|
+
return () => Date.now() - start;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Retorna resumo de uso da sessão atual.
|
|
56
|
+
*/
|
|
57
|
+
function getSessionSummary() {
|
|
58
|
+
const total = sessionEntries.length;
|
|
59
|
+
const successful = sessionEntries.filter((e) => e.status >= 200 && e.status < 400).length;
|
|
60
|
+
const failed = total - successful;
|
|
61
|
+
const totalDuration = sessionEntries.reduce((sum, e) => sum + e.duration_ms, 0);
|
|
62
|
+
const byMethod = {};
|
|
63
|
+
const byStatus = {};
|
|
64
|
+
for (const entry of sessionEntries) {
|
|
65
|
+
byMethod[entry.method] = (byMethod[entry.method] || 0) + 1;
|
|
66
|
+
const statusGroup = `${Math.floor(entry.status / 100)}xx`;
|
|
67
|
+
byStatus[statusGroup] = (byStatus[statusGroup] || 0) + 1;
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
total_calls: total,
|
|
71
|
+
successful,
|
|
72
|
+
failed,
|
|
73
|
+
avg_duration_ms: total > 0 ? Math.round(totalDuration / total) : 0,
|
|
74
|
+
by_method: byMethod,
|
|
75
|
+
by_status: byStatus,
|
|
76
|
+
session_start: sessionStart.toISOString(),
|
|
77
|
+
session_duration_s: Math.round((Date.now() - sessionStart.getTime()) / 1000),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Lê o log do dia e retorna as entradas.
|
|
82
|
+
*/
|
|
83
|
+
function readTodayLog() {
|
|
84
|
+
try {
|
|
85
|
+
const file = logFileName();
|
|
86
|
+
if (!(0, fs_1.existsSync)(file))
|
|
87
|
+
return [];
|
|
88
|
+
const content = (0, fs_1.readFileSync)(file, "utf-8");
|
|
89
|
+
return content
|
|
90
|
+
.split("\n")
|
|
91
|
+
.filter((line) => line.trim())
|
|
92
|
+
.map((line) => JSON.parse(line));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Formata o resumo para exibição humana.
|
|
100
|
+
*/
|
|
101
|
+
function formatSummary(summary) {
|
|
102
|
+
const lines = [
|
|
103
|
+
`📊 Resumo da sessão`,
|
|
104
|
+
` Início: ${summary.session_start}`,
|
|
105
|
+
` Duração: ${summary.session_duration_s}s`,
|
|
106
|
+
` Total de chamadas: ${summary.total_calls}`,
|
|
107
|
+
` Sucesso: ${summary.successful} | Falha: ${summary.failed}`,
|
|
108
|
+
` Duração média: ${summary.avg_duration_ms}ms`,
|
|
109
|
+
];
|
|
110
|
+
if (Object.keys(summary.by_method).length > 0) {
|
|
111
|
+
lines.push(` Por método: ${Object.entries(summary.by_method).map(([m, c]) => `${m}=${c}`).join(", ")}`);
|
|
112
|
+
}
|
|
113
|
+
return lines.join("\n");
|
|
114
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* System Profile — configuração contextual do sistema SAP alvo.
|
|
4
|
+
*
|
|
5
|
+
* Permite ao MCP adaptar comportamento (URLs, CDS syntax, ETag handling, etc.)
|
|
6
|
+
* com base no tipo e versão do sistema conectado.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.getProfile = getProfile;
|
|
10
|
+
exports.setProfile = setProfile;
|
|
11
|
+
exports.getPreset = getPreset;
|
|
12
|
+
exports.listPresets = listPresets;
|
|
13
|
+
exports.resolveODataV4Url = resolveODataV4Url;
|
|
14
|
+
exports.resolveBspUrl = resolveBspUrl;
|
|
15
|
+
// ─── Presets ──────────────────────────────────────────────────────
|
|
16
|
+
const ON_PREMISE_S4_DEFAULTS = {
|
|
17
|
+
system: {
|
|
18
|
+
type: "ON_PREMISE_S4",
|
|
19
|
+
release: "2023",
|
|
20
|
+
abapPlatform: "STANDARD",
|
|
21
|
+
sapBasisVersion: "758",
|
|
22
|
+
systemId: "",
|
|
23
|
+
},
|
|
24
|
+
capabilities: {
|
|
25
|
+
cdsViewEntity: true,
|
|
26
|
+
cdsProjection: true,
|
|
27
|
+
rapManaged: true,
|
|
28
|
+
rapDraftEnabled: true,
|
|
29
|
+
releasedApiOnly: false,
|
|
30
|
+
codeCompletion: true,
|
|
31
|
+
callHierarchy: true,
|
|
32
|
+
quickFix: true,
|
|
33
|
+
extractMethod: true,
|
|
34
|
+
codeCoverage: true,
|
|
35
|
+
sqlConsole: true,
|
|
36
|
+
releasedApiCatalog: true,
|
|
37
|
+
abapGitAvailable: false,
|
|
38
|
+
},
|
|
39
|
+
gateway: {
|
|
40
|
+
topology: "EMBEDDED",
|
|
41
|
+
odataV4UrlPattern: "/sap/opu/odata4/sap/{binding}/srvd/sap/{binding}/{version}/",
|
|
42
|
+
publishEndpoint: "/sap/bc/adt/businessservices/odatav4/publishjobs",
|
|
43
|
+
autoRegistration: false,
|
|
44
|
+
},
|
|
45
|
+
client: {
|
|
46
|
+
role: "DEVELOPMENT",
|
|
47
|
+
allowLocalPublish: true,
|
|
48
|
+
},
|
|
49
|
+
ui5: {
|
|
50
|
+
bootstrapPath: "/sap/public/bc/ui5_ui5/resources/",
|
|
51
|
+
bspUrlPattern: "/sap/bc/ui5_ui5/sap/{bsp}?sap-client={client}",
|
|
52
|
+
},
|
|
53
|
+
quirks: {
|
|
54
|
+
etagRequiresManualFix: true,
|
|
55
|
+
conversionExitFields: ["USTYP", "BSTYP", "PRCTR", "KOSTL"],
|
|
56
|
+
lockReturns406ForDDLS: true,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const BTP_ABAP_ENV_DEFAULTS = {
|
|
60
|
+
system: {
|
|
61
|
+
type: "BTP_ABAP_ENV",
|
|
62
|
+
release: "2023",
|
|
63
|
+
abapPlatform: "CLOUD",
|
|
64
|
+
sapBasisVersion: "758",
|
|
65
|
+
systemId: "",
|
|
66
|
+
},
|
|
67
|
+
capabilities: {
|
|
68
|
+
cdsViewEntity: true,
|
|
69
|
+
cdsProjection: true,
|
|
70
|
+
rapManaged: true,
|
|
71
|
+
rapDraftEnabled: true,
|
|
72
|
+
releasedApiOnly: true,
|
|
73
|
+
codeCompletion: true,
|
|
74
|
+
callHierarchy: true,
|
|
75
|
+
quickFix: true,
|
|
76
|
+
extractMethod: true,
|
|
77
|
+
codeCoverage: true,
|
|
78
|
+
sqlConsole: true,
|
|
79
|
+
releasedApiCatalog: true,
|
|
80
|
+
abapGitAvailable: false,
|
|
81
|
+
},
|
|
82
|
+
gateway: {
|
|
83
|
+
topology: "EMBEDDED",
|
|
84
|
+
odataV4UrlPattern: "/sap/opu/odata4/sap/{binding}/{version}/",
|
|
85
|
+
publishEndpoint: "",
|
|
86
|
+
autoRegistration: true,
|
|
87
|
+
},
|
|
88
|
+
client: {
|
|
89
|
+
role: "DEVELOPMENT",
|
|
90
|
+
allowLocalPublish: true,
|
|
91
|
+
},
|
|
92
|
+
ui5: {
|
|
93
|
+
bootstrapPath: "/resources/",
|
|
94
|
+
bspUrlPattern: "",
|
|
95
|
+
},
|
|
96
|
+
quirks: {
|
|
97
|
+
etagRequiresManualFix: false,
|
|
98
|
+
conversionExitFields: [],
|
|
99
|
+
lockReturns406ForDDLS: false,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
const ON_PREMISE_ECC_DEFAULTS = {
|
|
103
|
+
system: {
|
|
104
|
+
type: "ON_PREMISE_ECC",
|
|
105
|
+
release: "750",
|
|
106
|
+
abapPlatform: "STANDARD",
|
|
107
|
+
sapBasisVersion: "750",
|
|
108
|
+
systemId: "",
|
|
109
|
+
},
|
|
110
|
+
capabilities: {
|
|
111
|
+
cdsViewEntity: false, // ECC não tem view entity
|
|
112
|
+
cdsProjection: false,
|
|
113
|
+
rapManaged: false,
|
|
114
|
+
rapDraftEnabled: false,
|
|
115
|
+
releasedApiOnly: false,
|
|
116
|
+
codeCompletion: true, // disponível desde BASIS 750
|
|
117
|
+
callHierarchy: false,
|
|
118
|
+
quickFix: false,
|
|
119
|
+
extractMethod: false,
|
|
120
|
+
codeCoverage: false,
|
|
121
|
+
sqlConsole: false,
|
|
122
|
+
releasedApiCatalog: false,
|
|
123
|
+
abapGitAvailable: false,
|
|
124
|
+
},
|
|
125
|
+
gateway: {
|
|
126
|
+
topology: "HUB",
|
|
127
|
+
odataV4UrlPattern: "", // ECC geralmente não tem OData V4
|
|
128
|
+
publishEndpoint: "",
|
|
129
|
+
autoRegistration: false,
|
|
130
|
+
},
|
|
131
|
+
client: {
|
|
132
|
+
role: "DEVELOPMENT",
|
|
133
|
+
allowLocalPublish: false,
|
|
134
|
+
},
|
|
135
|
+
ui5: {
|
|
136
|
+
bootstrapPath: "/sap/public/bc/ui5_ui5/resources/",
|
|
137
|
+
bspUrlPattern: "/sap/bc/ui5_ui5/sap/{bsp}?sap-client={client}",
|
|
138
|
+
},
|
|
139
|
+
quirks: {
|
|
140
|
+
etagRequiresManualFix: false,
|
|
141
|
+
conversionExitFields: [],
|
|
142
|
+
lockReturns406ForDDLS: false,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
const PRESETS = {
|
|
146
|
+
ON_PREMISE_S4: ON_PREMISE_S4_DEFAULTS,
|
|
147
|
+
BTP_ABAP_ENV: BTP_ABAP_ENV_DEFAULTS,
|
|
148
|
+
ON_PREMISE_ECC: ON_PREMISE_ECC_DEFAULTS,
|
|
149
|
+
};
|
|
150
|
+
// ─── Profile singleton ───────────────────────────────────────────
|
|
151
|
+
let currentProfile = null;
|
|
152
|
+
/**
|
|
153
|
+
* Retorna o profile atual. Se não inicializado, usa o preset
|
|
154
|
+
* definido em SAP_SYSTEM_TYPE ou detecta automaticamente.
|
|
155
|
+
*/
|
|
156
|
+
function getProfile() {
|
|
157
|
+
if (!currentProfile) {
|
|
158
|
+
const preset = process.env.SAP_SYSTEM_TYPE || "ON_PREMISE_S4";
|
|
159
|
+
currentProfile = deepClone(PRESETS[preset] || ON_PREMISE_S4_DEFAULTS);
|
|
160
|
+
}
|
|
161
|
+
return currentProfile;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Define o profile (usado pelo abap_system_info após auto-detecção).
|
|
165
|
+
*/
|
|
166
|
+
function setProfile(profile) {
|
|
167
|
+
currentProfile = profile;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Retorna um preset base pelo nome.
|
|
171
|
+
*/
|
|
172
|
+
function getPreset(name) {
|
|
173
|
+
const p = PRESETS[name];
|
|
174
|
+
return p ? deepClone(p) : undefined;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Lista os presets disponíveis.
|
|
178
|
+
*/
|
|
179
|
+
function listPresets() {
|
|
180
|
+
return Object.keys(PRESETS);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Gera a URL real do serviço OData V4 com base no pattern do profile.
|
|
184
|
+
*/
|
|
185
|
+
function resolveODataV4Url(bindingName, version = "0000") {
|
|
186
|
+
const profile = getProfile();
|
|
187
|
+
const pattern = profile.gateway.odataV4UrlPattern;
|
|
188
|
+
if (!pattern)
|
|
189
|
+
return "";
|
|
190
|
+
return pattern
|
|
191
|
+
.replace(/\{binding\}/g, bindingName.toLowerCase())
|
|
192
|
+
.replace(/\{version\}/g, version);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Gera a URL da app BSP com base no pattern do profile.
|
|
196
|
+
*/
|
|
197
|
+
function resolveBspUrl(bspName) {
|
|
198
|
+
const profile = getProfile();
|
|
199
|
+
const client = process.env.SAP_CLIENT ?? "100";
|
|
200
|
+
return profile.ui5.bspUrlPattern
|
|
201
|
+
.replace("{bsp}", bspName.toLowerCase())
|
|
202
|
+
.replace("{client}", client);
|
|
203
|
+
}
|
|
204
|
+
// ─── Helpers ─────────────────────────────────────────────────────
|
|
205
|
+
function deepClone(obj) {
|
|
206
|
+
return JSON.parse(JSON.stringify(obj));
|
|
207
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.abapDoc = abapDoc;
|
|
4
|
+
const adt_client_js_1 = require("../adt-client.js");
|
|
5
|
+
function resolveSourceUri(objectType, objectName) {
|
|
6
|
+
const adtPath = (0, adt_client_js_1.resolveAdtPath)(objectType);
|
|
7
|
+
return `/${adtPath}/${objectName.toLowerCase()}/source/main`;
|
|
8
|
+
}
|
|
9
|
+
function extractAbapDoc(source, methodName) {
|
|
10
|
+
const lines = source.split("\n");
|
|
11
|
+
const docs = [];
|
|
12
|
+
let currentComments = [];
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
const trimmed = line.trim();
|
|
15
|
+
// ABAP Doc comment: starts with "!
|
|
16
|
+
if (trimmed.startsWith('"!') || trimmed.startsWith('"! ')) {
|
|
17
|
+
currentComments.push(trimmed.replace(/^"!\s?/, ""));
|
|
18
|
+
}
|
|
19
|
+
else if (currentComments.length > 0) {
|
|
20
|
+
// Próxima linha após bloco de comentários — é o target
|
|
21
|
+
// Extrair nome do método/atributo/parâmetro
|
|
22
|
+
const nameMatch = trimmed.match(/(?:methods|data|class-data|constants|types|class-methods)\s+(\w+)/i)
|
|
23
|
+
|| trimmed.match(/(?:interface|class)\s+(\w+)/i);
|
|
24
|
+
const target = nameMatch?.[1] ?? trimmed.substring(0, 60);
|
|
25
|
+
docs.push({ target, comments: [...currentComments] });
|
|
26
|
+
currentComments = [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Filtrar por método se especificado
|
|
30
|
+
if (methodName) {
|
|
31
|
+
const filtered = docs.filter((d) => d.target.toUpperCase() === methodName.toUpperCase());
|
|
32
|
+
if (filtered.length === 0) {
|
|
33
|
+
return `Nenhuma documentação ABAP Doc encontrada para ${methodName}.`;
|
|
34
|
+
}
|
|
35
|
+
return filtered.map((d) => d.comments.join("\n")).join("\n\n");
|
|
36
|
+
}
|
|
37
|
+
if (docs.length === 0) {
|
|
38
|
+
return "Nenhuma documentação ABAP Doc encontrada neste objeto.";
|
|
39
|
+
}
|
|
40
|
+
const result = [];
|
|
41
|
+
for (const d of docs) {
|
|
42
|
+
result.push(`▸ ${d.target}`);
|
|
43
|
+
for (const c of d.comments) {
|
|
44
|
+
result.push(` ${c}`);
|
|
45
|
+
}
|
|
46
|
+
result.push("");
|
|
47
|
+
}
|
|
48
|
+
return result.join("\n");
|
|
49
|
+
}
|
|
50
|
+
async function abapDoc(input) {
|
|
51
|
+
const { object_type, object_name, method_name } = input;
|
|
52
|
+
const name = object_name.toUpperCase();
|
|
53
|
+
await (0, adt_client_js_1.ensureSession)();
|
|
54
|
+
const uri = resolveSourceUri(object_type, name);
|
|
55
|
+
const response = await adt_client_js_1.http.get(uri, {
|
|
56
|
+
headers: { Accept: "text/plain" },
|
|
57
|
+
responseType: "text",
|
|
58
|
+
validateStatus: (status) => status < 500,
|
|
59
|
+
});
|
|
60
|
+
if (response.status === 404) {
|
|
61
|
+
throw new Error(`Objeto ${name} (${object_type}) não encontrado.`);
|
|
62
|
+
}
|
|
63
|
+
const source = response.data;
|
|
64
|
+
const target = method_name ? `${name}->${method_name}` : name;
|
|
65
|
+
const doc = extractAbapDoc(source, method_name);
|
|
66
|
+
const lines = [
|
|
67
|
+
`ABAP Doc: ${target}`,
|
|
68
|
+
"─".repeat(50),
|
|
69
|
+
doc,
|
|
70
|
+
];
|
|
71
|
+
return lines.join("\n");
|
|
72
|
+
}
|