catalyst-relay 0.5.1 → 0.5.3
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/dist/index.d.mts +40 -3
- package/dist/index.d.ts +40 -3
- package/dist/index.js +270 -83
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +268 -83
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -1
package/dist/index.d.mts
CHANGED
|
@@ -182,7 +182,7 @@ type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;
|
|
|
182
182
|
/**
|
|
183
183
|
* Machine-readable error codes
|
|
184
184
|
*/
|
|
185
|
-
type ErrorCode = 'AUTH_FAILED' | 'SESSION_EXPIRED' | 'SESSION_NOT_FOUND' | 'CSRF_INVALID' | 'OBJECT_LOCKED' | 'OBJECT_NOT_FOUND' | 'TRANSPORT_REQUIRED' | 'ACTIVATION_FAILED' | 'VALIDATION_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
185
|
+
type ErrorCode = 'AUTH_FAILED' | 'SESSION_EXPIRED' | 'SESSION_NOT_FOUND' | 'CSRF_INVALID' | 'OBJECT_LOCKED' | 'OBJECT_NOT_FOUND' | 'TRANSPORT_REQUIRED' | 'ACTIVATION_FAILED' | 'CHECK_FAILED' | 'VALIDATION_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
188
|
* Session management type definitions
|
|
@@ -308,6 +308,13 @@ interface ActivationMessage {
|
|
|
308
308
|
column?: number;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
interface CheckResult {
|
|
312
|
+
name: string;
|
|
313
|
+
extension: string;
|
|
314
|
+
status: 'success' | 'warning' | 'error';
|
|
315
|
+
messages: ActivationMessage[];
|
|
316
|
+
}
|
|
317
|
+
|
|
311
318
|
/**
|
|
312
319
|
* Tree types — Public and internal type definitions
|
|
313
320
|
*/
|
|
@@ -341,6 +348,21 @@ interface Package {
|
|
|
341
348
|
name: string;
|
|
342
349
|
description?: string;
|
|
343
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Get list of available packages
|
|
353
|
+
*
|
|
354
|
+
* Uses the ADT search API with DEVC/K object type to search for packages,
|
|
355
|
+
* then enriches results with descriptions from the virtualfolders API.
|
|
356
|
+
*
|
|
357
|
+
* @param client - ADT client
|
|
358
|
+
* @param filter - Package name filter pattern (default: '*' for all packages)
|
|
359
|
+
* Examples: 'Z*' for custom packages, '$TMP' for local, 'ZSNAP*' for specific prefix
|
|
360
|
+
* @returns Array of packages or error
|
|
361
|
+
*/
|
|
362
|
+
interface GetPackagesOptions {
|
|
363
|
+
filter?: string;
|
|
364
|
+
includeDescriptions?: boolean;
|
|
365
|
+
}
|
|
344
366
|
|
|
345
367
|
/**
|
|
346
368
|
* Transports — List transport requests for a package
|
|
@@ -543,8 +565,9 @@ interface ADTClient {
|
|
|
543
565
|
update(object: ObjectContent, transport?: string): AsyncResult<void>;
|
|
544
566
|
upsert(objects: ObjectContent[], packageName: string, transport?: string): AsyncResult<UpsertResult[]>;
|
|
545
567
|
activate(objects: ObjectRef[]): AsyncResult<ActivationResult[]>;
|
|
568
|
+
checkSyntax(objects: ObjectRef[]): AsyncResult<CheckResult[]>;
|
|
546
569
|
delete(objects: ObjectRef[], transport?: string): AsyncResult<void>;
|
|
547
|
-
getPackages(
|
|
570
|
+
getPackages(options?: GetPackagesOptions): AsyncResult<Package[]>;
|
|
548
571
|
getTree(query: TreeQuery): AsyncResult<TreeResponse>;
|
|
549
572
|
getPackageStats(packageName: string): AsyncResult<PackageNode>;
|
|
550
573
|
getPackageStats(packageNames: string[]): AsyncResult<PackageNode[]>;
|
|
@@ -567,4 +590,18 @@ interface ADTClient {
|
|
|
567
590
|
|
|
568
591
|
declare function createClient(config: ClientConfig): Result<ADTClient, Error>;
|
|
569
592
|
|
|
570
|
-
|
|
593
|
+
/**
|
|
594
|
+
* Logging Utility — Debug logging with activation control
|
|
595
|
+
*
|
|
596
|
+
* Logs are silent by default. Call activateLogging() to enable console output.
|
|
597
|
+
*/
|
|
598
|
+
/**
|
|
599
|
+
* Enable debug logging to console
|
|
600
|
+
*/
|
|
601
|
+
declare function activateLogging(): void;
|
|
602
|
+
/**
|
|
603
|
+
* Disable debug logging (default state)
|
|
604
|
+
*/
|
|
605
|
+
declare function deactivateLogging(): void;
|
|
606
|
+
|
|
607
|
+
export { type ADTClient, type ActivationMessage, type ActivationResult, type Aggregation, type ApiResponse, type AsyncResult, type AuthConfig, type AuthType, type BasicAuthConfig, type BasicFilter, type BetweenFilter, type ClientConfig, type ColumnInfo, type DataFrame, type DataPreviewQuery, type Dependency, type DiffResult, type DistinctResult, type ErrorCode, type ErrorResponse, type ExportableSessionState, type FolderNode, type ListFilter, type ObjectConfig, type ObjectContent, type ObjectMetadata, type ObjectNode, type ObjectRef, type ObjectWithContent, type Package, type PackageNode, type Parameter, type PreviewSQL, type QueryFilter, type Result, type SamlAuthConfig, type SearchResult, type Session, type Sorting, type SsoAuthConfig, type SuccessResponse, type Transport, type TransportConfig, type TreeQuery, type TreeResponse, type UpsertResult, activateLogging, buildSQLQuery, createClient, deactivateLogging, err, ok };
|
package/dist/index.d.ts
CHANGED
|
@@ -182,7 +182,7 @@ type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;
|
|
|
182
182
|
/**
|
|
183
183
|
* Machine-readable error codes
|
|
184
184
|
*/
|
|
185
|
-
type ErrorCode = 'AUTH_FAILED' | 'SESSION_EXPIRED' | 'SESSION_NOT_FOUND' | 'CSRF_INVALID' | 'OBJECT_LOCKED' | 'OBJECT_NOT_FOUND' | 'TRANSPORT_REQUIRED' | 'ACTIVATION_FAILED' | 'VALIDATION_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
185
|
+
type ErrorCode = 'AUTH_FAILED' | 'SESSION_EXPIRED' | 'SESSION_NOT_FOUND' | 'CSRF_INVALID' | 'OBJECT_LOCKED' | 'OBJECT_NOT_FOUND' | 'TRANSPORT_REQUIRED' | 'ACTIVATION_FAILED' | 'CHECK_FAILED' | 'VALIDATION_ERROR' | 'NETWORK_ERROR' | 'UNKNOWN_ERROR';
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
188
|
* Session management type definitions
|
|
@@ -308,6 +308,13 @@ interface ActivationMessage {
|
|
|
308
308
|
column?: number;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
interface CheckResult {
|
|
312
|
+
name: string;
|
|
313
|
+
extension: string;
|
|
314
|
+
status: 'success' | 'warning' | 'error';
|
|
315
|
+
messages: ActivationMessage[];
|
|
316
|
+
}
|
|
317
|
+
|
|
311
318
|
/**
|
|
312
319
|
* Tree types — Public and internal type definitions
|
|
313
320
|
*/
|
|
@@ -341,6 +348,21 @@ interface Package {
|
|
|
341
348
|
name: string;
|
|
342
349
|
description?: string;
|
|
343
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* Get list of available packages
|
|
353
|
+
*
|
|
354
|
+
* Uses the ADT search API with DEVC/K object type to search for packages,
|
|
355
|
+
* then enriches results with descriptions from the virtualfolders API.
|
|
356
|
+
*
|
|
357
|
+
* @param client - ADT client
|
|
358
|
+
* @param filter - Package name filter pattern (default: '*' for all packages)
|
|
359
|
+
* Examples: 'Z*' for custom packages, '$TMP' for local, 'ZSNAP*' for specific prefix
|
|
360
|
+
* @returns Array of packages or error
|
|
361
|
+
*/
|
|
362
|
+
interface GetPackagesOptions {
|
|
363
|
+
filter?: string;
|
|
364
|
+
includeDescriptions?: boolean;
|
|
365
|
+
}
|
|
344
366
|
|
|
345
367
|
/**
|
|
346
368
|
* Transports — List transport requests for a package
|
|
@@ -543,8 +565,9 @@ interface ADTClient {
|
|
|
543
565
|
update(object: ObjectContent, transport?: string): AsyncResult<void>;
|
|
544
566
|
upsert(objects: ObjectContent[], packageName: string, transport?: string): AsyncResult<UpsertResult[]>;
|
|
545
567
|
activate(objects: ObjectRef[]): AsyncResult<ActivationResult[]>;
|
|
568
|
+
checkSyntax(objects: ObjectRef[]): AsyncResult<CheckResult[]>;
|
|
546
569
|
delete(objects: ObjectRef[], transport?: string): AsyncResult<void>;
|
|
547
|
-
getPackages(
|
|
570
|
+
getPackages(options?: GetPackagesOptions): AsyncResult<Package[]>;
|
|
548
571
|
getTree(query: TreeQuery): AsyncResult<TreeResponse>;
|
|
549
572
|
getPackageStats(packageName: string): AsyncResult<PackageNode>;
|
|
550
573
|
getPackageStats(packageNames: string[]): AsyncResult<PackageNode[]>;
|
|
@@ -567,4 +590,18 @@ interface ADTClient {
|
|
|
567
590
|
|
|
568
591
|
declare function createClient(config: ClientConfig): Result<ADTClient, Error>;
|
|
569
592
|
|
|
570
|
-
|
|
593
|
+
/**
|
|
594
|
+
* Logging Utility — Debug logging with activation control
|
|
595
|
+
*
|
|
596
|
+
* Logs are silent by default. Call activateLogging() to enable console output.
|
|
597
|
+
*/
|
|
598
|
+
/**
|
|
599
|
+
* Enable debug logging to console
|
|
600
|
+
*/
|
|
601
|
+
declare function activateLogging(): void;
|
|
602
|
+
/**
|
|
603
|
+
* Disable debug logging (default state)
|
|
604
|
+
*/
|
|
605
|
+
declare function deactivateLogging(): void;
|
|
606
|
+
|
|
607
|
+
export { type ADTClient, type ActivationMessage, type ActivationResult, type Aggregation, type ApiResponse, type AsyncResult, type AuthConfig, type AuthType, type BasicAuthConfig, type BasicFilter, type BetweenFilter, type ClientConfig, type ColumnInfo, type DataFrame, type DataPreviewQuery, type Dependency, type DiffResult, type DistinctResult, type ErrorCode, type ErrorResponse, type ExportableSessionState, type FolderNode, type ListFilter, type ObjectConfig, type ObjectContent, type ObjectMetadata, type ObjectNode, type ObjectRef, type ObjectWithContent, type Package, type PackageNode, type Parameter, type PreviewSQL, type QueryFilter, type Result, type SamlAuthConfig, type SearchResult, type Session, type Sorting, type SsoAuthConfig, type SuccessResponse, type Transport, type TransportConfig, type TreeQuery, type TreeResponse, type UpsertResult, activateLogging, buildSQLQuery, createClient, deactivateLogging, err, ok };
|
package/dist/index.js
CHANGED
|
@@ -30,8 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
activateLogging: () => activateLogging,
|
|
33
34
|
buildSQLQuery: () => buildSQLQuery,
|
|
34
35
|
createClient: () => createClient,
|
|
36
|
+
deactivateLogging: () => deactivateLogging,
|
|
35
37
|
err: () => err,
|
|
36
38
|
ok: () => ok
|
|
37
39
|
});
|
|
@@ -871,6 +873,12 @@ function extractCsrfToken(headers) {
|
|
|
871
873
|
|
|
872
874
|
// src/core/utils/logging.ts
|
|
873
875
|
var isActive = false;
|
|
876
|
+
function activateLogging() {
|
|
877
|
+
isActive = true;
|
|
878
|
+
}
|
|
879
|
+
function deactivateLogging() {
|
|
880
|
+
isActive = false;
|
|
881
|
+
}
|
|
874
882
|
function debug(message) {
|
|
875
883
|
if (isActive) {
|
|
876
884
|
console.log(`[DEBUG] ${message}`);
|
|
@@ -1093,16 +1101,22 @@ function exportSessionState(ctx, ssoCerts) {
|
|
|
1093
1101
|
// src/client/methods/session/importSessionState.ts
|
|
1094
1102
|
var DEFAULT_REFRESH_INTERVAL2 = 30 * 60 * 1e3;
|
|
1095
1103
|
async function importSessionState(ctx, state, setSsoCerts) {
|
|
1104
|
+
debug(`importSessionState: starting import`);
|
|
1096
1105
|
if (state.session.expiresAt <= Date.now()) {
|
|
1106
|
+
debug(`importSessionState: session expired`);
|
|
1097
1107
|
return err(new Error("Session has expired"));
|
|
1098
1108
|
}
|
|
1109
|
+
debug(`importSessionState: session expiry OK (expires at ${state.session.expiresAt})`);
|
|
1099
1110
|
ctx.state.session = state.session;
|
|
1100
1111
|
ctx.state.csrfToken = state.csrfToken;
|
|
1112
|
+
debug(`importSessionState: restored session and CSRF token`);
|
|
1101
1113
|
ctx.state.cookies.clear();
|
|
1102
1114
|
for (const cookie of state.cookies) {
|
|
1103
1115
|
ctx.state.cookies.set(cookie.name, cookie.value);
|
|
1104
1116
|
}
|
|
1117
|
+
debug(`importSessionState: restored ${state.cookies.length} cookies`);
|
|
1105
1118
|
if (state.authType === "sso" && state.ssoCertPaths) {
|
|
1119
|
+
debug(`importSessionState: loading SSO certs from ${state.ssoCertPaths.fullChainPath}`);
|
|
1106
1120
|
try {
|
|
1107
1121
|
const fs = await import("fs/promises");
|
|
1108
1122
|
const [fullChain, key] = await Promise.all([
|
|
@@ -1110,32 +1124,52 @@ async function importSessionState(ctx, state, setSsoCerts) {
|
|
|
1110
1124
|
fs.readFile(state.ssoCertPaths.keyPath, "utf-8")
|
|
1111
1125
|
]);
|
|
1112
1126
|
setSsoCerts({ cert: fullChain, key });
|
|
1127
|
+
debug(`importSessionState: SSO certs loaded successfully`);
|
|
1113
1128
|
} catch (certErr) {
|
|
1129
|
+
debug(`importSessionState: SSO cert load failed: ${certErr}`);
|
|
1114
1130
|
return err(new Error(`Failed to load SSO certificates: ${certErr instanceof Error ? certErr.message : String(certErr)}`));
|
|
1115
1131
|
}
|
|
1116
1132
|
}
|
|
1133
|
+
debug(`importSessionState: fetching fresh CSRF token...`);
|
|
1117
1134
|
const [response, reqErr] = await ctx.request({
|
|
1118
1135
|
method: "GET",
|
|
1119
|
-
path: "/sap/bc/adt/compatibility/graph"
|
|
1136
|
+
path: "/sap/bc/adt/compatibility/graph",
|
|
1137
|
+
headers: {
|
|
1138
|
+
[CSRF_TOKEN_HEADER]: FETCH_CSRF_TOKEN
|
|
1139
|
+
}
|
|
1120
1140
|
});
|
|
1141
|
+
debug(`importSessionState: CSRF fetch completed`);
|
|
1121
1142
|
if (reqErr) {
|
|
1143
|
+
debug(`importSessionState: CSRF fetch error: ${reqErr.message}`);
|
|
1122
1144
|
ctx.state.session = null;
|
|
1123
1145
|
ctx.state.csrfToken = null;
|
|
1124
1146
|
ctx.state.cookies.clear();
|
|
1125
1147
|
return err(new Error(`Session validation failed: ${reqErr.message}`));
|
|
1126
1148
|
}
|
|
1127
1149
|
if (!response.ok) {
|
|
1150
|
+
debug(`importSessionState: CSRF fetch failed with status ${response.status}`);
|
|
1128
1151
|
ctx.state.session = null;
|
|
1129
1152
|
ctx.state.csrfToken = null;
|
|
1130
1153
|
ctx.state.cookies.clear();
|
|
1131
1154
|
return err(new Error(`Session validation failed with status ${response.status}`));
|
|
1132
1155
|
}
|
|
1156
|
+
const newToken = extractCsrfToken(response.headers);
|
|
1157
|
+
if (!newToken) {
|
|
1158
|
+
debug(`importSessionState: no CSRF token in response headers`);
|
|
1159
|
+
ctx.state.session = null;
|
|
1160
|
+
ctx.state.csrfToken = null;
|
|
1161
|
+
ctx.state.cookies.clear();
|
|
1162
|
+
return err(new Error("Session validation failed: no CSRF token returned"));
|
|
1163
|
+
}
|
|
1164
|
+
ctx.state.csrfToken = newToken;
|
|
1165
|
+
debug(`importSessionState: new CSRF token obtained: ${newToken.substring(0, 20)}...`);
|
|
1133
1166
|
const autoRefresh = ctx.state.config.autoRefresh ?? { enabled: true };
|
|
1134
1167
|
if (autoRefresh.enabled) {
|
|
1135
1168
|
const interval = autoRefresh.intervalMs ?? DEFAULT_REFRESH_INTERVAL2;
|
|
1136
1169
|
ctx.startAutoRefresh(interval);
|
|
1137
1170
|
debug(`Auto-refresh started with ${interval}ms interval (after import)`);
|
|
1138
1171
|
}
|
|
1172
|
+
debug(`importSessionState: import complete`);
|
|
1139
1173
|
return ok(true);
|
|
1140
1174
|
}
|
|
1141
1175
|
|
|
@@ -1177,6 +1211,14 @@ var OBJECT_CONFIG_MAP = {
|
|
|
1177
1211
|
dpEndpoint: "ddic",
|
|
1178
1212
|
dpParam: "ddicEntityName"
|
|
1179
1213
|
},
|
|
1214
|
+
"astablds": {
|
|
1215
|
+
endpoint: "ddic/structures",
|
|
1216
|
+
nameSpace: 'xmlns:blue="http://www.sap.com/wbobj/blue"',
|
|
1217
|
+
rootName: "blue:blueSource",
|
|
1218
|
+
type: "STRU/D",
|
|
1219
|
+
label: "Structure" /* STRUCTURE */,
|
|
1220
|
+
extension: "astablds"
|
|
1221
|
+
},
|
|
1180
1222
|
"asprog": {
|
|
1181
1223
|
endpoint: "programs/programs",
|
|
1182
1224
|
nameSpace: 'xmlns:program="http://www.sap.com/adt/programs/programs"',
|
|
@@ -1493,8 +1535,201 @@ function extractActivationErrors(objects, xml, _extension) {
|
|
|
1493
1535
|
return ok(results);
|
|
1494
1536
|
}
|
|
1495
1537
|
|
|
1538
|
+
// src/core/adt/craud/syntaxCheck.ts
|
|
1539
|
+
async function checkSyntax(client, objects) {
|
|
1540
|
+
if (objects.length === 0) {
|
|
1541
|
+
return ok([]);
|
|
1542
|
+
}
|
|
1543
|
+
const extension = objects[0].extension;
|
|
1544
|
+
const config = getConfigByExtension(extension);
|
|
1545
|
+
if (!config) return err(new Error(`Unsupported extension: ${extension}`));
|
|
1546
|
+
for (const obj of objects) {
|
|
1547
|
+
if (obj.extension !== extension) {
|
|
1548
|
+
return err(new Error("All objects must have the same extension for batch syntax check"));
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
const sources = /* @__PURE__ */ new Map();
|
|
1552
|
+
for (const obj of objects) {
|
|
1553
|
+
const [result, readErr] = await readObject(client, obj);
|
|
1554
|
+
if (readErr) return err(new Error(`Failed to read ${obj.name}: ${readErr.message}`));
|
|
1555
|
+
sources.set(obj.name.toLowerCase(), result.content);
|
|
1556
|
+
}
|
|
1557
|
+
const objectRefs = objects.map((obj) => {
|
|
1558
|
+
const uri = `/sap/bc/adt/${config.endpoint}/${obj.name.toLowerCase()}`;
|
|
1559
|
+
const sourceUri = `${uri}/source/main`;
|
|
1560
|
+
const content = sources.get(obj.name.toLowerCase()) ?? "";
|
|
1561
|
+
const encoded = Buffer.from(content).toString("base64");
|
|
1562
|
+
return `<chkrun:checkObject adtcore:uri="${uri}" chkrun:version="active">
|
|
1563
|
+
<chkrun:artifacts>
|
|
1564
|
+
<chkrun:artifact chkrun:contentType="text/plain; charset=utf-8" chkrun:uri="${sourceUri}">
|
|
1565
|
+
<chkrun:content>${encoded}</chkrun:content>
|
|
1566
|
+
</chkrun:artifact>
|
|
1567
|
+
</chkrun:artifacts>
|
|
1568
|
+
</chkrun:checkObject>`;
|
|
1569
|
+
}).join("\n ");
|
|
1570
|
+
const body = `<?xml version="1.0" encoding="UTF-8"?>
|
|
1571
|
+
<chkrun:checkObjectList xmlns:chkrun="http://www.sap.com/adt/checkrun"
|
|
1572
|
+
xmlns:adtcore="http://www.sap.com/adt/core">
|
|
1573
|
+
${objectRefs}
|
|
1574
|
+
</chkrun:checkObjectList>`;
|
|
1575
|
+
const [response, requestErr] = await client.request({
|
|
1576
|
+
method: "POST",
|
|
1577
|
+
path: "/sap/bc/adt/checkruns",
|
|
1578
|
+
params: {
|
|
1579
|
+
"reporters": "abapCheckRun"
|
|
1580
|
+
},
|
|
1581
|
+
headers: {
|
|
1582
|
+
"Content-Type": "application/vnd.sap.adt.checkobjects+xml",
|
|
1583
|
+
"Accept": "application/vnd.sap.adt.checkmessages+xml"
|
|
1584
|
+
},
|
|
1585
|
+
body
|
|
1586
|
+
});
|
|
1587
|
+
if (requestErr) {
|
|
1588
|
+
return err(requestErr);
|
|
1589
|
+
}
|
|
1590
|
+
const text = await response.text();
|
|
1591
|
+
debug(`Syntax check response status: ${response.status}`);
|
|
1592
|
+
debug(`Syntax check response: ${text.substring(0, 500)}`);
|
|
1593
|
+
if (!response.ok) {
|
|
1594
|
+
const errorMsg = extractError(text);
|
|
1595
|
+
return err(new Error(`Syntax check failed: ${errorMsg}`));
|
|
1596
|
+
}
|
|
1597
|
+
const [results, parseErr] = extractCheckMessages(objects, text);
|
|
1598
|
+
if (parseErr) {
|
|
1599
|
+
return err(parseErr);
|
|
1600
|
+
}
|
|
1601
|
+
return ok(results);
|
|
1602
|
+
}
|
|
1603
|
+
function extractCheckMessages(objects, xml) {
|
|
1604
|
+
const [doc, parseErr] = safeParseXml(xml);
|
|
1605
|
+
if (parseErr) {
|
|
1606
|
+
return err(parseErr);
|
|
1607
|
+
}
|
|
1608
|
+
const messageMap = /* @__PURE__ */ new Map();
|
|
1609
|
+
objects.forEach((obj) => messageMap.set(obj.name.toLowerCase(), []));
|
|
1610
|
+
let msgElements = doc.getElementsByTagName("chkrun:checkMessage");
|
|
1611
|
+
if (msgElements.length === 0) {
|
|
1612
|
+
msgElements = doc.getElementsByTagName("checkMessage");
|
|
1613
|
+
}
|
|
1614
|
+
const startRegex = /#start=(\d+),(\d+)/;
|
|
1615
|
+
for (let i = 0; i < msgElements.length; i++) {
|
|
1616
|
+
const msg = msgElements[i];
|
|
1617
|
+
if (!msg) continue;
|
|
1618
|
+
const type = msg.getAttribute("chkrun:type") ?? msg.getAttribute("type");
|
|
1619
|
+
if (!type) continue;
|
|
1620
|
+
const uri = msg.getAttribute("chkrun:uri") ?? msg.getAttribute("uri") ?? "";
|
|
1621
|
+
let line;
|
|
1622
|
+
let column;
|
|
1623
|
+
const match = startRegex.exec(uri);
|
|
1624
|
+
if (match && match[1] && match[2]) {
|
|
1625
|
+
line = parseInt(match[1], 10);
|
|
1626
|
+
column = parseInt(match[2], 10);
|
|
1627
|
+
}
|
|
1628
|
+
const matchingObj = objects.find(
|
|
1629
|
+
(obj) => uri.toLowerCase().includes(obj.name.toLowerCase())
|
|
1630
|
+
);
|
|
1631
|
+
if (!matchingObj) continue;
|
|
1632
|
+
const text = msg.getAttribute("chkrun:shortText") ?? msg.getAttribute("shortText");
|
|
1633
|
+
if (!text) continue;
|
|
1634
|
+
const message = {
|
|
1635
|
+
severity: type === "E" ? "error" : type === "W" ? "warning" : "info",
|
|
1636
|
+
text,
|
|
1637
|
+
...line !== void 0 && { line },
|
|
1638
|
+
...column !== void 0 && { column }
|
|
1639
|
+
};
|
|
1640
|
+
const messages = messageMap.get(matchingObj.name.toLowerCase()) || [];
|
|
1641
|
+
messages.push(message);
|
|
1642
|
+
messageMap.set(matchingObj.name.toLowerCase(), messages);
|
|
1643
|
+
}
|
|
1644
|
+
const results = objects.map((obj) => {
|
|
1645
|
+
const messages = messageMap.get(obj.name.toLowerCase()) || [];
|
|
1646
|
+
const hasErrors = messages.some((m) => m.severity === "error");
|
|
1647
|
+
return {
|
|
1648
|
+
name: obj.name,
|
|
1649
|
+
extension: obj.extension,
|
|
1650
|
+
status: hasErrors ? "error" : messages.length > 0 ? "warning" : "success",
|
|
1651
|
+
messages
|
|
1652
|
+
};
|
|
1653
|
+
});
|
|
1654
|
+
return ok(results);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
// src/core/adt/discovery/tree/packageStats.ts
|
|
1658
|
+
function constructPackageStatsBody(packageNames) {
|
|
1659
|
+
const names = packageNames.length === 1 ? [...packageNames, "SRIS_TEST_DATA_VFS_EMPTY"] : packageNames;
|
|
1660
|
+
const values = names.map((name) => ` <vfs:value>${name}</vfs:value>`).join("\n");
|
|
1661
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1662
|
+
<vfs:virtualFoldersRequest xmlns:vfs="http://www.sap.com/adt/ris/virtualFolders" objectSearchPattern="*">
|
|
1663
|
+
<vfs:preselection facet="package">
|
|
1664
|
+
${values}
|
|
1665
|
+
</vfs:preselection>
|
|
1666
|
+
<vfs:facetorder>
|
|
1667
|
+
<vfs:facet>package</vfs:facet>
|
|
1668
|
+
<vfs:facet>group</vfs:facet>
|
|
1669
|
+
<vfs:facet>type</vfs:facet>
|
|
1670
|
+
</vfs:facetorder>
|
|
1671
|
+
</vfs:virtualFoldersRequest>`;
|
|
1672
|
+
}
|
|
1673
|
+
function parsePackageStats(xml) {
|
|
1674
|
+
const [doc, parseErr] = safeParseXml(xml);
|
|
1675
|
+
if (parseErr) return [];
|
|
1676
|
+
const packages = [];
|
|
1677
|
+
const virtualFolders = doc.getElementsByTagName("vfs:virtualFolder");
|
|
1678
|
+
for (let i = 0; i < virtualFolders.length; i++) {
|
|
1679
|
+
const vf = virtualFolders[i];
|
|
1680
|
+
if (!vf) continue;
|
|
1681
|
+
const facet = vf.getAttribute("facet")?.toUpperCase();
|
|
1682
|
+
if (facet !== "PACKAGE") continue;
|
|
1683
|
+
const name = vf.getAttribute("name");
|
|
1684
|
+
if (!name) continue;
|
|
1685
|
+
const countAttr = vf.getAttribute("counter");
|
|
1686
|
+
const count = countAttr ? parseInt(countAttr, 10) : 0;
|
|
1687
|
+
const description = vf.getAttribute("text");
|
|
1688
|
+
const pkg = {
|
|
1689
|
+
name,
|
|
1690
|
+
numContents: count
|
|
1691
|
+
};
|
|
1692
|
+
if (description) pkg.description = description;
|
|
1693
|
+
packages.push(pkg);
|
|
1694
|
+
}
|
|
1695
|
+
return packages;
|
|
1696
|
+
}
|
|
1697
|
+
async function getPackageStats(client, packageNames) {
|
|
1698
|
+
const isSingle = typeof packageNames === "string";
|
|
1699
|
+
const names = isSingle ? [packageNames] : packageNames;
|
|
1700
|
+
if (names.length === 0) {
|
|
1701
|
+
return ok([]);
|
|
1702
|
+
}
|
|
1703
|
+
const body = constructPackageStatsBody(names);
|
|
1704
|
+
const [response, requestErr] = await client.request({
|
|
1705
|
+
method: "POST",
|
|
1706
|
+
path: "/sap/bc/adt/repository/informationsystem/virtualfolders/contents",
|
|
1707
|
+
headers: {
|
|
1708
|
+
"Content-Type": "application/vnd.sap.adt.repository.virtualfolders.request.v1+xml",
|
|
1709
|
+
"Accept": "application/vnd.sap.adt.repository.virtualfolders.result.v1+xml"
|
|
1710
|
+
},
|
|
1711
|
+
body
|
|
1712
|
+
});
|
|
1713
|
+
if (requestErr) return err(requestErr);
|
|
1714
|
+
if (!response.ok) {
|
|
1715
|
+
const text = await response.text();
|
|
1716
|
+
const errorMsg = extractError(text);
|
|
1717
|
+
return err(new Error(`Package stats fetch failed: ${errorMsg}`));
|
|
1718
|
+
}
|
|
1719
|
+
const xml = await response.text();
|
|
1720
|
+
const packages = parsePackageStats(xml).filter((pkg) => pkg.name !== "SRIS_TEST_DATA_VFS_EMPTY");
|
|
1721
|
+
if (isSingle) {
|
|
1722
|
+
if (packages.length === 0) {
|
|
1723
|
+
return err(new Error(`Package ${packageNames} not found`));
|
|
1724
|
+
}
|
|
1725
|
+
return ok(packages[0]);
|
|
1726
|
+
}
|
|
1727
|
+
return ok(packages);
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1496
1730
|
// src/core/adt/discovery/packages.ts
|
|
1497
|
-
async function getPackages(client,
|
|
1731
|
+
async function getPackages(client, options = {}) {
|
|
1732
|
+
const { filter = "*", includeDescriptions = false } = options;
|
|
1498
1733
|
const params = new URLSearchParams([
|
|
1499
1734
|
["operation", "quickSearch"],
|
|
1500
1735
|
["query", filter],
|
|
@@ -1518,18 +1753,31 @@ async function getPackages(client, filter = "*") {
|
|
|
1518
1753
|
if (parseErr) {
|
|
1519
1754
|
return err(parseErr);
|
|
1520
1755
|
}
|
|
1521
|
-
const
|
|
1756
|
+
const packageNames = [];
|
|
1522
1757
|
const objectRefs = doc.getElementsByTagNameNS("http://www.sap.com/adt/core", "objectReference");
|
|
1523
1758
|
for (let i = 0; i < objectRefs.length; i++) {
|
|
1524
1759
|
const obj = objectRefs[i];
|
|
1525
1760
|
if (!obj) return err(new Error("Invalid object reference in package search results"));
|
|
1526
1761
|
const name = obj.getAttributeNS("http://www.sap.com/adt/core", "name") || obj.getAttribute("adtcore:name");
|
|
1527
|
-
const description = obj.getAttributeNS("http://www.sap.com/adt/core", "description") || obj.getAttribute("adtcore:description");
|
|
1528
1762
|
if (!name) return err(new Error("Package name missing in object reference"));
|
|
1763
|
+
packageNames.push(name);
|
|
1764
|
+
}
|
|
1765
|
+
if (packageNames.length === 0) return ok([]);
|
|
1766
|
+
if (!includeDescriptions) return ok(packageNames.map((name) => ({ name })));
|
|
1767
|
+
const [stats, statsErr] = await getPackageStats(client, packageNames);
|
|
1768
|
+
if (statsErr) {
|
|
1769
|
+
return ok(packageNames.map((name) => ({ name })));
|
|
1770
|
+
}
|
|
1771
|
+
const descriptionMap = /* @__PURE__ */ new Map();
|
|
1772
|
+
for (const stat2 of stats) {
|
|
1773
|
+
if (stat2.description) descriptionMap.set(stat2.name, stat2.description);
|
|
1774
|
+
}
|
|
1775
|
+
const packages = packageNames.map((name) => {
|
|
1529
1776
|
const pkg = { name };
|
|
1777
|
+
const description = descriptionMap.get(name);
|
|
1530
1778
|
if (description) pkg.description = description;
|
|
1531
|
-
|
|
1532
|
-
}
|
|
1779
|
+
return pkg;
|
|
1780
|
+
});
|
|
1533
1781
|
return ok(packages);
|
|
1534
1782
|
}
|
|
1535
1783
|
|
|
@@ -1769,79 +2017,6 @@ async function getTree(client, query = {}) {
|
|
|
1769
2017
|
return ok(result);
|
|
1770
2018
|
}
|
|
1771
2019
|
|
|
1772
|
-
// src/core/adt/discovery/tree/packageStats.ts
|
|
1773
|
-
function constructPackageStatsBody(packageNames) {
|
|
1774
|
-
const names = packageNames.length === 1 ? [...packageNames, "SRIS_TEST_DATA_VFS_EMPTY"] : packageNames;
|
|
1775
|
-
const values = names.map((name) => ` <vfs:value>${name}</vfs:value>`).join("\n");
|
|
1776
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
1777
|
-
<vfs:virtualFoldersRequest xmlns:vfs="http://www.sap.com/adt/ris/virtualFolders" objectSearchPattern="*">
|
|
1778
|
-
<vfs:preselection facet="package">
|
|
1779
|
-
${values}
|
|
1780
|
-
</vfs:preselection>
|
|
1781
|
-
<vfs:facetorder>
|
|
1782
|
-
<vfs:facet>package</vfs:facet>
|
|
1783
|
-
<vfs:facet>group</vfs:facet>
|
|
1784
|
-
<vfs:facet>type</vfs:facet>
|
|
1785
|
-
</vfs:facetorder>
|
|
1786
|
-
</vfs:virtualFoldersRequest>`;
|
|
1787
|
-
}
|
|
1788
|
-
function parsePackageStats(xml) {
|
|
1789
|
-
const [doc, parseErr] = safeParseXml(xml);
|
|
1790
|
-
if (parseErr) return [];
|
|
1791
|
-
const packages = [];
|
|
1792
|
-
const virtualFolders = doc.getElementsByTagName("vfs:virtualFolder");
|
|
1793
|
-
for (let i = 0; i < virtualFolders.length; i++) {
|
|
1794
|
-
const vf = virtualFolders[i];
|
|
1795
|
-
if (!vf) continue;
|
|
1796
|
-
const facet = vf.getAttribute("facet")?.toUpperCase();
|
|
1797
|
-
if (facet !== "PACKAGE") continue;
|
|
1798
|
-
const name = vf.getAttribute("name");
|
|
1799
|
-
if (!name) continue;
|
|
1800
|
-
const countAttr = vf.getAttribute("counter");
|
|
1801
|
-
const count = countAttr ? parseInt(countAttr, 10) : 0;
|
|
1802
|
-
const description = vf.getAttribute("text");
|
|
1803
|
-
const pkg = {
|
|
1804
|
-
name,
|
|
1805
|
-
numContents: count
|
|
1806
|
-
};
|
|
1807
|
-
if (description) pkg.description = description;
|
|
1808
|
-
packages.push(pkg);
|
|
1809
|
-
}
|
|
1810
|
-
return packages;
|
|
1811
|
-
}
|
|
1812
|
-
async function getPackageStats(client, packageNames) {
|
|
1813
|
-
const isSingle = typeof packageNames === "string";
|
|
1814
|
-
const names = isSingle ? [packageNames] : packageNames;
|
|
1815
|
-
if (names.length === 0) {
|
|
1816
|
-
return ok([]);
|
|
1817
|
-
}
|
|
1818
|
-
const body = constructPackageStatsBody(names);
|
|
1819
|
-
const [response, requestErr] = await client.request({
|
|
1820
|
-
method: "POST",
|
|
1821
|
-
path: "/sap/bc/adt/repository/informationsystem/virtualfolders/contents",
|
|
1822
|
-
headers: {
|
|
1823
|
-
"Content-Type": "application/vnd.sap.adt.repository.virtualfolders.request.v1+xml",
|
|
1824
|
-
"Accept": "application/vnd.sap.adt.repository.virtualfolders.result.v1+xml"
|
|
1825
|
-
},
|
|
1826
|
-
body
|
|
1827
|
-
});
|
|
1828
|
-
if (requestErr) return err(requestErr);
|
|
1829
|
-
if (!response.ok) {
|
|
1830
|
-
const text = await response.text();
|
|
1831
|
-
const errorMsg = extractError(text);
|
|
1832
|
-
return err(new Error(`Package stats fetch failed: ${errorMsg}`));
|
|
1833
|
-
}
|
|
1834
|
-
const xml = await response.text();
|
|
1835
|
-
const packages = parsePackageStats(xml).filter((pkg) => pkg.name !== "SRIS_TEST_DATA_VFS_EMPTY");
|
|
1836
|
-
if (isSingle) {
|
|
1837
|
-
if (packages.length === 0) {
|
|
1838
|
-
return err(new Error(`Package ${packageNames} not found`));
|
|
1839
|
-
}
|
|
1840
|
-
return ok(packages[0]);
|
|
1841
|
-
}
|
|
1842
|
-
return ok(packages);
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
2020
|
// src/core/adt/transports/transports.ts
|
|
1846
2021
|
async function getTransports(client, packageName) {
|
|
1847
2022
|
const contentType = "application/vnd.sap.as+xml; charset=UTF-8; dataname=com.sap.adt.transport.service.checkData";
|
|
@@ -2497,10 +2672,16 @@ async function deleteObjects(state, requestor, objects, transport) {
|
|
|
2497
2672
|
return ok(void 0);
|
|
2498
2673
|
}
|
|
2499
2674
|
|
|
2675
|
+
// src/client/methods/craud/checkSyntax.ts
|
|
2676
|
+
async function checkSyntax2(state, requestor, objects) {
|
|
2677
|
+
if (!state.session) return err(new Error("Not logged in"));
|
|
2678
|
+
return checkSyntax(requestor, objects);
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2500
2681
|
// src/client/methods/discovery/getPackages.ts
|
|
2501
|
-
async function getPackages2(state, requestor,
|
|
2682
|
+
async function getPackages2(state, requestor, options) {
|
|
2502
2683
|
if (!state.session) return err(new Error("Not logged in"));
|
|
2503
|
-
return getPackages(requestor,
|
|
2684
|
+
return getPackages(requestor, options);
|
|
2504
2685
|
}
|
|
2505
2686
|
|
|
2506
2687
|
// src/client/methods/discovery/getTree.ts
|
|
@@ -2605,6 +2786,7 @@ function createAutoRefresh(getSession, refreshSession3) {
|
|
|
2605
2786
|
debug(`Auto-refresh failed: ${refreshErr.message}`);
|
|
2606
2787
|
}
|
|
2607
2788
|
}, intervalMs);
|
|
2789
|
+
timer.unref();
|
|
2608
2790
|
},
|
|
2609
2791
|
stop() {
|
|
2610
2792
|
if (timer) {
|
|
@@ -2892,12 +3074,15 @@ var ADTClientImpl = class {
|
|
|
2892
3074
|
async activate(objects) {
|
|
2893
3075
|
return activate(this.state, this.requestor, objects);
|
|
2894
3076
|
}
|
|
3077
|
+
async checkSyntax(objects) {
|
|
3078
|
+
return checkSyntax2(this.state, this.requestor, objects);
|
|
3079
|
+
}
|
|
2895
3080
|
async delete(objects, transport) {
|
|
2896
3081
|
return deleteObjects(this.state, this.requestor, objects, transport);
|
|
2897
3082
|
}
|
|
2898
3083
|
// --- Discovery ---
|
|
2899
|
-
async getPackages(
|
|
2900
|
-
return getPackages2(this.state, this.requestor,
|
|
3084
|
+
async getPackages(options) {
|
|
3085
|
+
return getPackages2(this.state, this.requestor, options);
|
|
2901
3086
|
}
|
|
2902
3087
|
async getTree(query) {
|
|
2903
3088
|
return getTree2(this.state, this.requestor, query);
|
|
@@ -2950,8 +3135,10 @@ function createClient(config) {
|
|
|
2950
3135
|
}
|
|
2951
3136
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2952
3137
|
0 && (module.exports = {
|
|
3138
|
+
activateLogging,
|
|
2953
3139
|
buildSQLQuery,
|
|
2954
3140
|
createClient,
|
|
3141
|
+
deactivateLogging,
|
|
2955
3142
|
err,
|
|
2956
3143
|
ok
|
|
2957
3144
|
});
|