arc-1 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/bin/arc1.js +12 -0
- package/dist/adt/btp.d.ts +122 -0
- package/dist/adt/btp.d.ts.map +1 -0
- package/dist/adt/btp.js +392 -0
- package/dist/adt/btp.js.map +1 -0
- package/dist/adt/client.d.ts +89 -0
- package/dist/adt/client.d.ts.map +1 -0
- package/dist/adt/client.js +208 -0
- package/dist/adt/client.js.map +1 -0
- package/dist/adt/codeintel.d.ts +38 -0
- package/dist/adt/codeintel.d.ts.map +1 -0
- package/dist/adt/codeintel.js +61 -0
- package/dist/adt/codeintel.js.map +1 -0
- package/dist/adt/config.d.ts +65 -0
- package/dist/adt/config.d.ts.map +1 -0
- package/dist/adt/config.js +35 -0
- package/dist/adt/config.js.map +1 -0
- package/dist/adt/cookies.d.ts +27 -0
- package/dist/adt/cookies.d.ts.map +1 -0
- package/dist/adt/cookies.js +67 -0
- package/dist/adt/cookies.js.map +1 -0
- package/dist/adt/crud.d.ts +35 -0
- package/dist/adt/crud.d.ts.map +1 -0
- package/dist/adt/crud.js +87 -0
- package/dist/adt/crud.js.map +1 -0
- package/dist/adt/devtools.d.ts +32 -0
- package/dist/adt/devtools.d.ts.map +1 -0
- package/dist/adt/devtools.js +154 -0
- package/dist/adt/devtools.js.map +1 -0
- package/dist/adt/errors.d.ts +49 -0
- package/dist/adt/errors.d.ts.map +1 -0
- package/dist/adt/errors.js +80 -0
- package/dist/adt/errors.js.map +1 -0
- package/dist/adt/features.d.ts +44 -0
- package/dist/adt/features.d.ts.map +1 -0
- package/dist/adt/features.js +173 -0
- package/dist/adt/features.js.map +1 -0
- package/dist/adt/http.d.ts +116 -0
- package/dist/adt/http.d.ts.map +1 -0
- package/dist/adt/http.js +374 -0
- package/dist/adt/http.js.map +1 -0
- package/dist/adt/safety.d.ts +70 -0
- package/dist/adt/safety.d.ts.map +1 -0
- package/dist/adt/safety.js +222 -0
- package/dist/adt/safety.js.map +1 -0
- package/dist/adt/transport.d.ts +18 -0
- package/dist/adt/transport.d.ts.map +1 -0
- package/dist/adt/transport.js +66 -0
- package/dist/adt/transport.js.map +1 -0
- package/dist/adt/types.d.ts +91 -0
- package/dist/adt/types.d.ts.map +1 -0
- package/dist/adt/types.js +9 -0
- package/dist/adt/types.js.map +1 -0
- package/dist/adt/xml-parser.d.ts +109 -0
- package/dist/adt/xml-parser.d.ts.map +1 -0
- package/dist/adt/xml-parser.js +283 -0
- package/dist/adt/xml-parser.js.map +1 -0
- package/dist/cache/cache.d.ts +61 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +14 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/memory.d.ts +25 -0
- package/dist/cache/memory.d.ts.map +1 -0
- package/dist/cache/memory.js +69 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/sqlite.d.ts +26 -0
- package/dist/cache/sqlite.d.ts.map +1 -0
- package/dist/cache/sqlite.js +130 -0
- package/dist/cache/sqlite.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +101 -0
- package/dist/cli.js.map +1 -0
- package/dist/context/compressor.d.ts +33 -0
- package/dist/context/compressor.d.ts.map +1 -0
- package/dist/context/compressor.js +208 -0
- package/dist/context/compressor.js.map +1 -0
- package/dist/context/contract.d.ts +14 -0
- package/dist/context/contract.d.ts.map +1 -0
- package/dist/context/contract.js +202 -0
- package/dist/context/contract.js.map +1 -0
- package/dist/context/deps.d.ts +32 -0
- package/dist/context/deps.d.ts.map +1 -0
- package/dist/context/deps.js +240 -0
- package/dist/context/deps.js.map +1 -0
- package/dist/context/types.d.ts +56 -0
- package/dist/context/types.d.ts.map +1 -0
- package/dist/context/types.js +10 -0
- package/dist/context/types.js.map +1 -0
- package/dist/handlers/intent.d.ts +46 -0
- package/dist/handlers/intent.d.ts.map +1 -0
- package/dist/handlers/intent.js +539 -0
- package/dist/handlers/intent.js.map +1 -0
- package/dist/handlers/tools.d.ts +21 -0
- package/dist/handlers/tools.d.ts.map +1 -0
- package/dist/handlers/tools.js +260 -0
- package/dist/handlers/tools.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/lint/lint.d.ts +35 -0
- package/dist/lint/lint.d.ts.map +1 -0
- package/dist/lint/lint.js +67 -0
- package/dist/lint/lint.js.map +1 -0
- package/dist/server/audit.d.ts +96 -0
- package/dist/server/audit.d.ts.map +1 -0
- package/dist/server/audit.js +27 -0
- package/dist/server/audit.js.map +1 -0
- package/dist/server/config.d.ts +19 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +101 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/context.d.ts +20 -0
- package/dist/server/context.d.ts.map +1 -0
- package/dist/server/context.js +20 -0
- package/dist/server/context.js.map +1 -0
- package/dist/server/elicit.d.ts +43 -0
- package/dist/server/elicit.d.ts.map +1 -0
- package/dist/server/elicit.js +183 -0
- package/dist/server/elicit.js.map +1 -0
- package/dist/server/http.d.ts +34 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +328 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/logger.d.ts +57 -0
- package/dist/server/logger.d.ts.map +1 -0
- package/dist/server/logger.js +129 -0
- package/dist/server/logger.js.map +1 -0
- package/dist/server/server.d.ts +25 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +307 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/sinks/btp-auditlog.d.ts +48 -0
- package/dist/server/sinks/btp-auditlog.d.ts.map +1 -0
- package/dist/server/sinks/btp-auditlog.js +232 -0
- package/dist/server/sinks/btp-auditlog.js.map +1 -0
- package/dist/server/sinks/file.d.ts +22 -0
- package/dist/server/sinks/file.d.ts.map +1 -0
- package/dist/server/sinks/file.js +59 -0
- package/dist/server/sinks/file.js.map +1 -0
- package/dist/server/sinks/stderr.d.ts +19 -0
- package/dist/server/sinks/stderr.d.ts.map +1 -0
- package/dist/server/sinks/stderr.js +63 -0
- package/dist/server/sinks/stderr.js.map +1 -0
- package/dist/server/sinks/types.d.ts +14 -0
- package/dist/server/sinks/types.d.ts.map +1 -0
- package/dist/server/sinks/types.js +8 -0
- package/dist/server/sinks/types.js.map +1 -0
- package/dist/server/types.d.ts +54 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +42 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/xsuaa.d.ts +77 -0
- package/dist/server/xsuaa.d.ts.map +1 -0
- package/dist/server/xsuaa.js +364 -0
- package/dist/server/xsuaa.js.map +1 -0
- package/package.json +66 -0
package/dist/adt/crud.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRUD operations for SAP ADT objects.
|
|
3
|
+
*
|
|
4
|
+
* All write operations follow the pattern: lock → modify → unlock
|
|
5
|
+
* The lock/unlock must happen on the same stateful HTTP session.
|
|
6
|
+
* We use AdtHttpClient.withStatefulSession() to guarantee this.
|
|
7
|
+
*
|
|
8
|
+
* Critical: unlock MUST happen even if modify fails (try-finally pattern).
|
|
9
|
+
* This was a hard-won lesson in the fr0ster codebase — earlier versions
|
|
10
|
+
* leaked locks on error, blocking the object for other developers.
|
|
11
|
+
*/
|
|
12
|
+
import { checkOperation, checkTransportableEdit, OperationType } from './safety.js';
|
|
13
|
+
/** Lock an ABAP object for editing */
|
|
14
|
+
export async function lockObject(http, safety, objectUrl, accessMode = 'MODIFY') {
|
|
15
|
+
if (accessMode === 'MODIFY') {
|
|
16
|
+
checkOperation(safety, OperationType.Lock, 'LockObject');
|
|
17
|
+
}
|
|
18
|
+
const resp = await http.post(`${objectUrl}?_action=LOCK&accessMode=${accessMode}`, undefined, undefined, {
|
|
19
|
+
Accept: 'application/vnd.sap.as+xml;charset=UTF-8;dataname=com.sap.adt.lock.result',
|
|
20
|
+
});
|
|
21
|
+
// Parse lock response (asx:abap format) — simple regex extraction
|
|
22
|
+
const lockHandle = extractXmlValue(resp.body, 'LOCK_HANDLE');
|
|
23
|
+
const corrNr = extractXmlValue(resp.body, 'CORRNR');
|
|
24
|
+
const isLocal = extractXmlValue(resp.body, 'IS_LOCAL') === 'X';
|
|
25
|
+
return { lockHandle, corrNr, isLocal };
|
|
26
|
+
}
|
|
27
|
+
/** Unlock an ABAP object */
|
|
28
|
+
export async function unlockObject(http, objectUrl, lockHandle) {
|
|
29
|
+
await http.post(`${objectUrl}?_action=UNLOCK&lockHandle=${encodeURIComponent(lockHandle)}`);
|
|
30
|
+
}
|
|
31
|
+
/** Create a new ABAP object */
|
|
32
|
+
export async function createObject(http, safety, objectUrl, body, contentType = 'application/xml', transport) {
|
|
33
|
+
checkOperation(safety, OperationType.Create, 'CreateObject');
|
|
34
|
+
if (transport) {
|
|
35
|
+
checkTransportableEdit(safety, transport, 'CreateObject');
|
|
36
|
+
}
|
|
37
|
+
const url = transport ? `${objectUrl}?corrNr=${encodeURIComponent(transport)}` : objectUrl;
|
|
38
|
+
const resp = await http.post(url, body, contentType);
|
|
39
|
+
return resp.body;
|
|
40
|
+
}
|
|
41
|
+
/** Update source code of an ABAP object (requires lock) */
|
|
42
|
+
export async function updateSource(http, safety, sourceUrl, source, lockHandle, transport) {
|
|
43
|
+
checkOperation(safety, OperationType.Update, 'UpdateSource');
|
|
44
|
+
if (transport) {
|
|
45
|
+
checkTransportableEdit(safety, transport, 'UpdateSource');
|
|
46
|
+
}
|
|
47
|
+
let url = sourceUrl;
|
|
48
|
+
const params = [`lockHandle=${encodeURIComponent(lockHandle)}`];
|
|
49
|
+
if (transport) {
|
|
50
|
+
params.push(`corrNr=${encodeURIComponent(transport)}`);
|
|
51
|
+
}
|
|
52
|
+
if (params.length > 0) {
|
|
53
|
+
url += (url.includes('?') ? '&' : '?') + params.join('&');
|
|
54
|
+
}
|
|
55
|
+
await http.put(url, source, 'text/plain');
|
|
56
|
+
}
|
|
57
|
+
/** Delete an ABAP object (requires lock) */
|
|
58
|
+
export async function deleteObject(http, safety, objectUrl, lockHandle, transport) {
|
|
59
|
+
checkOperation(safety, OperationType.Delete, 'DeleteObject');
|
|
60
|
+
let url = `${objectUrl}?lockHandle=${encodeURIComponent(lockHandle)}`;
|
|
61
|
+
if (transport) {
|
|
62
|
+
url += `&corrNr=${encodeURIComponent(transport)}`;
|
|
63
|
+
}
|
|
64
|
+
await http.delete(url);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* High-level: update source with guaranteed unlock.
|
|
68
|
+
* lock → updateSource → unlock (in try-finally)
|
|
69
|
+
*/
|
|
70
|
+
export async function safeUpdateSource(http, safety, objectUrl, sourceUrl, source, transport) {
|
|
71
|
+
await http.withStatefulSession(async (session) => {
|
|
72
|
+
const lock = await lockObject(session, safety, objectUrl);
|
|
73
|
+
try {
|
|
74
|
+
await updateSource(session, safety, sourceUrl, source, lock.lockHandle, transport);
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
await unlockObject(session, objectUrl, lock.lockHandle);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/** Simple XML value extractor (for lock responses) */
|
|
82
|
+
function extractXmlValue(xml, tag) {
|
|
83
|
+
const regex = new RegExp(`<${tag}>([^<]*)</${tag}>`);
|
|
84
|
+
const match = xml.match(regex);
|
|
85
|
+
return match?.[1] ?? '';
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=crud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crud.js","sourceRoot":"","sources":["../../ts-src/adt/crud.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development tools for SAP ADT.
|
|
3
|
+
*
|
|
4
|
+
* - SyntaxCheck: compile-time validation
|
|
5
|
+
* - Activate: publish objects to the main repository
|
|
6
|
+
* - RunUnitTests: execute ABAP unit tests
|
|
7
|
+
* - RunATCCheck: ABAP Test Cockpit (code quality)
|
|
8
|
+
*/
|
|
9
|
+
import type { AdtHttpClient } from './http.js';
|
|
10
|
+
import { type SafetyConfig } from './safety.js';
|
|
11
|
+
import type { SyntaxCheckResult, UnitTestResult } from './types.js';
|
|
12
|
+
/** Run syntax check on an ABAP object */
|
|
13
|
+
export declare function syntaxCheck(http: AdtHttpClient, safety: SafetyConfig, objectUrl: string): Promise<SyntaxCheckResult>;
|
|
14
|
+
/** Activate (publish) ABAP objects */
|
|
15
|
+
export declare function activate(http: AdtHttpClient, safety: SafetyConfig, objectUrl: string): Promise<{
|
|
16
|
+
success: boolean;
|
|
17
|
+
messages: string[];
|
|
18
|
+
}>;
|
|
19
|
+
/** Run ABAP unit tests for an object */
|
|
20
|
+
export declare function runUnitTests(http: AdtHttpClient, safety: SafetyConfig, objectUrl: string): Promise<UnitTestResult[]>;
|
|
21
|
+
/** Run ATC check on an object */
|
|
22
|
+
export declare function runAtcCheck(http: AdtHttpClient, safety: SafetyConfig, objectUrl: string, variant?: string): Promise<{
|
|
23
|
+
findings: AtcFinding[];
|
|
24
|
+
}>;
|
|
25
|
+
export interface AtcFinding {
|
|
26
|
+
priority: number;
|
|
27
|
+
checkTitle: string;
|
|
28
|
+
messageTitle: string;
|
|
29
|
+
uri: string;
|
|
30
|
+
line: number;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=devtools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../../ts-src/adt/devtools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAiC,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,iBAAiB,EAAiB,cAAc,EAAE,MAAM,YAAY,CAAC;AAEnF,yCAAyC;AACzC,wBAAsB,WAAW,CAC/B,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAa5B;AAED,sCAAsC;AACtC,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAuBnD;AAED,wCAAwC;AACxC,wBAAsB,YAAY,CAChC,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,EAAE,CAAC,CAiC3B;AAED,iCAAiC;AACjC,wBAAsB,WAAW,CAC/B,IAAI,EAAE,aAAa,EACnB,MAAM,EAAE,YAAY,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAE,CAAC,CA2BrC;AAID,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Development tools for SAP ADT.
|
|
3
|
+
*
|
|
4
|
+
* - SyntaxCheck: compile-time validation
|
|
5
|
+
* - Activate: publish objects to the main repository
|
|
6
|
+
* - RunUnitTests: execute ABAP unit tests
|
|
7
|
+
* - RunATCCheck: ABAP Test Cockpit (code quality)
|
|
8
|
+
*/
|
|
9
|
+
import { checkOperation, OperationType } from './safety.js';
|
|
10
|
+
/** Run syntax check on an ABAP object */
|
|
11
|
+
export async function syntaxCheck(http, safety, objectUrl) {
|
|
12
|
+
checkOperation(safety, OperationType.Read, 'SyntaxCheck');
|
|
13
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
14
|
+
<chkrun:checkObjectList xmlns:chkrun="http://www.sap.com/adt/checkrun" xmlns:adtcore="http://www.sap.com/adt/core">
|
|
15
|
+
<chkrun:checkObject adtcore:uri="${objectUrl}" chkrun:version="active"/>
|
|
16
|
+
</chkrun:checkObjectList>`;
|
|
17
|
+
const resp = await http.post('/sap/bc/adt/checkruns', body, 'application/vnd.sap.adt.checkobjects+xml', {
|
|
18
|
+
Accept: 'application/vnd.sap.adt.checkmessages+xml',
|
|
19
|
+
});
|
|
20
|
+
return parseSyntaxCheckResult(resp.body);
|
|
21
|
+
}
|
|
22
|
+
/** Activate (publish) ABAP objects */
|
|
23
|
+
export async function activate(http, safety, objectUrl) {
|
|
24
|
+
checkOperation(safety, OperationType.Activate, 'Activate');
|
|
25
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
26
|
+
<adtcore:objectReferences xmlns:adtcore="http://www.sap.com/adt/core">
|
|
27
|
+
<adtcore:objectReference adtcore:uri="${objectUrl}"/>
|
|
28
|
+
</adtcore:objectReferences>`;
|
|
29
|
+
const resp = await http.post('/sap/bc/adt/activation', body, 'application/xml', {
|
|
30
|
+
Accept: 'application/xml',
|
|
31
|
+
});
|
|
32
|
+
// Check if activation succeeded (no error messages)
|
|
33
|
+
const hasErrors = resp.body.includes('severity="error"') || resp.body.includes('type="E"');
|
|
34
|
+
const messages = [];
|
|
35
|
+
// Extract message texts
|
|
36
|
+
const msgRegex = /shortText="([^"]+)"/g;
|
|
37
|
+
let match;
|
|
38
|
+
while ((match = msgRegex.exec(resp.body)) !== null) {
|
|
39
|
+
messages.push(match[1]);
|
|
40
|
+
}
|
|
41
|
+
return { success: !hasErrors, messages };
|
|
42
|
+
}
|
|
43
|
+
/** Run ABAP unit tests for an object */
|
|
44
|
+
export async function runUnitTests(http, safety, objectUrl) {
|
|
45
|
+
checkOperation(safety, OperationType.Test, 'RunUnitTests');
|
|
46
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
47
|
+
<aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
|
|
48
|
+
<external>
|
|
49
|
+
<coverage active="false"/>
|
|
50
|
+
</external>
|
|
51
|
+
<options>
|
|
52
|
+
<uriType value="semantic"/>
|
|
53
|
+
<testDeterminationStrategy sameProgram="true" assignedTests="false" publicApi="false"/>
|
|
54
|
+
<testRiskLevels harmless="true" dangerous="true" critical="true"/>
|
|
55
|
+
<testDurations short="true" medium="true" long="true"/>
|
|
56
|
+
</options>
|
|
57
|
+
<adtcore:objectSets xmlns:adtcore="http://www.sap.com/adt/core">
|
|
58
|
+
<objectSet kind="inclusive">
|
|
59
|
+
<adtcore:objectReferences>
|
|
60
|
+
<adtcore:objectReference adtcore:uri="${objectUrl}"/>
|
|
61
|
+
</adtcore:objectReferences>
|
|
62
|
+
</objectSet>
|
|
63
|
+
</adtcore:objectSets>
|
|
64
|
+
</aunit:runConfiguration>`;
|
|
65
|
+
const resp = await http.post('/sap/bc/adt/abapunit/testruns', body, 'application/vnd.sap.adt.abapunit.testruns.config.v4+xml', {
|
|
66
|
+
Accept: 'application/xml',
|
|
67
|
+
});
|
|
68
|
+
return parseUnitTestResults(resp.body);
|
|
69
|
+
}
|
|
70
|
+
/** Run ATC check on an object */
|
|
71
|
+
export async function runAtcCheck(http, safety, objectUrl, variant) {
|
|
72
|
+
checkOperation(safety, OperationType.Read, 'RunATCCheck');
|
|
73
|
+
// Create ATC run
|
|
74
|
+
const createBody = `<?xml version="1.0" encoding="UTF-8"?>
|
|
75
|
+
<atc:run xmlns:atc="http://www.sap.com/adt/atc"${variant ? ` maximumVerdicts="100"` : ''}>
|
|
76
|
+
<objectSets xmlns:adtcore="http://www.sap.com/adt/core">
|
|
77
|
+
<objectSet kind="inclusive">
|
|
78
|
+
<adtcore:objectReferences>
|
|
79
|
+
<adtcore:objectReference adtcore:uri="${objectUrl}"/>
|
|
80
|
+
</adtcore:objectReferences>
|
|
81
|
+
</objectSet>
|
|
82
|
+
</objectSets>
|
|
83
|
+
</atc:run>`;
|
|
84
|
+
const createResp = await http.post('/sap/bc/adt/atc/runs?worklistId=1', createBody, 'application/xml', {
|
|
85
|
+
Accept: 'application/xml',
|
|
86
|
+
});
|
|
87
|
+
// Parse worklist ID from response and fetch results
|
|
88
|
+
const worklistId = extractAttr(createResp.body, 'id') || '1';
|
|
89
|
+
const resultResp = await http.get(`/sap/bc/adt/atc/worklists/${worklistId}`, {
|
|
90
|
+
Accept: 'application/atc.worklist.v1+xml',
|
|
91
|
+
});
|
|
92
|
+
return { findings: parseAtcFindings(resultResp.body) };
|
|
93
|
+
}
|
|
94
|
+
function parseSyntaxCheckResult(xml) {
|
|
95
|
+
const messages = [];
|
|
96
|
+
// Parse check messages from XML
|
|
97
|
+
const msgRegex = /<msg[^>]*type="([^"]*)"[^>]*line="(\d+)"[^>]*col="(\d+)"[^>]*>/g;
|
|
98
|
+
const textRegex = /shortText="([^"]*)"/;
|
|
99
|
+
let match;
|
|
100
|
+
while ((match = msgRegex.exec(xml)) !== null) {
|
|
101
|
+
const fullTag = xml.slice(match.index, xml.indexOf('>', match.index + match[0].length) + 1);
|
|
102
|
+
const textMatch = textRegex.exec(fullTag);
|
|
103
|
+
messages.push({
|
|
104
|
+
severity: match[1] === 'E' ? 'error' : match[1] === 'W' ? 'warning' : 'info',
|
|
105
|
+
text: textMatch?.[1] ?? '',
|
|
106
|
+
line: Number.parseInt(match[2], 10),
|
|
107
|
+
column: Number.parseInt(match[3], 10),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
hasErrors: messages.some((m) => m.severity === 'error'),
|
|
112
|
+
messages,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function parseUnitTestResults(xml) {
|
|
116
|
+
const results = [];
|
|
117
|
+
// Extract test results from ABAP Unit XML response
|
|
118
|
+
const testMethodRegex = /<testMethod[^>]*name="([^"]*)"[^>]*>/g;
|
|
119
|
+
let match;
|
|
120
|
+
while ((match = testMethodRegex.exec(xml)) !== null) {
|
|
121
|
+
const methodName = match[1];
|
|
122
|
+
// Check for alerts after this method
|
|
123
|
+
const afterMatch = xml.slice(match.index);
|
|
124
|
+
const hasAlert = afterMatch.includes('<alert') && afterMatch.indexOf('<alert') < afterMatch.indexOf('</testMethod');
|
|
125
|
+
results.push({
|
|
126
|
+
program: '',
|
|
127
|
+
testClass: '',
|
|
128
|
+
testMethod: methodName,
|
|
129
|
+
status: hasAlert ? 'failed' : 'passed',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
function parseAtcFindings(xml) {
|
|
135
|
+
const findings = [];
|
|
136
|
+
const findingRegex = /<finding[^>]*priority="(\d)"[^>]*checkTitle="([^"]*)"[^>]*messageTitle="([^"]*)"[^>]*/g;
|
|
137
|
+
let match;
|
|
138
|
+
while ((match = findingRegex.exec(xml)) !== null) {
|
|
139
|
+
findings.push({
|
|
140
|
+
priority: Number.parseInt(match[1], 10),
|
|
141
|
+
checkTitle: match[2],
|
|
142
|
+
messageTitle: match[3],
|
|
143
|
+
uri: '',
|
|
144
|
+
line: 0,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return findings;
|
|
148
|
+
}
|
|
149
|
+
function extractAttr(xml, attr) {
|
|
150
|
+
const regex = new RegExp(`${attr}="([^"]*)"`);
|
|
151
|
+
const match = xml.match(regex);
|
|
152
|
+
return match?.[1] ?? '';
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=devtools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"devtools.js","sourceRoot":"","sources":["../../ts-src/adt/devtools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error types for ADT API interactions.
|
|
3
|
+
*
|
|
4
|
+
* SAP ADT returns errors in multiple formats:
|
|
5
|
+
* - HTTP status codes (401, 403, 404, 500)
|
|
6
|
+
* - XML exception bodies (with structured error messages)
|
|
7
|
+
* - HTML error pages (generic SAP web dispatcher errors)
|
|
8
|
+
* - Plain text (rare, usually session-related)
|
|
9
|
+
*
|
|
10
|
+
* We normalize all of these into typed error classes so handlers
|
|
11
|
+
* can make decisions without parsing strings.
|
|
12
|
+
*
|
|
13
|
+
* Learned from fr0ster: their extractAdtErrorMessage() parses the XML
|
|
14
|
+
* exception body to get the actual SAP error message. We do the same
|
|
15
|
+
* in AdtApiError.fromResponse().
|
|
16
|
+
*/
|
|
17
|
+
/** Base error for all ADT-related errors */
|
|
18
|
+
export declare class AdtError extends Error {
|
|
19
|
+
constructor(message: string);
|
|
20
|
+
}
|
|
21
|
+
/** HTTP-level API error from SAP ADT */
|
|
22
|
+
export declare class AdtApiError extends AdtError {
|
|
23
|
+
readonly statusCode: number;
|
|
24
|
+
readonly path: string;
|
|
25
|
+
readonly responseBody?: string | undefined;
|
|
26
|
+
constructor(message: string, statusCode: number, path: string, responseBody?: string | undefined);
|
|
27
|
+
get isNotFound(): boolean;
|
|
28
|
+
get isUnauthorized(): boolean;
|
|
29
|
+
get isForbidden(): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* SAP returns 400 with specific messages when the HTTP session expires.
|
|
32
|
+
* This is different from 401 (auth failure) — it means the stateful
|
|
33
|
+
* session cookie is no longer valid.
|
|
34
|
+
*/
|
|
35
|
+
get isSessionExpired(): boolean;
|
|
36
|
+
}
|
|
37
|
+
/** Network-level error (DNS, connection refused, timeout) */
|
|
38
|
+
export declare class AdtNetworkError extends AdtError {
|
|
39
|
+
readonly cause?: Error | undefined;
|
|
40
|
+
constructor(message: string, cause?: Error | undefined);
|
|
41
|
+
}
|
|
42
|
+
/** Safety system blocked the operation */
|
|
43
|
+
export declare class AdtSafetyError extends AdtError {
|
|
44
|
+
constructor(message: string);
|
|
45
|
+
}
|
|
46
|
+
/** Check if an error is a specific ADT error type */
|
|
47
|
+
export declare function isNotFoundError(err: unknown): boolean;
|
|
48
|
+
export declare function isSessionExpiredError(err: unknown): boolean;
|
|
49
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../ts-src/adt/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,4CAA4C;AAC5C,qBAAa,QAAS,SAAQ,KAAK;gBACrB,OAAO,EAAE,MAAM;CAI5B;AAED,wCAAwC;AACxC,qBAAa,WAAY,SAAQ,QAAQ;aAGrB,UAAU,EAAE,MAAM;aAClB,IAAI,EAAE,MAAM;aACZ,YAAY,CAAC,EAAE,MAAM;gBAHrC,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,MAAM,YAAA;IAMvC,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACH,IAAI,gBAAgB,IAAI,OAAO,CAM9B;CACF;AAED,6DAA6D;AAC7D,qBAAa,eAAgB,SAAQ,QAAQ;aAGzB,KAAK,CAAC,EAAE,KAAK;gBAD7B,OAAO,EAAE,MAAM,EACC,KAAK,CAAC,EAAE,KAAK,YAAA;CAKhC;AAED,0CAA0C;AAC1C,qBAAa,cAAe,SAAQ,QAAQ;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,qDAAqD;AACrD,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAErD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE3D"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error types for ADT API interactions.
|
|
3
|
+
*
|
|
4
|
+
* SAP ADT returns errors in multiple formats:
|
|
5
|
+
* - HTTP status codes (401, 403, 404, 500)
|
|
6
|
+
* - XML exception bodies (with structured error messages)
|
|
7
|
+
* - HTML error pages (generic SAP web dispatcher errors)
|
|
8
|
+
* - Plain text (rare, usually session-related)
|
|
9
|
+
*
|
|
10
|
+
* We normalize all of these into typed error classes so handlers
|
|
11
|
+
* can make decisions without parsing strings.
|
|
12
|
+
*
|
|
13
|
+
* Learned from fr0ster: their extractAdtErrorMessage() parses the XML
|
|
14
|
+
* exception body to get the actual SAP error message. We do the same
|
|
15
|
+
* in AdtApiError.fromResponse().
|
|
16
|
+
*/
|
|
17
|
+
/** Base error for all ADT-related errors */
|
|
18
|
+
export class AdtError extends Error {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'AdtError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** HTTP-level API error from SAP ADT */
|
|
25
|
+
export class AdtApiError extends AdtError {
|
|
26
|
+
statusCode;
|
|
27
|
+
path;
|
|
28
|
+
responseBody;
|
|
29
|
+
constructor(message, statusCode, path, responseBody) {
|
|
30
|
+
super(`ADT API error: status ${statusCode} at ${path}: ${message}`);
|
|
31
|
+
this.statusCode = statusCode;
|
|
32
|
+
this.path = path;
|
|
33
|
+
this.responseBody = responseBody;
|
|
34
|
+
this.name = 'AdtApiError';
|
|
35
|
+
}
|
|
36
|
+
get isNotFound() {
|
|
37
|
+
return this.statusCode === 404;
|
|
38
|
+
}
|
|
39
|
+
get isUnauthorized() {
|
|
40
|
+
return this.statusCode === 401;
|
|
41
|
+
}
|
|
42
|
+
get isForbidden() {
|
|
43
|
+
return this.statusCode === 403;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* SAP returns 400 with specific messages when the HTTP session expires.
|
|
47
|
+
* This is different from 401 (auth failure) — it means the stateful
|
|
48
|
+
* session cookie is no longer valid.
|
|
49
|
+
*/
|
|
50
|
+
get isSessionExpired() {
|
|
51
|
+
if (this.statusCode !== 400)
|
|
52
|
+
return false;
|
|
53
|
+
const msg = (this.responseBody ?? '').toLowerCase();
|
|
54
|
+
return (msg.includes('icmenosession') || msg.includes('session timed out') || msg.includes('session no longer exists'));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Network-level error (DNS, connection refused, timeout) */
|
|
58
|
+
export class AdtNetworkError extends AdtError {
|
|
59
|
+
cause;
|
|
60
|
+
constructor(message, cause) {
|
|
61
|
+
super(`ADT network error: ${message}`);
|
|
62
|
+
this.cause = cause;
|
|
63
|
+
this.name = 'AdtNetworkError';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Safety system blocked the operation */
|
|
67
|
+
export class AdtSafetyError extends AdtError {
|
|
68
|
+
constructor(message) {
|
|
69
|
+
super(message);
|
|
70
|
+
this.name = 'AdtSafetyError';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Check if an error is a specific ADT error type */
|
|
74
|
+
export function isNotFoundError(err) {
|
|
75
|
+
return err instanceof AdtApiError && err.isNotFound;
|
|
76
|
+
}
|
|
77
|
+
export function isSessionExpiredError(err) {
|
|
78
|
+
return err instanceof AdtApiError && err.isSessionExpired;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../ts-src/adt/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,4CAA4C;AAC5C,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AAED,wCAAwC;AACxC,MAAM,OAAO,WAAY,SAAQ,QAAQ;IAGrB;IACA;IACA;IAJlB,YACE,OAAe,EACC,UAAkB,EAClB,IAAY,EACZ,YAAqB;QAErC,KAAK,CAAC,yBAAyB,UAAU,OAAO,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QAJpD,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAQ;QACZ,iBAAY,GAAZ,YAAY,CAAS;QAGrC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;IAED,IAAI,UAAU;QACZ,OAAO,IAAI,CAAC,UAAU,KAAK,GAAG,CAAC;IACjC,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,UAAU,KAAK,GAAG,CAAC;IACjC,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,UAAU,KAAK,GAAG,CAAC;IACjC,CAAC;IAED;;;;OAIG;IACH,IAAI,gBAAgB;QAClB,IAAI,IAAI,CAAC,UAAU,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;QAC1C,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACpD,OAAO,CACL,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAC/G,CAAC;IACJ,CAAC;CACF;AAED,6DAA6D;AAC7D,MAAM,OAAO,eAAgB,SAAQ,QAAQ;IAGzB;IAFlB,YACE,OAAe,EACC,KAAa;QAE7B,KAAK,CAAC,sBAAsB,OAAO,EAAE,CAAC,CAAC;QAFvB,UAAK,GAAL,KAAK,CAAQ;QAG7B,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AAED,0CAA0C;AAC1C,MAAM,OAAO,cAAe,SAAQ,QAAQ;IAC1C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,qDAAqD;AACrD,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,OAAO,GAAG,YAAY,WAAW,IAAI,GAAG,CAAC,UAAU,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAY;IAChD,OAAO,GAAG,YAAY,WAAW,IAAI,GAAG,CAAC,gBAAgB,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature detection for ARC-1.
|
|
3
|
+
*
|
|
4
|
+
* Probes SAP system capabilities to determine which optional features
|
|
5
|
+
* are available (abapGit, RAP, AMDP, UI5, Transport, HANA).
|
|
6
|
+
*
|
|
7
|
+
* Each feature can be:
|
|
8
|
+
* - "auto": probe SAP system at startup, enable if available
|
|
9
|
+
* - "on": force enabled (skip probe, fail if feature is used but unavailable)
|
|
10
|
+
* - "off": force disabled (skip probe, hide related tools)
|
|
11
|
+
*
|
|
12
|
+
* The "safety network" concept: if a feature is "auto" and the probe
|
|
13
|
+
* returns 404 (endpoint doesn't exist), the feature is gracefully
|
|
14
|
+
* disabled. This prevents errors when connecting to older SAP systems.
|
|
15
|
+
*
|
|
16
|
+
* Probe endpoints are lightweight HEAD requests — they don't fetch data,
|
|
17
|
+
* just check if the endpoint exists (returns 200 or 404).
|
|
18
|
+
*/
|
|
19
|
+
import { Version } from '@abaplint/core';
|
|
20
|
+
import type { FeatureConfig } from './config.js';
|
|
21
|
+
import type { AdtHttpClient } from './http.js';
|
|
22
|
+
import type { ResolvedFeatures } from './types.js';
|
|
23
|
+
/**
|
|
24
|
+
* Probe all features and return resolved status.
|
|
25
|
+
*
|
|
26
|
+
* Runs all probes in parallel for speed.
|
|
27
|
+
* Each probe is a HEAD request — if it returns 2xx, the feature exists.
|
|
28
|
+
* 404 or network error means the feature is not available.
|
|
29
|
+
*/
|
|
30
|
+
export declare function probeFeatures(client: AdtHttpClient, config: FeatureConfig): Promise<ResolvedFeatures>;
|
|
31
|
+
/**
|
|
32
|
+
* Map SAP_BASIS release string to the closest @abaplint/core Version.
|
|
33
|
+
*
|
|
34
|
+
* abaplint versions are additive — each version accepts all syntax from
|
|
35
|
+
* previous versions plus new features. We map to the closest matching
|
|
36
|
+
* version, falling back to Cloud (the superset) for unknown releases.
|
|
37
|
+
*
|
|
38
|
+
* SAP_BASIS release examples: "700", "702", "740", "750", "757", "758"
|
|
39
|
+
* BTP ABAP Environment reports release like "sap_btp" or similar.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mapSapReleaseToAbaplintVersion(release: string): Version;
|
|
42
|
+
/** Get features without probing (for offline/test scenarios) */
|
|
43
|
+
export declare function resolveWithoutProbing(config: FeatureConfig): ResolvedFeatures;
|
|
44
|
+
//# sourceMappingURL=features.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../ts-src/adt/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,KAAK,EAAE,aAAa,EAAe,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAqClE;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+C3G;AAED;;;;;;;;;GASG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAqBvE;AAkBD,gEAAgE;AAChE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CAqB7E"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature detection for ARC-1.
|
|
3
|
+
*
|
|
4
|
+
* Probes SAP system capabilities to determine which optional features
|
|
5
|
+
* are available (abapGit, RAP, AMDP, UI5, Transport, HANA).
|
|
6
|
+
*
|
|
7
|
+
* Each feature can be:
|
|
8
|
+
* - "auto": probe SAP system at startup, enable if available
|
|
9
|
+
* - "on": force enabled (skip probe, fail if feature is used but unavailable)
|
|
10
|
+
* - "off": force disabled (skip probe, hide related tools)
|
|
11
|
+
*
|
|
12
|
+
* The "safety network" concept: if a feature is "auto" and the probe
|
|
13
|
+
* returns 404 (endpoint doesn't exist), the feature is gracefully
|
|
14
|
+
* disabled. This prevents errors when connecting to older SAP systems.
|
|
15
|
+
*
|
|
16
|
+
* Probe endpoints are lightweight HEAD requests — they don't fetch data,
|
|
17
|
+
* just check if the endpoint exists (returns 200 or 404).
|
|
18
|
+
*/
|
|
19
|
+
import { Version } from '@abaplint/core';
|
|
20
|
+
import { parseInstalledComponents } from './xml-parser.js';
|
|
21
|
+
const PROBES = [
|
|
22
|
+
{ id: 'hana', endpoint: '/sap/bc/adt/ddic/sysinfo/hanainfo', description: 'HANA database' },
|
|
23
|
+
{ id: 'abapGit', endpoint: '/sap/bc/adt/abapgit/repos', description: 'abapGit integration' },
|
|
24
|
+
{ id: 'rap', endpoint: '/sap/bc/adt/ddic/ddl/sources', description: 'RAP/CDS development' },
|
|
25
|
+
{ id: 'amdp', endpoint: '/sap/bc/adt/debugger/amdp', description: 'AMDP debugging' },
|
|
26
|
+
{ id: 'ui5', endpoint: '/sap/bc/adt/filestore/ui5-bsp', description: 'UI5/Fiori BSP' },
|
|
27
|
+
{ id: 'transport', endpoint: '/sap/bc/adt/cts/transportrequests', description: 'CTS transport management' },
|
|
28
|
+
];
|
|
29
|
+
/** Resolve a single feature based on its mode */
|
|
30
|
+
function resolveFeature(mode, probeResult, id, description) {
|
|
31
|
+
if (mode === 'on') {
|
|
32
|
+
return { id, available: true, mode: 'on', message: 'Forced on by configuration' };
|
|
33
|
+
}
|
|
34
|
+
if (mode === 'off') {
|
|
35
|
+
return { id, available: false, mode: 'off', message: 'Disabled by configuration' };
|
|
36
|
+
}
|
|
37
|
+
// auto
|
|
38
|
+
return {
|
|
39
|
+
id,
|
|
40
|
+
available: probeResult,
|
|
41
|
+
mode: 'auto',
|
|
42
|
+
message: probeResult ? `${description} is available` : `${description} is not available`,
|
|
43
|
+
probedAt: new Date().toISOString(),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Probe all features and return resolved status.
|
|
48
|
+
*
|
|
49
|
+
* Runs all probes in parallel for speed.
|
|
50
|
+
* Each probe is a HEAD request — if it returns 2xx, the feature exists.
|
|
51
|
+
* 404 or network error means the feature is not available.
|
|
52
|
+
*/
|
|
53
|
+
export async function probeFeatures(client, config) {
|
|
54
|
+
const modeMap = {
|
|
55
|
+
hana: config.hana,
|
|
56
|
+
abapGit: config.abapGit,
|
|
57
|
+
rap: config.rap,
|
|
58
|
+
amdp: config.amdp,
|
|
59
|
+
ui5: config.ui5,
|
|
60
|
+
transport: config.transport,
|
|
61
|
+
};
|
|
62
|
+
// Only probe features that are in "auto" mode
|
|
63
|
+
const probesToRun = PROBES.filter((p) => modeMap[p.id] === 'auto');
|
|
64
|
+
// Run feature probes + release detection in parallel
|
|
65
|
+
const [probeResults, abapRelease] = await Promise.all([
|
|
66
|
+
Promise.all(probesToRun.map(async (probe) => {
|
|
67
|
+
try {
|
|
68
|
+
const response = await client.get(probe.endpoint);
|
|
69
|
+
return { id: probe.id, available: response.statusCode < 400 };
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return { id: probe.id, available: false };
|
|
73
|
+
}
|
|
74
|
+
})),
|
|
75
|
+
detectAbapRelease(client),
|
|
76
|
+
]);
|
|
77
|
+
// Build result map
|
|
78
|
+
const resultMap = new Map();
|
|
79
|
+
for (const result of probeResults) {
|
|
80
|
+
resultMap.set(result.id, result.available);
|
|
81
|
+
}
|
|
82
|
+
// Resolve all features
|
|
83
|
+
const result = {};
|
|
84
|
+
for (const probe of PROBES) {
|
|
85
|
+
const mode = modeMap[probe.id] ?? 'auto';
|
|
86
|
+
const probeResult = resultMap.get(probe.id) ?? false;
|
|
87
|
+
result[probe.id] = resolveFeature(mode, probeResult, probe.id, probe.description);
|
|
88
|
+
}
|
|
89
|
+
const resolved = result;
|
|
90
|
+
if (abapRelease) {
|
|
91
|
+
resolved.abapRelease = abapRelease;
|
|
92
|
+
}
|
|
93
|
+
return resolved;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Map SAP_BASIS release string to the closest @abaplint/core Version.
|
|
97
|
+
*
|
|
98
|
+
* abaplint versions are additive — each version accepts all syntax from
|
|
99
|
+
* previous versions plus new features. We map to the closest matching
|
|
100
|
+
* version, falling back to Cloud (the superset) for unknown releases.
|
|
101
|
+
*
|
|
102
|
+
* SAP_BASIS release examples: "700", "702", "740", "750", "757", "758"
|
|
103
|
+
* BTP ABAP Environment reports release like "sap_btp" or similar.
|
|
104
|
+
*/
|
|
105
|
+
export function mapSapReleaseToAbaplintVersion(release) {
|
|
106
|
+
const r = release.replace(/\D/g, ''); // strip non-digits ("750" → "750", "7.57" → "757")
|
|
107
|
+
const num = Number.parseInt(r, 10);
|
|
108
|
+
if (Number.isNaN(num))
|
|
109
|
+
return Version.Cloud;
|
|
110
|
+
if (num >= 758)
|
|
111
|
+
return Version.v758;
|
|
112
|
+
if (num >= 757)
|
|
113
|
+
return Version.v757;
|
|
114
|
+
if (num >= 756)
|
|
115
|
+
return Version.v756;
|
|
116
|
+
if (num >= 755)
|
|
117
|
+
return Version.v755;
|
|
118
|
+
if (num >= 754)
|
|
119
|
+
return Version.v754;
|
|
120
|
+
if (num >= 753)
|
|
121
|
+
return Version.v753;
|
|
122
|
+
if (num >= 752)
|
|
123
|
+
return Version.v752;
|
|
124
|
+
if (num >= 751)
|
|
125
|
+
return Version.v751;
|
|
126
|
+
if (num >= 750)
|
|
127
|
+
return Version.v750;
|
|
128
|
+
// v740 has sub-versions in abaplint
|
|
129
|
+
if (num >= 74008)
|
|
130
|
+
return Version.v740sp08;
|
|
131
|
+
if (num >= 74005)
|
|
132
|
+
return Version.v740sp05;
|
|
133
|
+
if (num >= 740)
|
|
134
|
+
return Version.v740sp02;
|
|
135
|
+
if (num >= 702)
|
|
136
|
+
return Version.v702;
|
|
137
|
+
return Version.v700;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Detect the SAP_BASIS release from installed components.
|
|
141
|
+
* Returns the release string (e.g. "757") or undefined on failure.
|
|
142
|
+
*/
|
|
143
|
+
async function detectAbapRelease(client) {
|
|
144
|
+
try {
|
|
145
|
+
const resp = await client.get('/sap/bc/adt/system/components');
|
|
146
|
+
if (resp.statusCode >= 400)
|
|
147
|
+
return undefined;
|
|
148
|
+
const components = parseInstalledComponents(resp.body);
|
|
149
|
+
const basis = components.find((c) => c.name.toUpperCase() === 'SAP_BASIS');
|
|
150
|
+
return basis?.release || undefined;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Get features without probing (for offline/test scenarios) */
|
|
157
|
+
export function resolveWithoutProbing(config) {
|
|
158
|
+
const result = {};
|
|
159
|
+
const descriptions = {
|
|
160
|
+
hana: 'HANA database',
|
|
161
|
+
abapGit: 'abapGit integration',
|
|
162
|
+
rap: 'RAP/CDS development',
|
|
163
|
+
amdp: 'AMDP debugging',
|
|
164
|
+
ui5: 'UI5/Fiori BSP',
|
|
165
|
+
transport: 'CTS transport management',
|
|
166
|
+
};
|
|
167
|
+
for (const [id, mode] of Object.entries(config)) {
|
|
168
|
+
result[id] = resolveFeature(mode, mode === 'on', // Without probing, "auto" defaults to unavailable
|
|
169
|
+
id, descriptions[id] ?? id);
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=features.js.map
|