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 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(filter?: string): AsyncResult<Package[]>;
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
- 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, buildSQLQuery, createClient, err, ok };
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(filter?: string): AsyncResult<Package[]>;
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
- 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, buildSQLQuery, createClient, err, ok };
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, filter = "*") {
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 packages = [];
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
- packages.push(pkg);
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, filter) {
2682
+ async function getPackages2(state, requestor, options) {
2502
2683
  if (!state.session) return err(new Error("Not logged in"));
2503
- return getPackages(requestor, filter);
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(filter) {
2900
- return getPackages2(this.state, this.requestor, filter);
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
  });