@abapify/adt-atc 0.4.28 → 0.4.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -6
- package/dist/{atc-6cFtFkir.mjs → atc-CIPiGB5o.mjs} +18 -1
- package/dist/atc-CIPiGB5o.mjs.map +1 -0
- package/dist/commands/atc.mjs +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/dist/atc-6cFtFkir.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -99,10 +99,15 @@ Each finding can carry a `checkCategory` (e.g., `CI_PERFORMANCE`,
|
|
|
99
99
|
`CI_SECURITY`): the name of the ATC check category its check is assigned
|
|
100
100
|
to. The `atc` command resolves it automatically from the run's check
|
|
101
101
|
variant form template (`GET /sap/bc/adt/atc/checkvariants/formtemplate`)
|
|
102
|
-
by
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
by resolving each finding's documentation page to its implementing class
|
|
103
|
+
(the worklist `checkId` is a GUID), then mapping that class to a category.
|
|
104
|
+
The `enrichResultWithCategories` helper only does a direct `checkId` lookup;
|
|
105
|
+
the command uses the documentation bridge for normal worklist findings.
|
|
106
|
+
This is best-effort: unavailable templates, unreadable pages, or uncatalogued
|
|
107
|
+
checks leave the category unset without failing the run. Categories are
|
|
108
|
+
resolved from the variant's catalog as fetched after the run, so they
|
|
109
|
+
reflect the variant configuration in effect at that time (including any
|
|
110
|
+
edits made in the meantime).
|
|
106
111
|
|
|
107
112
|
The enrichment helpers are exported for reuse:
|
|
108
113
|
|
|
@@ -113,13 +118,15 @@ import {
|
|
|
113
118
|
enrichResultWithCategories,
|
|
114
119
|
} from '@abapify/adt-atc';
|
|
115
120
|
|
|
116
|
-
// Build the
|
|
121
|
+
// Build the implementing class -> category name index from a parsed form template
|
|
117
122
|
const index = parseCheckCategoryIndex(formTemplateDocument);
|
|
118
123
|
|
|
119
124
|
// Or fetch it through an ADT client (never throws)
|
|
120
125
|
const fetched = await fetchCheckCategoryIndex(client, 'DEFAULT');
|
|
121
126
|
|
|
122
|
-
// Enrich a result in place
|
|
127
|
+
// Enrich a result in place (direct lookup: only resolves findings whose
|
|
128
|
+
// checkId is already the implementing class name; normal worklist findings
|
|
129
|
+
// are enriched by the command via the documentation bridge)
|
|
123
130
|
enrichResultWithCategories(result, index);
|
|
124
131
|
```
|
|
125
132
|
|
|
@@ -243,11 +243,26 @@ function parseCheckClassFromDocumentation(html) {
|
|
|
243
243
|
return html.match(/Check\s*Class[^<]{0,80}<\/h3>\s*<p>\s*[^/<]{0,120}\/\s*((?:\/[A-Za-z0-9_]{1,8}\/)?[A-Za-z][A-Za-z0-9_]*)\s*\//u)?.[1];
|
|
244
244
|
}
|
|
245
245
|
/**
|
|
246
|
+
* Only ATC documentation paths may be fetched — a worklist-provided URI
|
|
247
|
+
* must never drive an authenticated read outside the documentation space.
|
|
248
|
+
*/
|
|
249
|
+
const ATC_DOCUMENTATION_URI = /^\/sap\/bc\/adt\/documentation\/atc\/documents\/[\w/%-]+$/u;
|
|
250
|
+
/**
|
|
246
251
|
* Read one documentation page and resolve its check class, with a shared
|
|
247
252
|
* per-URI cache so repeated findings never re-fetch the same page.
|
|
248
253
|
*/
|
|
249
254
|
async function resolveCheckClassForUri(documentationUri, context) {
|
|
250
255
|
if (context.classByDocumentationUri.has(documentationUri)) return;
|
|
256
|
+
if (!ATC_DOCUMENTATION_URI.test(documentationUri)) {
|
|
257
|
+
context.logger?.warn?.(`⚠️ Skipping non-ATC documentation URI: ${documentationUri}`);
|
|
258
|
+
context.classByDocumentationUri.set(documentationUri, void 0);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (context.reads >= context.maxReads) {
|
|
262
|
+
context.classByDocumentationUri.set(documentationUri, void 0);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
context.reads++;
|
|
251
266
|
try {
|
|
252
267
|
const html = await context.read(documentationUri, context.maxBytes, { headers: { Accept: "application/vnd.sap.adt.docu.v1+html" } });
|
|
253
268
|
context.classByDocumentationUri.set(documentationUri, parseCheckClassFromDocumentation(html));
|
|
@@ -283,6 +298,8 @@ async function resolveCategoriesViaDocumentation(client, findings, classToCatego
|
|
|
283
298
|
const context = {
|
|
284
299
|
read,
|
|
285
300
|
maxBytes: options.maxBytes ?? 65536,
|
|
301
|
+
maxReads: options.maxReads ?? 500,
|
|
302
|
+
reads: 0,
|
|
286
303
|
classByDocumentationUri: /* @__PURE__ */ new Map(),
|
|
287
304
|
logger: options.logger
|
|
288
305
|
};
|
|
@@ -549,4 +566,4 @@ const atcCommand = {
|
|
|
549
566
|
};
|
|
550
567
|
export { enrichResultWithCategories as a, outputGitLabCodeQuality as c, enrichFindingsWithCategories as i, outputSarifReport as l, parseCheckClassFromDocumentation as n, fetchCheckCategoryIndex as o, resolveCategoriesViaDocumentation as r, parseCheckCategoryIndex as s, atcCommand as t };
|
|
551
568
|
|
|
552
|
-
//# sourceMappingURL=atc-
|
|
569
|
+
//# sourceMappingURL=atc-CIPiGB5o.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"atc-CIPiGB5o.mjs","names":[],"sources":["../src/formatters/sarif.ts","../src/formatters/gitlab.ts","../src/check-categories.ts","../src/documentation-bridge.ts","../src/commands/atc.ts"],"sourcesContent":["/**\n * SARIF Formatter\n *\n * Outputs ATC findings in SARIF format for GitHub Code Scanning.\n */\n\nimport { writeFile } from 'node:fs/promises';\nimport type { AtcResult, AtcFinding } from '../types';\n\nexport async function outputSarifReport(\n result: AtcResult,\n outputFile: string,\n _targetName?: string,\n): Promise<void> {\n // Transform ATC findings to SARIF format\n const rules = Array.from(\n new Map(\n result.findings.map((f) => [\n f.checkId,\n {\n id: f.checkId,\n name: f.checkTitle || f.checkId,\n shortDescription: { text: f.checkTitle || f.checkId },\n fullDescription: { text: f.checkTitle || f.checkId },\n defaultConfiguration: {\n level:\n f.priority === 1\n ? 'error'\n : f.priority === 2\n ? 'warning'\n : 'note',\n },\n properties: {\n tags: ['abap', 'code-quality'],\n precision: 'high',\n ...(f.checkCategory && { category: f.checkCategory }),\n },\n },\n ]),\n ).values(),\n );\n\n const results = result.findings.map((finding: AtcFinding) => {\n const filePath = `src/${finding.objectType.toLowerCase()}/${finding.objectName.toLowerCase()}.${finding.objectType.toLowerCase()}`;\n\n // Parse line number from location if available\n const lineMatch = finding.location?.match(/start=(\\d+)/);\n const line = lineMatch ? Number.parseInt(lineMatch[1], 10) : 1;\n\n return {\n ruleId: finding.checkId,\n level:\n finding.priority === 1\n ? 'error'\n : finding.priority === 2\n ? 'warning'\n : 'note',\n message: { text: finding.messageText },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: filePath },\n region: {\n startLine: line,\n startColumn: 1,\n },\n },\n },\n ],\n partialFingerprints: {\n primaryLocationLineHash: `${finding.checkId}-${finding.objectName}-${line}`,\n },\n };\n });\n\n const sarifReport = {\n $schema: 'https://json.schemastore.org/sarif-2.1.0.json',\n version: '2.1.0',\n runs: [\n {\n tool: {\n driver: {\n name: 'ABAP Test Cockpit',\n version: '1.0.0',\n informationUri:\n 'https://help.sap.com/docs/ABAP_PLATFORM_NEW/c238d694b825421f940829321ffa326a/4ec5711c6e391014adc9fffe4e204223.html',\n rules: Array.from(rules),\n },\n },\n results,\n },\n ],\n };\n\n await writeFile(outputFile, JSON.stringify(sarifReport, null, 2));\n console.log(`\\n📄 SARIF report written to: ${outputFile}`);\n console.log(\n `📊 ${result.totalFindings} issues exported for GitHub Code Scanning`,\n );\n}\n","/**\n * GitLab Code Quality Formatter\n *\n * Outputs ATC findings in GitLab Code Quality format.\n *\n * This formatter is a pure output formatter — it maps ATC findings to the\n * GitLab Code Quality JSON structure. Path resolution and line number\n * conversion are delegated to an optional FindingResolver.\n *\n * Without a resolver, the formatter produces PREFIX-style paths and raw\n * ATC (method-relative) line numbers. With a resolver (e.g., from\n * @abapify/adt-plugin-abapgit), it produces correct git paths and\n * file-relative line numbers.\n *\n * @example\n * ```typescript\n * // Without resolver (standalone CLI usage)\n * await outputGitLabCodeQuality(result, 'report.json');\n *\n * // With resolver (inside an abapgit repo)\n * import { createFindingResolver } from '@abapify/adt-plugin-abapgit';\n * const resolver = await createFindingResolver();\n * await outputGitLabCodeQuality(result, 'report.json', { resolver });\n * ```\n */\n\nimport { writeFile } from 'node:fs/promises';\nimport type { AtcResult, AtcFinding, FindingResolver } from '../types';\n\n/**\n * Options for the GitLab Code Quality formatter\n */\nexport interface GitLabFormatterOptions {\n /** Optional resolver for path/line resolution (e.g., from abapgit plugin) */\n resolver?: FindingResolver;\n}\n\n// ── Extract method name from ATC location URI ───────────────────────────\n// Format 1: /sap/bc/adt/oo/classes/zcl_foo/methods/my_method#start=21,0\n// Format 2: ...#type=CLAS%2FOM;name=MY_METHOD;start=21\n\nfunction extractMethodName(location?: string): string | undefined {\n if (!location) return undefined;\n const match =\n location.match(/\\/methods\\/(\\w+)/i) || location.match(/[;?&]name=(\\w+)/i);\n return match ? match[1].toLowerCase() : undefined;\n}\n\n// ── Construct PREFIX-style fallback path ─────────────────────────────────\n\nfunction prefixPath(finding: AtcFinding): string {\n return `src/${finding.objectType.toLowerCase()}/${finding.objectName.toLowerCase()}.${finding.objectType.toLowerCase()}.abap`;\n}\n\n// ── Main formatter ──────────────────────────────────────────────────────\n\nexport async function outputGitLabCodeQuality(\n result: AtcResult,\n outputFile: string,\n options?: GitLabFormatterOptions,\n): Promise<void> {\n const resolver = options?.resolver;\n\n // Transform ATC findings to GitLab Code Quality format\n const gitlabReport = await Promise.all(\n result.findings.map(async (finding: AtcFinding) => {\n // Map ATC priority to GitLab severity\n let severity: string;\n switch (finding.priority) {\n case 1:\n severity = 'blocker';\n break;\n case 2:\n severity = 'major';\n break;\n case 3:\n severity = 'minor';\n break;\n default:\n severity = 'info';\n break;\n }\n\n // Parse ATC line number from location\n const lineMatch = finding.location?.match(/start=(\\d+)/);\n const atcLine = lineMatch ? Number.parseInt(lineMatch[1], 10) : 1;\n\n // Extract method name from ATC location URI\n const methodName = extractMethodName(finding.location);\n\n // Resolve path and line via plugin, or fall back to PREFIX-style\n let filePath: string;\n let fileLine: number;\n\n if (resolver) {\n const resolved = await resolver.resolve(\n finding.objectType,\n finding.objectName,\n atcLine,\n methodName,\n finding.location,\n );\n if (resolved) {\n filePath = resolved.path;\n fileLine = resolved.line;\n } else {\n filePath = prefixPath(finding);\n fileLine = atcLine;\n }\n } else {\n filePath = prefixPath(finding);\n fileLine = atcLine;\n }\n\n // Create unique fingerprint\n const fingerprint = `${finding.checkId}-${finding.objectName}-${fileLine}`;\n\n return {\n description: finding.messageText,\n check_name: finding.checkTitle || finding.checkId,\n // The worklist checkId as SAP reports it. On live systems this is a\n // per-instance GUID (stable for the finding, but not a catalog\n // identity); checkTitle is a display name that SAP can rename, so\n // downstream consumers should key on this field rather than the\n // title.\n check_id: finding.checkId || undefined,\n fingerprint,\n severity,\n location: {\n path: filePath,\n lines: {\n begin: fileLine,\n end: fileLine,\n },\n },\n // Extra fields for downstream processing\n ...(methodName && { method: methodName }),\n ...(finding.location && { atc_location: finding.location }),\n ...(finding.checkCategory && {\n check_category: finding.checkCategory,\n }),\n };\n }),\n );\n\n await writeFile(outputFile, JSON.stringify(gitlabReport, null, 2));\n console.log(`\\n📄 GitLab Code Quality report written to: ${outputFile}`);\n console.log(`📊 ${result.totalFindings} issues exported`);\n}\n","/**\n * Check Category Enrichment\n *\n * Resolves ATC findings to their check category (e.g., 'CI_PERFORMANCE',\n * 'CI_SECURITY') through the check variant form template\n * (GET /sap/bc/adt/atc/checkvariants/formtemplate?chkvName={name}).\n *\n * The form template is the check catalog of the run's check variant: every\n * available check category lists its checks, and each check carries its\n * implementing class. When a finding's checkId matches a catalog\n * implementing class, the index resolves it directly. Live worklist\n * checkIds are check-instance GUIDs (observed on a live S/4 system,\n * 2026-10-22); those resolve through the documentation bridge in\n * ./documentation-bridge.ts.\n *\n * Enrichment is best-effort by design: without the form template, or for a\n * checkId outside the catalog, findings keep no category and the run still\n * succeeds.\n */\n\n/**\n * Structural shape of the parsed check variant form template document\n * (GET /sap/bc/adt/atc/checkvariants/formtemplate).\n *\n * Only the fields needed for the checkId -> category index are declared.\n */\nexport interface FormTemplateDocument {\n formTemplate?: {\n availableCheckCategories?: {\n availableCheckCategory?: FormTemplateCategoryEntry[];\n };\n };\n}\n\n/** One available check category with the checks it contains. */\nexport interface FormTemplateCategoryEntry {\n checkCategoryObject?: {\n /** Category name (the enrichment key, e.g., 'CI_PERFORMANCE') */\n name?: string;\n /** Category display title (e.g., 'Performance') */\n description?: string;\n };\n check?: FormTemplateCheckEntry[];\n}\n\n/** One check entry of a category. */\nexport interface FormTemplateCheckEntry {\n checkObject?: {\n /** Check name (e.g., 'CI_ANALYZE_WHERE') */\n name?: string;\n content?: {\n /** The check's category assignment (the category name) */\n category?: { name?: string };\n /**\n * Implementing class of the check (e.g., 'CL_CI_TEST_SELECT_TAW_A').\n * Worklist findings carry a check-instance GUID instead; the\n * documentation bridge resolves that GUID to this class name.\n */\n implementingClass?: { name?: string };\n };\n };\n}\n\n/**\n * Build a checkId -> check category name index from a parsed check variant\n * form template document.\n *\n * The checkId key is the implementing class name; the value is the category\n * object name (a stable key like 'CI_PERFORMANCE', not the display title).\n *\n * @param doc - Parsed form template document (nullable for convenience)\n * @returns Map of checkId -> category name; empty when the document is\n * missing or malformed\n */\nexport function parseCheckCategoryIndex(\n doc: FormTemplateDocument | null | undefined,\n): Map<string, string> {\n const index = new Map<string, string>();\n const categories =\n doc?.formTemplate?.availableCheckCategories?.availableCheckCategory ?? [];\n for (const category of categories) {\n collectCategoryChecks(category, index);\n }\n return index;\n}\n\n/** Index every check of one category under the category's name. */\nfunction collectCategoryChecks(\n category: FormTemplateCategoryEntry,\n index: Map<string, string>,\n): void {\n const categoryName = category.checkCategoryObject?.name;\n if (!categoryName) return;\n const checkIds = (category.check ?? [])\n .map((check) => check.checkObject?.content?.implementingClass?.name)\n .filter((checkId): checkId is string => !!checkId);\n for (const checkId of checkIds) {\n index.set(checkId, categoryName);\n }\n}\n\n/** Structural client surface needed to fetch a form template. */\nexport interface FormTemplateClient {\n adt: {\n atc: {\n checkvariants: {\n /** GET /sap/bc/adt/atc/checkvariants/formtemplate{?chkvName} */\n formtemplate: (params?: {\n chkvName?: string;\n }) => Promise<FormTemplateDocument>;\n };\n };\n };\n}\n\n/**\n * Fetch the check variant form template and build the checkId -> category\n * name index for a run.\n *\n * Best-effort: any error (network, 404, malformed document) is swallowed\n * and reported through the optional logger so a failed enrichment never\n * fails the ATC run itself.\n *\n * @param client - ADT client with the checkvariants.formtemplate surface\n * @param checkVariant - The check variant name of the run (e.g., 'DEFAULT')\n * @param logger - Optional CLI logger for diagnostics\n * @returns checkId -> category name index; empty when unavailable\n */\nexport async function fetchCheckCategoryIndex(\n client: FormTemplateClient,\n checkVariant: string,\n logger?: {\n warn: (message: string) => void;\n info?: (message: string) => void;\n },\n): Promise<Map<string, string>> {\n try {\n const doc = await client.adt.atc.checkvariants.formtemplate({\n chkvName: checkVariant,\n });\n return parseCheckCategoryIndex(doc);\n } catch (error) {\n logger?.warn?.(\n `⚠️ Check category enrichment unavailable: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return new Map();\n }\n}\n\n/**\n * Enrich findings with their check category in place.\n *\n * Generic over any finding shape carrying a checkId (and an optional\n * checkCategory field), so both the CLI plugin's AtcFinding and other\n * canonical finding types can be enriched with the same index.\n *\n * @param findings - Findings to enrich (mutated in place)\n * @param index - checkId -> category name index\n * @returns Number of findings that received a category\n */\nexport function enrichFindingsWithCategories<\n T extends { checkId: string; checkCategory?: string },\n>(findings: T[], index: Map<string, string>): number {\n let enriched = 0;\n for (const finding of findings) {\n const checkCategory = index.get(finding.checkId);\n if (checkCategory) {\n finding.checkCategory = checkCategory;\n enriched++;\n }\n }\n return enriched;\n}\n\n/**\n * Enrich an ATC result with check categories in place.\n *\n * @param result - The run result (findings mutated in place)\n * @param index - checkId -> category name index\n * @returns Number of findings that received a category\n */\nexport function enrichResultWithCategories<\n T extends { checkId: string; checkCategory?: string },\n>(result: { findings?: T[] }, index: Map<string, string>): number {\n return enrichFindingsWithCategories(result.findings ?? [], index);\n}\n","/**\n * Documentation Bridge for Check Category Enrichment\n *\n * ATC worklist findings carry a check-instance GUID as their checkId — not\n * the implementing class name the check variant form template catalog lists\n * (observed on a live S/4 system, 2026-10-22). The finding's documentation\n * page (the worklist atom:link) names the check class in its header line,\n * which resolves through the form-template category index.\n *\n * Everything here is best-effort: fetch errors are reported through the\n * optional logger and skipped; findings keep no category when their page\n * cannot be fetched or parsed.\n */\n\n/** Structural surface for a bounded documentation read. The real ADT client\n * provides readTextBounded; mocks without it simply skip the bridge. */\nexport interface DocumentationReadingClient {\n readTextBounded?: (\n uri: string,\n maxBytes: number,\n options?: { headers?: Record<string, string> },\n ) => Promise<string>;\n}\n\n/**\n * Parse the implementing check class from an ATC finding documentation page.\n *\n * Live shape (2026-10-22): the page opens with\n * \"Appl. Comp. Check / Check Class / Message Code\" followed by\n * `<p>BC-ABA-LA / CL_CI_TEST_SELECT_TAW_A / 0501</p>`. The check class in\n * that line is the implementing class the check variant form template lists\n * — the bridge from the finding's GUID checkId to the check catalog.\n *\n * The match is bounded (no unbounded quantifiers on page input) and accepts\n * an optional ABAP namespace prefix (e.g. `/NSP/CL_EXAMPLE`).\n *\n * @param html - The documentation page HTML\n * @returns The check class name, or undefined when the page does not carry it\n */\nexport function parseCheckClassFromDocumentation(\n html: string,\n): string | undefined {\n const match = html.match(\n /Check\\s*Class[^<]{0,80}<\\/h3>\\s*<p>\\s*[^/<]{0,120}\\/\\s*((?:\\/[A-Za-z0-9_]{1,8}\\/)?[A-Za-z][A-Za-z0-9_]*)\\s*\\//u,\n );\n return match?.[1];\n}\n\n/** Finding shape the bridge enriches in place. */\nexport interface DocumentationBridgeFinding {\n checkId: string;\n checkCategory?: string;\n documentationUri?: string;\n}\n\n/**\n * Only ATC documentation paths may be fetched — a worklist-provided URI\n * must never drive an authenticated read outside the documentation space.\n */\nconst ATC_DOCUMENTATION_URI =\n /^\\/sap\\/bc\\/adt\\/documentation\\/atc\\/documents\\/[\\w/%-]+$/u;\n\n/** Shared state for the per-URI documentation fetch. */\ninterface DocumentationFetchContext {\n read: NonNullable<DocumentationReadingClient['readTextBounded']>;\n maxBytes: number;\n maxReads: number;\n reads: number;\n classByDocumentationUri: Map<string, string | undefined>;\n logger?: { warn: (message: string) => void };\n}\n\n/**\n * Read one documentation page and resolve its check class, with a shared\n * per-URI cache so repeated findings never re-fetch the same page.\n */\nasync function resolveCheckClassForUri(\n documentationUri: string,\n context: DocumentationFetchContext,\n): Promise<void> {\n if (context.classByDocumentationUri.has(documentationUri)) return;\n if (!ATC_DOCUMENTATION_URI.test(documentationUri)) {\n context.logger?.warn?.(\n `⚠️ Skipping non-ATC documentation URI: ${documentationUri}`,\n );\n context.classByDocumentationUri.set(documentationUri, undefined);\n return;\n }\n if (context.reads >= context.maxReads) {\n context.classByDocumentationUri.set(documentationUri, undefined);\n return;\n }\n context.reads++;\n try {\n const html = await context.read(documentationUri, context.maxBytes, {\n headers: { Accept: 'application/vnd.sap.adt.docu.v1+html' },\n });\n context.classByDocumentationUri.set(\n documentationUri,\n parseCheckClassFromDocumentation(html),\n );\n } catch (error) {\n context.logger?.warn?.(\n `⚠️ ATC documentation read failed for ${documentationUri}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n context.classByDocumentationUri.set(documentationUri, undefined);\n }\n}\n\n/**\n * Bridge finding categories through the ATC documentation page.\n *\n * After the direct form-template index pass (which resolves findings whose\n * checkId already matches a catalog implementing class), this resolves the\n * GUID-checkId findings: each finding's documentation page names the check\n * class, which resolves through the same index.\n *\n * Best-effort: per-documentation-URI fetch errors are reported through the\n * optional logger and skipped; a finding keeps no category when its page\n * cannot be fetched or parsed. Reads are deduplicated per URI.\n *\n * @param client - ADT client with an optional bounded text read\n * @param findings - Findings to enrich in place\n * @param classToCategory - checkId/class -> category name index (the\n * form-template catalog: parseCheckCategoryIndex output)\n * @param options - maxBytes (default 64 KiB) and an optional logger\n * @returns Number of findings that received a category\n */\nexport async function resolveCategoriesViaDocumentation(\n client: DocumentationReadingClient,\n findings: DocumentationBridgeFinding[],\n classToCategory: ReadonlyMap<string, string>,\n options: {\n maxBytes?: number;\n maxReads?: number;\n logger?: { warn: (message: string) => void };\n } = {},\n): Promise<number> {\n const read = client.readTextBounded;\n if (!read || classToCategory.size === 0) return 0;\n\n const pending = findings.filter(\n (finding) => !finding.checkCategory && finding.documentationUri,\n );\n if (pending.length === 0) return 0;\n\n const context: DocumentationFetchContext = {\n read,\n maxBytes: options.maxBytes ?? 64 * 1024,\n maxReads: options.maxReads ?? 500,\n reads: 0,\n classByDocumentationUri: new Map<string, string | undefined>(),\n logger: options.logger,\n };\n let enriched = 0;\n for (const finding of pending) {\n await resolveCheckClassForUri(finding.documentationUri!, context);\n const checkClass = context.classByDocumentationUri.get(\n finding.documentationUri!,\n );\n const checkCategory = checkClass\n ? classToCategory.get(checkClass)\n : undefined;\n if (checkCategory) {\n finding.checkCategory = checkCategory;\n enriched++;\n }\n }\n return enriched;\n}\n","/**\n * ATC Command Plugin\n *\n * CLI-agnostic command for running ABAP Test Cockpit checks.\n * Uses the CliContext.getAdtClient() factory for API access.\n */\n\nimport type { CliCommandPlugin, CliContext } from '@abapify/adt-plugin';\nimport chalk from 'chalk';\nimport { outputSarifReport, outputGitLabCodeQuality } from '../formatters';\nimport {\n fetchCheckCategoryIndex,\n enrichResultWithCategories,\n} from '../check-categories';\nimport { resolveCategoriesViaDocumentation } from '../documentation-bridge';\nimport type { AtcResult, AtcFinding, OutputFormat } from '../types';\n\n// Client type - the plugin receives a typed client from context\n// The client.adt.atc namespace provides all ATC operations\ninterface AdtClient {\n adt: {\n atc: {\n customizing: {\n get: () => Promise<{\n customizing: {\n properties: { property?: Array<{ name: string; value?: string }> };\n };\n }>;\n };\n worklists: {\n create: (params: {\n checkVariant: string;\n }) => Promise<\n | string\n | { worklistRun?: { worklistId: string }; worklist?: { id: string } }\n >;\n get: (\n id: string,\n params: { includeExemptedFindings: string },\n ) => Promise<WorklistResponse>;\n };\n checkvariants: {\n formtemplate: (params?: {\n chkvName?: string;\n }) => Promise<import('../check-categories').FormTemplateDocument>;\n };\n runs: {\n post: (\n params: { worklistId: string },\n body: {\n run: {\n maximumVerdicts: number;\n objectSets: {\n objectSet: Array<{\n kind: string;\n objectReferences: {\n objectReference: Array<{ uri: string }>;\n };\n }>;\n };\n };\n },\n ) => Promise<void>;\n };\n };\n };\n /**\n * Bounded documentation text read (the real ADT client provides it).\n * Optional: without it the documentation category bridge is skipped.\n */\n readTextBounded?: (\n uri: string,\n maxBytes: number,\n options?: { headers?: Record<string, string> },\n ) => Promise<string>;\n}\n\n// Worklist response type\ninterface WorklistResponse {\n worklist?: {\n id: string;\n timestamp: string;\n objects: {\n object?: Array<{\n uri?: string;\n type?: string;\n name?: string;\n findings: {\n finding?: Array<{\n uri?: string;\n location?: string;\n priority?: string;\n checkId?: string;\n checkTitle?: string;\n messageId?: string;\n messageTitle?: string;\n /** Documentation link: the page that names the check class. */\n link?: Array<{ href?: string; rel?: string }>;\n }>;\n };\n }>;\n };\n };\n worklistRun?: { worklistId: string };\n}\n\n/**\n * Create OSC 8 hyperlink for terminal\n */\nfunction hyperlink(text: string, url: string): string {\n const OSC = '\\x1b]';\n const BEL = '\\x07';\n const SEP = ';';\n return `${OSC}8${SEP}${SEP}${url}${BEL}${text}${OSC}8${SEP}${SEP}${BEL}`;\n}\n\n/**\n * Create ADT Eclipse link\n */\nfunction adtLink(name: string, uri: string, systemName?: string): string {\n if (!systemName || !name || !uri) {\n return name ? chalk.cyan(name) : '';\n }\n const path = uri.startsWith('/sap/bc/adt') ? uri : `/sap/bc/adt${uri}`;\n const url = `adt://${systemName}${path}`;\n return hyperlink(chalk.cyan(name), url);\n}\n\n/**\n * The trusted documentation link of one worklist finding.\n *\n * Live shape (2026-10-22): <atom:link rel=\"http://www.sap.com/adt/\n * relations/documentation\" href=\"/sap/bc/adt/documentation/atc/documents/\n * itemid/{guid}/index/{n}\"/> inside the atcfinding:finding element.\n */\nfunction documentationUriFromFinding(\n finding: NonNullable<\n NonNullable<\n NonNullable<WorklistResponse['worklist']>['objects']['object']\n >[number]['findings']['finding']\n >[number],\n): string | undefined {\n return finding.link?.find(\n (link) => link.rel === 'http://www.sap.com/adt/relations/documentation',\n )?.href;\n}\n\n/**\n * Convert worklist response to AtcResult\n */\nfunction convertWorklistToResult(\n worklistResponse: WorklistResponse,\n checkVariant: string,\n): AtcResult {\n const findings: AtcFinding[] = [];\n\n if (worklistResponse.worklist) {\n const objects = worklistResponse.worklist.objects.object || [];\n\n for (const obj of objects) {\n const objFindings = obj.findings.finding || [];\n for (const finding of objFindings) {\n findings.push({\n checkId: finding.checkId || '',\n checkTitle: finding.checkTitle || '',\n messageId: finding.messageId || '',\n priority: Number.parseInt(finding.priority || '3', 10),\n messageText: finding.messageTitle || '',\n objectUri: obj.uri || '',\n objectType: obj.type || '',\n objectName: obj.name || '',\n location: finding.location,\n findingUri: finding.uri,\n documentationUri: documentationUriFromFinding(finding),\n });\n }\n }\n }\n\n const errorCount = findings.filter((f) => f.priority === 1).length;\n const warningCount = findings.filter((f) => f.priority === 2).length;\n const infoCount = findings.filter((f) => f.priority >= 3).length;\n\n return {\n checkVariant,\n totalFindings: findings.length,\n errorCount,\n warningCount,\n infoCount,\n findings,\n };\n}\n\n/**\n * Display ATC results in console\n */\nfunction displayAtcResults(result: AtcResult, systemName?: string): void {\n if (result.totalFindings === 0) {\n console.log(`\\n✅ ATC check passed - No issues found!`);\n console.log(` 🎯 Variant: ${result.checkVariant}`);\n return;\n }\n\n console.log(`\\n📊 ATC Results Summary:`);\n console.log(` 🎯 Variant: ${result.checkVariant}`);\n if (result.errorCount > 0) console.log(` ❌ Errors: ${result.errorCount}`);\n if (result.warningCount > 0)\n console.log(` ⚠️ Warnings: ${result.warningCount}`);\n if (result.infoCount > 0) console.log(` ℹ️ Info: ${result.infoCount}`);\n console.log(` 📋 Total Issues: ${result.totalFindings}`);\n\n if (result.findings && result.findings.length > 0) {\n console.log(`\\n📄 Findings:`);\n result.findings.slice(0, 5).forEach((finding) => {\n const priorityIcon =\n finding.priority === 1 ? '❌' : finding.priority === 2 ? '⚠️' : 'ℹ️';\n const objectLink = finding.location\n ? adtLink(finding.objectName, finding.location, systemName)\n : finding.objectName;\n console.log(\n ` ${priorityIcon} ${finding.checkTitle || finding.checkId}`,\n );\n console.log(` ${finding.messageText}`);\n console.log(` Object: ${objectLink} (${finding.objectType})`);\n });\n\n if (result.findings.length > 5) {\n console.log(` ... (${result.findings.length - 5} more findings)`);\n }\n }\n\n console.log(`\\n💡 Use --debug for detailed results`);\n}\n\n/**\n * ATC Command Plugin\n */\nexport const atcCommand: CliCommandPlugin = {\n name: 'atc',\n description: 'Run ABAP Test Cockpit (ATC) checks',\n\n options: [\n { flags: '-p, --package <package>', description: 'Run ATC on package' },\n {\n flags: '-o, --object <uri>',\n description:\n 'Run ATC on specific object (e.g., /sap/bc/adt/oo/classes/zcl_my_class)',\n },\n {\n flags: '-t, --transport <transport>',\n description: 'Run ATC on transport request (e.g., TRLK942970)',\n },\n {\n flags: '-f, --from-file <file>',\n description: 'Run ATC on objects listed in file (one URI per line)',\n },\n {\n flags: '--variant <variant>',\n description: 'ATC check variant (default: from system customizing)',\n },\n {\n flags: '--max-results <number>',\n description: 'Maximum number of results',\n default: '100',\n },\n {\n flags: '--format <format>',\n description: 'Output format: console, json, gitlab, sarif',\n default: 'console',\n },\n {\n flags: '--output <file>',\n description: 'Output file (required for gitlab/sarif format)',\n },\n {\n flags: '--resolver <name>',\n description:\n 'Finding resolver plugin for path/line resolution (e.g., abapgit)',\n },\n ],\n\n async execute(args, ctx: CliContext) {\n const options = args as {\n package?: string;\n object?: string;\n transport?: string;\n fromFile?: string;\n variant?: string;\n maxResults?: string;\n format?: OutputFormat;\n output?: string;\n resolver?: string;\n };\n\n // Validate mutually exclusive options\n const targetCount = [\n options.package,\n options.object,\n options.transport,\n options.fromFile,\n ].filter(Boolean).length;\n\n if (targetCount === 0) {\n ctx.logger.error(\n '❌ One of --package, --object, --transport, or --from-file is required',\n );\n process.exit(1);\n }\n\n if (targetCount > 1) {\n ctx.logger.error(\n '❌ Only one of --package, --object, --transport, or --from-file can be specified',\n );\n process.exit(1);\n }\n\n // Validate output file for gitlab/sarif formats\n if (\n (options.format === 'gitlab' || options.format === 'sarif') &&\n !options.output\n ) {\n ctx.logger.error(`❌ --output is required for ${options.format} format`);\n process.exit(1);\n }\n\n // Get ADT client from context\n if (!ctx.getAdtClient) {\n ctx.logger.error(\n '❌ ADT client not available. This command requires authentication.',\n );\n ctx.logger.error(' Run: adt auth login');\n process.exit(1);\n }\n\n const client = (await ctx.getAdtClient()) as AdtClient;\n\n ctx.logger.info('🔍 Running ABAP Test Cockpit checks...');\n\n // Determine target(s)\n let targetUris: string[];\n let targetName: string;\n\n if (options.fromFile) {\n // Read URIs from file\n const { readFileSync } = await import('fs');\n const fileContent = readFileSync(options.fromFile, 'utf-8');\n targetUris = fileContent\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line && !line.startsWith('#')); // Skip empty lines and comments\n\n if (targetUris.length === 0) {\n ctx.logger.error(`❌ No objects found in ${options.fromFile}`);\n process.exit(1);\n }\n targetName = `${targetUris.length} objects from ${options.fromFile}`;\n ctx.logger.info(`📄 Target: ${targetName}`);\n } else if (options.transport) {\n targetUris = [`/sap/bc/adt/cts/transportrequests/${options.transport}`];\n targetName = `Transport ${options.transport}`;\n ctx.logger.info(`🚚 Target: ${targetName}`);\n } else if (options.package) {\n targetUris = [`/sap/bc/adt/packages/${options.package.toUpperCase()}`];\n targetName = `Package ${options.package.toUpperCase()}`;\n ctx.logger.info(`📦 Target: ${targetName}`);\n } else {\n targetUris = [options.object!];\n targetName = options.object!;\n ctx.logger.info(`📄 Target: ${targetName}`);\n }\n\n // Step 1: Get ATC customizing to find default check variant\n ctx.logger.info('📋 Reading ATC customizing...');\n const customizing = await client.adt.atc.customizing.get();\n\n // Find systemCheckVariant in properties\n let checkVariant = options.variant;\n if (!checkVariant) {\n const variantProp = customizing.customizing.properties.property?.find(\n (p) => p.name === 'systemCheckVariant',\n );\n if (variantProp?.value) {\n checkVariant = variantProp.value;\n } else {\n checkVariant = 'DEFAULT';\n }\n }\n ctx.logger.info(`🎯 Check variant: ${checkVariant}`);\n\n // Step 2: Create worklist (just with checkVariant, no objects)\n ctx.logger.info('📋 Creating ATC worklist...');\n const worklistCreateResponse = await client.adt.atc.worklists.create({\n checkVariant,\n });\n\n // Extract worklist ID - SAP can return plain string or object\n let worklistId: string;\n if (typeof worklistCreateResponse === 'string') {\n // Plain string response - extract ID from XML or use as-is\n const idMatch = worklistCreateResponse.match(/id=\"([^\"]+)\"/);\n worklistId = idMatch ? idMatch[1] : worklistCreateResponse.trim();\n } else if (\n 'worklist' in worklistCreateResponse &&\n worklistCreateResponse.worklist?.id\n ) {\n worklistId = worklistCreateResponse.worklist.id;\n } else if (\n 'worklistRun' in worklistCreateResponse &&\n worklistCreateResponse.worklistRun?.worklistId\n ) {\n worklistId = worklistCreateResponse.worklistRun.worklistId;\n } else {\n ctx.logger.error('❌ Failed to get worklist ID from response');\n process.exit(1);\n }\n ctx.logger.info(`📋 Worklist created: ${worklistId}`);\n\n // Step 3: Run ATC checks with objects\n ctx.logger.info('⏳ Running ATC analysis...');\n const maxResults = Number.parseInt(options.maxResults || '100', 10);\n const runData = {\n run: {\n maximumVerdicts: maxResults,\n objectSets: {\n objectSet: [\n {\n kind: 'inclusive',\n objectReferences: {\n objectReference: targetUris.map((uri) => ({ uri })),\n },\n },\n ],\n },\n },\n };\n await client.adt.atc.runs.post({ worklistId }, runData);\n\n // Step 4: Get results\n ctx.logger.info('📊 Fetching results...');\n const worklistResult = await client.adt.atc.worklists.get(worklistId, {\n includeExemptedFindings: 'false',\n });\n\n // Convert to AtcResult\n const result = convertWorklistToResult(\n worklistResult as WorklistResponse,\n checkVariant,\n );\n\n // Enrich findings with check categories from the run's check variant\n // form template (best-effort: never fails the run itself). The catalog is\n // fetched after the run, so categories reflect the variant configuration\n // in effect at that time.\n const categoryIndex = await fetchCheckCategoryIndex(\n client,\n checkVariant,\n ctx.logger,\n );\n const enriched = enrichResultWithCategories(result, categoryIndex);\n if (enriched > 0) {\n ctx.logger.info(\n `🏷️ Enriched ${enriched}/${result.totalFindings} finding(s) with check categories`,\n );\n }\n\n // Bridge remaining findings through their ATC documentation page: the\n // worklist checkId is a check-instance GUID, not the implementing class\n // the form-template catalog lists (live evidence, 2026-10-22), so\n // the direct index match cannot resolve them. The documentation page\n // names the check class, which resolves through the same index.\n // Best-effort: never fails the run.\n const bridged = await resolveCategoriesViaDocumentation(\n client,\n result.findings,\n categoryIndex,\n { logger: ctx.logger },\n );\n if (bridged > 0) {\n ctx.logger.info(\n `🏷 Bridged ${bridged} finding(s) to check categories via ATC documentation`,\n );\n }\n\n // Load finding resolver if requested\n let resolver: import('../types').FindingResolver | undefined;\n if (options.resolver) {\n const name = options.resolver;\n if (name === 'abapgit') {\n // Built-in abapgit resolver — no external dependencies\n const { createAbapGitResolver } = await import('../resolvers/abapgit');\n resolver = createAbapGitResolver();\n } else {\n // External resolver — try dynamic import\n try {\n ctx.logger.info(`📂 Loading external resolver: ${name}`);\n const mod = await import(name);\n if (typeof mod.createFindingResolver === 'function') {\n resolver = mod.createFindingResolver();\n } else {\n ctx.logger.warn(\n `⚠️ Module ${name} does not export createFindingResolver()`,\n );\n }\n } catch (e) {\n ctx.logger.warn(\n `⚠️ Failed to load resolver \"${name}\": ${e instanceof Error ? e.message : e}`,\n );\n }\n }\n }\n\n // Output based on format\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2));\n } else if (options.format === 'gitlab' && options.output) {\n await outputGitLabCodeQuality(result, options.output, { resolver });\n } else if (options.format === 'sarif' && options.output) {\n await outputSarifReport(result, options.output, targetName);\n } else {\n displayAtcResults(result, ctx.adtSystemName);\n }\n\n // Exit with error code if there are errors\n if (result.errorCount > 0) {\n process.exit(1);\n }\n },\n};\n\nexport default atcCommand;\n"],"mappings":"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"}
|
package/dist/commands/atc.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { t as atcCommand, t as default } from "../atc-
|
|
1
|
+
export { t as atcCommand, t as default } from "../atc-CIPiGB5o.mjs";
|
package/dist/index.d.mts
CHANGED
|
@@ -296,6 +296,7 @@ interface DocumentationBridgeFinding {
|
|
|
296
296
|
*/
|
|
297
297
|
declare function resolveCategoriesViaDocumentation(client: DocumentationReadingClient, findings: DocumentationBridgeFinding[], classToCategory: ReadonlyMap<string, string>, options?: {
|
|
298
298
|
maxBytes?: number;
|
|
299
|
+
maxReads?: number;
|
|
299
300
|
logger?: {
|
|
300
301
|
warn: (message: string) => void;
|
|
301
302
|
};
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/commands/atc-customizing.ts","../src/types.ts","../src/formatters/sarif.ts","../src/formatters/gitlab.ts","../src/check-categories.ts","../src/documentation-bridge.ts","../src/resolvers/abapgit.ts","../src/resolvers/adt-uri-to-abapgit-path.ts"],"mappings":";;cAiCa,uBAAuB;;;;;;;;;UCxBnB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;EAOA;;;;;;;EAOA;;;;;UAMe;EACf;EACA;EACA;EACA;EACA;EACA,UAAU;;;;;KAMA;;;;UAKK;;EAEf;;EAEA;;;;;;;;;;;;;;;;;;;;;UAsBe;;;;;;;;;;;EAWf,QACE,oBACA,oBACA,iBACA,qBACA,uBACC,QAAQ;;iBC1FS,kBACpB,QAAQ,WACR,oBACA,uBACC;;;;UCmBc;;EAEf,WAAW;;iBAsBS,wBACpB,QAAQ,WACR,oBACA,UAAU,yBACT;;;;;;;;;;;;;;;;;;;;;;;;;;UClCc;EACf;IACE;MACE,yBAAyB;;;;;UAMd;EACf;;IAEE;;IAEA;;EAEF,QAAQ;;;UAIO;EACf;;IAEE;IACA;;MAEE;QAAa;;;;;;;MAMb;QAAsB;;;;;;;;;;;;;;;;iBAgBZ,wBACd,KAAK,0CACJ;;UA0Bc;EACf;IACE;MACE;;QAEE,eAAe;UACb;cACI,QAAQ;;;;;;;;;;;;;;;;;;iBAmBA,wBACpB,QAAQ,oBACR,sBACA;EACE,OAAO;EACP,QAAQ;IAET,QAAQ;;;;;;;;;;;;iBA2BK,6BACd;EAAY;EAAiB;GAC7B,UAAU,KAAK,OAAO;;;;;;;;iBAmBR,2BACd;EAAY;EAAiB;GAC7B;EAAU,WAAW;GAAO,OAAO;;;;;;;;;;;;;;;;UCzKpB;EACf,mBACE,aACA,kBACA;IAAY,UAAU;QACnB;;;;;;;;;;;;;;;;;iBAkBS,iCACd;;UASe;EACf;EACA;EACA;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/commands/atc-customizing.ts","../src/types.ts","../src/formatters/sarif.ts","../src/formatters/gitlab.ts","../src/check-categories.ts","../src/documentation-bridge.ts","../src/resolvers/abapgit.ts","../src/resolvers/adt-uri-to-abapgit-path.ts"],"mappings":";;cAiCa,uBAAuB;;;;;;;;;UCxBnB;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;EAOA;;;;;;;EAOA;;;;;UAMe;EACf;EACA;EACA;EACA;EACA;EACA,UAAU;;;;;KAMA;;;;UAKK;;EAEf;;EAEA;;;;;;;;;;;;;;;;;;;;;UAsBe;;;;;;;;;;;EAWf,QACE,oBACA,oBACA,iBACA,qBACA,uBACC,QAAQ;;iBC1FS,kBACpB,QAAQ,WACR,oBACA,uBACC;;;;UCmBc;;EAEf,WAAW;;iBAsBS,wBACpB,QAAQ,WACR,oBACA,UAAU,yBACT;;;;;;;;;;;;;;;;;;;;;;;;;;UClCc;EACf;IACE;MACE,yBAAyB;;;;;UAMd;EACf;;IAEE;;IAEA;;EAEF,QAAQ;;;UAIO;EACf;;IAEE;IACA;;MAEE;QAAa;;;;;;;MAMb;QAAsB;;;;;;;;;;;;;;;;iBAgBZ,wBACd,KAAK,0CACJ;;UA0Bc;EACf;IACE;MACE;;QAEE,eAAe;UACb;cACI,QAAQ;;;;;;;;;;;;;;;;;;iBAmBA,wBACpB,QAAQ,oBACR,sBACA;EACE,OAAO;EACP,QAAQ;IAET,QAAQ;;;;;;;;;;;;iBA2BK,6BACd;EAAY;EAAiB;GAC7B,UAAU,KAAK,OAAO;;;;;;;;iBAmBR,2BACd;EAAY;EAAiB;GAC7B;EAAU,WAAW;GAAO,OAAO;;;;;;;;;;;;;;;;UCzKpB;EACf,mBACE,aACA,kBACA;IAAY,UAAU;QACnB;;;;;;;;;;;;;;;;;iBAkBS,iCACd;;UASe;EACf;EACA;EACA;;;;;;;;;;;;;;;;;;;;;iBA8EoB,kCACpB,QAAQ,4BACR,UAAU,8BACV,iBAAiB,6BACjB;EACE;EACA;EACA;IAAW,OAAO;;IAEnB;;;;;;;;;iBCoJa,sBAAsB,mBAAmB;;;;;;;;;;;;;;iBCzHzC,oBAAoB"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as createAbapGitResolver, r as adtUriToAbapGitPath } from "./abapgit-B2E5I-u0.mjs";
|
|
2
|
-
import { a as enrichResultWithCategories, c as outputGitLabCodeQuality, i as enrichFindingsWithCategories, l as outputSarifReport, n as parseCheckClassFromDocumentation, o as fetchCheckCategoryIndex, r as resolveCategoriesViaDocumentation, s as parseCheckCategoryIndex, t as atcCommand } from "./atc-
|
|
2
|
+
import { a as enrichResultWithCategories, c as outputGitLabCodeQuality, i as enrichFindingsWithCategories, l as outputSarifReport, n as parseCheckClassFromDocumentation, o as fetchCheckCategoryIndex, r as resolveCategoriesViaDocumentation, s as parseCheckCategoryIndex, t as atcCommand } from "./atc-CIPiGB5o.mjs";
|
|
3
3
|
// src/commands/atc-customizing.ts
|
|
4
4
|
const atcCustomizingCommand = {
|
|
5
5
|
name: "atc-customizing",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abapify/adt-atc",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.29",
|
|
4
4
|
"description": "ABAP Test Cockpit (ATC) CLI plugin for adt-cli",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"gitlab"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@abapify/adt-plugin": "0.4.
|
|
31
|
+
"@abapify/adt-plugin": "0.4.29",
|
|
32
32
|
"chalk": "^5.3.0"
|
|
33
33
|
},
|
|
34
34
|
"module": "./dist/index.mjs",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"atc-6cFtFkir.mjs","names":[],"sources":["../src/formatters/sarif.ts","../src/formatters/gitlab.ts","../src/check-categories.ts","../src/documentation-bridge.ts","../src/commands/atc.ts"],"sourcesContent":["/**\n * SARIF Formatter\n *\n * Outputs ATC findings in SARIF format for GitHub Code Scanning.\n */\n\nimport { writeFile } from 'node:fs/promises';\nimport type { AtcResult, AtcFinding } from '../types';\n\nexport async function outputSarifReport(\n result: AtcResult,\n outputFile: string,\n _targetName?: string,\n): Promise<void> {\n // Transform ATC findings to SARIF format\n const rules = Array.from(\n new Map(\n result.findings.map((f) => [\n f.checkId,\n {\n id: f.checkId,\n name: f.checkTitle || f.checkId,\n shortDescription: { text: f.checkTitle || f.checkId },\n fullDescription: { text: f.checkTitle || f.checkId },\n defaultConfiguration: {\n level:\n f.priority === 1\n ? 'error'\n : f.priority === 2\n ? 'warning'\n : 'note',\n },\n properties: {\n tags: ['abap', 'code-quality'],\n precision: 'high',\n ...(f.checkCategory && { category: f.checkCategory }),\n },\n },\n ]),\n ).values(),\n );\n\n const results = result.findings.map((finding: AtcFinding) => {\n const filePath = `src/${finding.objectType.toLowerCase()}/${finding.objectName.toLowerCase()}.${finding.objectType.toLowerCase()}`;\n\n // Parse line number from location if available\n const lineMatch = finding.location?.match(/start=(\\d+)/);\n const line = lineMatch ? Number.parseInt(lineMatch[1], 10) : 1;\n\n return {\n ruleId: finding.checkId,\n level:\n finding.priority === 1\n ? 'error'\n : finding.priority === 2\n ? 'warning'\n : 'note',\n message: { text: finding.messageText },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: filePath },\n region: {\n startLine: line,\n startColumn: 1,\n },\n },\n },\n ],\n partialFingerprints: {\n primaryLocationLineHash: `${finding.checkId}-${finding.objectName}-${line}`,\n },\n };\n });\n\n const sarifReport = {\n $schema: 'https://json.schemastore.org/sarif-2.1.0.json',\n version: '2.1.0',\n runs: [\n {\n tool: {\n driver: {\n name: 'ABAP Test Cockpit',\n version: '1.0.0',\n informationUri:\n 'https://help.sap.com/docs/ABAP_PLATFORM_NEW/c238d694b825421f940829321ffa326a/4ec5711c6e391014adc9fffe4e204223.html',\n rules: Array.from(rules),\n },\n },\n results,\n },\n ],\n };\n\n await writeFile(outputFile, JSON.stringify(sarifReport, null, 2));\n console.log(`\\n📄 SARIF report written to: ${outputFile}`);\n console.log(\n `📊 ${result.totalFindings} issues exported for GitHub Code Scanning`,\n );\n}\n","/**\n * GitLab Code Quality Formatter\n *\n * Outputs ATC findings in GitLab Code Quality format.\n *\n * This formatter is a pure output formatter — it maps ATC findings to the\n * GitLab Code Quality JSON structure. Path resolution and line number\n * conversion are delegated to an optional FindingResolver.\n *\n * Without a resolver, the formatter produces PREFIX-style paths and raw\n * ATC (method-relative) line numbers. With a resolver (e.g., from\n * @abapify/adt-plugin-abapgit), it produces correct git paths and\n * file-relative line numbers.\n *\n * @example\n * ```typescript\n * // Without resolver (standalone CLI usage)\n * await outputGitLabCodeQuality(result, 'report.json');\n *\n * // With resolver (inside an abapgit repo)\n * import { createFindingResolver } from '@abapify/adt-plugin-abapgit';\n * const resolver = await createFindingResolver();\n * await outputGitLabCodeQuality(result, 'report.json', { resolver });\n * ```\n */\n\nimport { writeFile } from 'node:fs/promises';\nimport type { AtcResult, AtcFinding, FindingResolver } from '../types';\n\n/**\n * Options for the GitLab Code Quality formatter\n */\nexport interface GitLabFormatterOptions {\n /** Optional resolver for path/line resolution (e.g., from abapgit plugin) */\n resolver?: FindingResolver;\n}\n\n// ── Extract method name from ATC location URI ───────────────────────────\n// Format 1: /sap/bc/adt/oo/classes/zcl_foo/methods/my_method#start=21,0\n// Format 2: ...#type=CLAS%2FOM;name=MY_METHOD;start=21\n\nfunction extractMethodName(location?: string): string | undefined {\n if (!location) return undefined;\n const match =\n location.match(/\\/methods\\/(\\w+)/i) || location.match(/[;?&]name=(\\w+)/i);\n return match ? match[1].toLowerCase() : undefined;\n}\n\n// ── Construct PREFIX-style fallback path ─────────────────────────────────\n\nfunction prefixPath(finding: AtcFinding): string {\n return `src/${finding.objectType.toLowerCase()}/${finding.objectName.toLowerCase()}.${finding.objectType.toLowerCase()}.abap`;\n}\n\n// ── Main formatter ──────────────────────────────────────────────────────\n\nexport async function outputGitLabCodeQuality(\n result: AtcResult,\n outputFile: string,\n options?: GitLabFormatterOptions,\n): Promise<void> {\n const resolver = options?.resolver;\n\n // Transform ATC findings to GitLab Code Quality format\n const gitlabReport = await Promise.all(\n result.findings.map(async (finding: AtcFinding) => {\n // Map ATC priority to GitLab severity\n let severity: string;\n switch (finding.priority) {\n case 1:\n severity = 'blocker';\n break;\n case 2:\n severity = 'major';\n break;\n case 3:\n severity = 'minor';\n break;\n default:\n severity = 'info';\n break;\n }\n\n // Parse ATC line number from location\n const lineMatch = finding.location?.match(/start=(\\d+)/);\n const atcLine = lineMatch ? Number.parseInt(lineMatch[1], 10) : 1;\n\n // Extract method name from ATC location URI\n const methodName = extractMethodName(finding.location);\n\n // Resolve path and line via plugin, or fall back to PREFIX-style\n let filePath: string;\n let fileLine: number;\n\n if (resolver) {\n const resolved = await resolver.resolve(\n finding.objectType,\n finding.objectName,\n atcLine,\n methodName,\n finding.location,\n );\n if (resolved) {\n filePath = resolved.path;\n fileLine = resolved.line;\n } else {\n filePath = prefixPath(finding);\n fileLine = atcLine;\n }\n } else {\n filePath = prefixPath(finding);\n fileLine = atcLine;\n }\n\n // Create unique fingerprint\n const fingerprint = `${finding.checkId}-${finding.objectName}-${fileLine}`;\n\n return {\n description: finding.messageText,\n check_name: finding.checkTitle || finding.checkId,\n // The worklist checkId as SAP reports it. On live systems this is a\n // per-instance GUID (stable for the finding, but not a catalog\n // identity); checkTitle is a display name that SAP can rename, so\n // downstream consumers should key on this field rather than the\n // title.\n check_id: finding.checkId || undefined,\n fingerprint,\n severity,\n location: {\n path: filePath,\n lines: {\n begin: fileLine,\n end: fileLine,\n },\n },\n // Extra fields for downstream processing\n ...(methodName && { method: methodName }),\n ...(finding.location && { atc_location: finding.location }),\n ...(finding.checkCategory && {\n check_category: finding.checkCategory,\n }),\n };\n }),\n );\n\n await writeFile(outputFile, JSON.stringify(gitlabReport, null, 2));\n console.log(`\\n📄 GitLab Code Quality report written to: ${outputFile}`);\n console.log(`📊 ${result.totalFindings} issues exported`);\n}\n","/**\n * Check Category Enrichment\n *\n * Resolves ATC findings to their check category (e.g., 'CI_PERFORMANCE',\n * 'CI_SECURITY') through the check variant form template\n * (GET /sap/bc/adt/atc/checkvariants/formtemplate?chkvName={name}).\n *\n * The form template is the check catalog of the run's check variant: every\n * available check category lists its checks, and each check carries its\n * implementing class. When a finding's checkId matches a catalog\n * implementing class, the index resolves it directly. Live worklist\n * checkIds are check-instance GUIDs (observed on a live S/4 system,\n * 2026-10-22); those resolve through the documentation bridge in\n * ./documentation-bridge.ts.\n *\n * Enrichment is best-effort by design: without the form template, or for a\n * checkId outside the catalog, findings keep no category and the run still\n * succeeds.\n */\n\n/**\n * Structural shape of the parsed check variant form template document\n * (GET /sap/bc/adt/atc/checkvariants/formtemplate).\n *\n * Only the fields needed for the checkId -> category index are declared.\n */\nexport interface FormTemplateDocument {\n formTemplate?: {\n availableCheckCategories?: {\n availableCheckCategory?: FormTemplateCategoryEntry[];\n };\n };\n}\n\n/** One available check category with the checks it contains. */\nexport interface FormTemplateCategoryEntry {\n checkCategoryObject?: {\n /** Category name (the enrichment key, e.g., 'CI_PERFORMANCE') */\n name?: string;\n /** Category display title (e.g., 'Performance') */\n description?: string;\n };\n check?: FormTemplateCheckEntry[];\n}\n\n/** One check entry of a category. */\nexport interface FormTemplateCheckEntry {\n checkObject?: {\n /** Check name (e.g., 'CI_ANALYZE_WHERE') */\n name?: string;\n content?: {\n /** The check's category assignment (the category name) */\n category?: { name?: string };\n /**\n * Implementing class of the check (e.g., 'CL_CI_TEST_SELECT_TAW_A').\n * Worklist findings carry a check-instance GUID instead; the\n * documentation bridge resolves that GUID to this class name.\n */\n implementingClass?: { name?: string };\n };\n };\n}\n\n/**\n * Build a checkId -> check category name index from a parsed check variant\n * form template document.\n *\n * The checkId key is the implementing class name; the value is the category\n * object name (a stable key like 'CI_PERFORMANCE', not the display title).\n *\n * @param doc - Parsed form template document (nullable for convenience)\n * @returns Map of checkId -> category name; empty when the document is\n * missing or malformed\n */\nexport function parseCheckCategoryIndex(\n doc: FormTemplateDocument | null | undefined,\n): Map<string, string> {\n const index = new Map<string, string>();\n const categories =\n doc?.formTemplate?.availableCheckCategories?.availableCheckCategory ?? [];\n for (const category of categories) {\n collectCategoryChecks(category, index);\n }\n return index;\n}\n\n/** Index every check of one category under the category's name. */\nfunction collectCategoryChecks(\n category: FormTemplateCategoryEntry,\n index: Map<string, string>,\n): void {\n const categoryName = category.checkCategoryObject?.name;\n if (!categoryName) return;\n const checkIds = (category.check ?? [])\n .map((check) => check.checkObject?.content?.implementingClass?.name)\n .filter((checkId): checkId is string => !!checkId);\n for (const checkId of checkIds) {\n index.set(checkId, categoryName);\n }\n}\n\n/** Structural client surface needed to fetch a form template. */\nexport interface FormTemplateClient {\n adt: {\n atc: {\n checkvariants: {\n /** GET /sap/bc/adt/atc/checkvariants/formtemplate{?chkvName} */\n formtemplate: (params?: {\n chkvName?: string;\n }) => Promise<FormTemplateDocument>;\n };\n };\n };\n}\n\n/**\n * Fetch the check variant form template and build the checkId -> category\n * name index for a run.\n *\n * Best-effort: any error (network, 404, malformed document) is swallowed\n * and reported through the optional logger so a failed enrichment never\n * fails the ATC run itself.\n *\n * @param client - ADT client with the checkvariants.formtemplate surface\n * @param checkVariant - The check variant name of the run (e.g., 'DEFAULT')\n * @param logger - Optional CLI logger for diagnostics\n * @returns checkId -> category name index; empty when unavailable\n */\nexport async function fetchCheckCategoryIndex(\n client: FormTemplateClient,\n checkVariant: string,\n logger?: {\n warn: (message: string) => void;\n info?: (message: string) => void;\n },\n): Promise<Map<string, string>> {\n try {\n const doc = await client.adt.atc.checkvariants.formtemplate({\n chkvName: checkVariant,\n });\n return parseCheckCategoryIndex(doc);\n } catch (error) {\n logger?.warn?.(\n `⚠️ Check category enrichment unavailable: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return new Map();\n }\n}\n\n/**\n * Enrich findings with their check category in place.\n *\n * Generic over any finding shape carrying a checkId (and an optional\n * checkCategory field), so both the CLI plugin's AtcFinding and other\n * canonical finding types can be enriched with the same index.\n *\n * @param findings - Findings to enrich (mutated in place)\n * @param index - checkId -> category name index\n * @returns Number of findings that received a category\n */\nexport function enrichFindingsWithCategories<\n T extends { checkId: string; checkCategory?: string },\n>(findings: T[], index: Map<string, string>): number {\n let enriched = 0;\n for (const finding of findings) {\n const checkCategory = index.get(finding.checkId);\n if (checkCategory) {\n finding.checkCategory = checkCategory;\n enriched++;\n }\n }\n return enriched;\n}\n\n/**\n * Enrich an ATC result with check categories in place.\n *\n * @param result - The run result (findings mutated in place)\n * @param index - checkId -> category name index\n * @returns Number of findings that received a category\n */\nexport function enrichResultWithCategories<\n T extends { checkId: string; checkCategory?: string },\n>(result: { findings?: T[] }, index: Map<string, string>): number {\n return enrichFindingsWithCategories(result.findings ?? [], index);\n}\n","/**\n * Documentation Bridge for Check Category Enrichment\n *\n * ATC worklist findings carry a check-instance GUID as their checkId — not\n * the implementing class name the check variant form template catalog lists\n * (observed on a live S/4 system, 2026-10-22). The finding's documentation\n * page (the worklist atom:link) names the check class in its header line,\n * which resolves through the form-template category index.\n *\n * Everything here is best-effort: fetch errors are reported through the\n * optional logger and skipped; findings keep no category when their page\n * cannot be fetched or parsed.\n */\n\n/** Structural surface for a bounded documentation read. The real ADT client\n * provides readTextBounded; mocks without it simply skip the bridge. */\nexport interface DocumentationReadingClient {\n readTextBounded?: (\n uri: string,\n maxBytes: number,\n options?: { headers?: Record<string, string> },\n ) => Promise<string>;\n}\n\n/**\n * Parse the implementing check class from an ATC finding documentation page.\n *\n * Live shape (2026-10-22): the page opens with\n * \"Appl. Comp. Check / Check Class / Message Code\" followed by\n * `<p>BC-ABA-LA / CL_CI_TEST_SELECT_TAW_A / 0501</p>`. The check class in\n * that line is the implementing class the check variant form template lists\n * — the bridge from the finding's GUID checkId to the check catalog.\n *\n * The match is bounded (no unbounded quantifiers on page input) and accepts\n * an optional ABAP namespace prefix (e.g. `/NSP/CL_EXAMPLE`).\n *\n * @param html - The documentation page HTML\n * @returns The check class name, or undefined when the page does not carry it\n */\nexport function parseCheckClassFromDocumentation(\n html: string,\n): string | undefined {\n const match = html.match(\n /Check\\s*Class[^<]{0,80}<\\/h3>\\s*<p>\\s*[^/<]{0,120}\\/\\s*((?:\\/[A-Za-z0-9_]{1,8}\\/)?[A-Za-z][A-Za-z0-9_]*)\\s*\\//u,\n );\n return match?.[1];\n}\n\n/** Finding shape the bridge enriches in place. */\nexport interface DocumentationBridgeFinding {\n checkId: string;\n checkCategory?: string;\n documentationUri?: string;\n}\n\n/** Shared state for the per-URI documentation fetch. */\ninterface DocumentationFetchContext {\n read: NonNullable<DocumentationReadingClient['readTextBounded']>;\n maxBytes: number;\n classByDocumentationUri: Map<string, string | undefined>;\n logger?: { warn: (message: string) => void };\n}\n\n/**\n * Read one documentation page and resolve its check class, with a shared\n * per-URI cache so repeated findings never re-fetch the same page.\n */\nasync function resolveCheckClassForUri(\n documentationUri: string,\n context: DocumentationFetchContext,\n): Promise<void> {\n if (context.classByDocumentationUri.has(documentationUri)) return;\n try {\n const html = await context.read(documentationUri, context.maxBytes, {\n headers: { Accept: 'application/vnd.sap.adt.docu.v1+html' },\n });\n context.classByDocumentationUri.set(\n documentationUri,\n parseCheckClassFromDocumentation(html),\n );\n } catch (error) {\n context.logger?.warn?.(\n `⚠️ ATC documentation read failed for ${documentationUri}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n context.classByDocumentationUri.set(documentationUri, undefined);\n }\n}\n\n/**\n * Bridge finding categories through the ATC documentation page.\n *\n * After the direct form-template index pass (which resolves findings whose\n * checkId already matches a catalog implementing class), this resolves the\n * GUID-checkId findings: each finding's documentation page names the check\n * class, which resolves through the same index.\n *\n * Best-effort: per-documentation-URI fetch errors are reported through the\n * optional logger and skipped; a finding keeps no category when its page\n * cannot be fetched or parsed. Reads are deduplicated per URI.\n *\n * @param client - ADT client with an optional bounded text read\n * @param findings - Findings to enrich in place\n * @param classToCategory - checkId/class -> category name index (the\n * form-template catalog: parseCheckCategoryIndex output)\n * @param options - maxBytes (default 64 KiB) and an optional logger\n * @returns Number of findings that received a category\n */\nexport async function resolveCategoriesViaDocumentation(\n client: DocumentationReadingClient,\n findings: DocumentationBridgeFinding[],\n classToCategory: ReadonlyMap<string, string>,\n options: {\n maxBytes?: number;\n logger?: { warn: (message: string) => void };\n } = {},\n): Promise<number> {\n const read = client.readTextBounded;\n if (!read || classToCategory.size === 0) return 0;\n\n const pending = findings.filter(\n (finding) => !finding.checkCategory && finding.documentationUri,\n );\n if (pending.length === 0) return 0;\n\n const context: DocumentationFetchContext = {\n read,\n maxBytes: options.maxBytes ?? 64 * 1024,\n classByDocumentationUri: new Map<string, string | undefined>(),\n logger: options.logger,\n };\n let enriched = 0;\n for (const finding of pending) {\n await resolveCheckClassForUri(finding.documentationUri!, context);\n const checkClass = context.classByDocumentationUri.get(\n finding.documentationUri!,\n );\n const checkCategory = checkClass\n ? classToCategory.get(checkClass)\n : undefined;\n if (checkCategory) {\n finding.checkCategory = checkCategory;\n enriched++;\n }\n }\n return enriched;\n}\n","/**\n * ATC Command Plugin\n *\n * CLI-agnostic command for running ABAP Test Cockpit checks.\n * Uses the CliContext.getAdtClient() factory for API access.\n */\n\nimport type { CliCommandPlugin, CliContext } from '@abapify/adt-plugin';\nimport chalk from 'chalk';\nimport { outputSarifReport, outputGitLabCodeQuality } from '../formatters';\nimport {\n fetchCheckCategoryIndex,\n enrichResultWithCategories,\n} from '../check-categories';\nimport { resolveCategoriesViaDocumentation } from '../documentation-bridge';\nimport type { AtcResult, AtcFinding, OutputFormat } from '../types';\n\n// Client type - the plugin receives a typed client from context\n// The client.adt.atc namespace provides all ATC operations\ninterface AdtClient {\n adt: {\n atc: {\n customizing: {\n get: () => Promise<{\n customizing: {\n properties: { property?: Array<{ name: string; value?: string }> };\n };\n }>;\n };\n worklists: {\n create: (params: {\n checkVariant: string;\n }) => Promise<\n | string\n | { worklistRun?: { worklistId: string }; worklist?: { id: string } }\n >;\n get: (\n id: string,\n params: { includeExemptedFindings: string },\n ) => Promise<WorklistResponse>;\n };\n checkvariants: {\n formtemplate: (params?: {\n chkvName?: string;\n }) => Promise<import('../check-categories').FormTemplateDocument>;\n };\n runs: {\n post: (\n params: { worklistId: string },\n body: {\n run: {\n maximumVerdicts: number;\n objectSets: {\n objectSet: Array<{\n kind: string;\n objectReferences: {\n objectReference: Array<{ uri: string }>;\n };\n }>;\n };\n };\n },\n ) => Promise<void>;\n };\n };\n };\n /**\n * Bounded documentation text read (the real ADT client provides it).\n * Optional: without it the documentation category bridge is skipped.\n */\n readTextBounded?: (\n uri: string,\n maxBytes: number,\n options?: { headers?: Record<string, string> },\n ) => Promise<string>;\n}\n\n// Worklist response type\ninterface WorklistResponse {\n worklist?: {\n id: string;\n timestamp: string;\n objects: {\n object?: Array<{\n uri?: string;\n type?: string;\n name?: string;\n findings: {\n finding?: Array<{\n uri?: string;\n location?: string;\n priority?: string;\n checkId?: string;\n checkTitle?: string;\n messageId?: string;\n messageTitle?: string;\n /** Documentation link: the page that names the check class. */\n link?: Array<{ href?: string; rel?: string }>;\n }>;\n };\n }>;\n };\n };\n worklistRun?: { worklistId: string };\n}\n\n/**\n * Create OSC 8 hyperlink for terminal\n */\nfunction hyperlink(text: string, url: string): string {\n const OSC = '\\x1b]';\n const BEL = '\\x07';\n const SEP = ';';\n return `${OSC}8${SEP}${SEP}${url}${BEL}${text}${OSC}8${SEP}${SEP}${BEL}`;\n}\n\n/**\n * Create ADT Eclipse link\n */\nfunction adtLink(name: string, uri: string, systemName?: string): string {\n if (!systemName || !name || !uri) {\n return name ? chalk.cyan(name) : '';\n }\n const path = uri.startsWith('/sap/bc/adt') ? uri : `/sap/bc/adt${uri}`;\n const url = `adt://${systemName}${path}`;\n return hyperlink(chalk.cyan(name), url);\n}\n\n/**\n * The trusted documentation link of one worklist finding.\n *\n * Live shape (2026-10-22): <atom:link rel=\"http://www.sap.com/adt/\n * relations/documentation\" href=\"/sap/bc/adt/documentation/atc/documents/\n * itemid/{guid}/index/{n}\"/> inside the atcfinding:finding element.\n */\nfunction documentationUriFromFinding(\n finding: NonNullable<\n NonNullable<\n NonNullable<WorklistResponse['worklist']>['objects']['object']\n >[number]['findings']['finding']\n >[number],\n): string | undefined {\n return finding.link?.find(\n (link) => link.rel === 'http://www.sap.com/adt/relations/documentation',\n )?.href;\n}\n\n/**\n * Convert worklist response to AtcResult\n */\nfunction convertWorklistToResult(\n worklistResponse: WorklistResponse,\n checkVariant: string,\n): AtcResult {\n const findings: AtcFinding[] = [];\n\n if (worklistResponse.worklist) {\n const objects = worklistResponse.worklist.objects.object || [];\n\n for (const obj of objects) {\n const objFindings = obj.findings.finding || [];\n for (const finding of objFindings) {\n findings.push({\n checkId: finding.checkId || '',\n checkTitle: finding.checkTitle || '',\n messageId: finding.messageId || '',\n priority: Number.parseInt(finding.priority || '3', 10),\n messageText: finding.messageTitle || '',\n objectUri: obj.uri || '',\n objectType: obj.type || '',\n objectName: obj.name || '',\n location: finding.location,\n findingUri: finding.uri,\n documentationUri: documentationUriFromFinding(finding),\n });\n }\n }\n }\n\n const errorCount = findings.filter((f) => f.priority === 1).length;\n const warningCount = findings.filter((f) => f.priority === 2).length;\n const infoCount = findings.filter((f) => f.priority >= 3).length;\n\n return {\n checkVariant,\n totalFindings: findings.length,\n errorCount,\n warningCount,\n infoCount,\n findings,\n };\n}\n\n/**\n * Display ATC results in console\n */\nfunction displayAtcResults(result: AtcResult, systemName?: string): void {\n if (result.totalFindings === 0) {\n console.log(`\\n✅ ATC check passed - No issues found!`);\n console.log(` 🎯 Variant: ${result.checkVariant}`);\n return;\n }\n\n console.log(`\\n📊 ATC Results Summary:`);\n console.log(` 🎯 Variant: ${result.checkVariant}`);\n if (result.errorCount > 0) console.log(` ❌ Errors: ${result.errorCount}`);\n if (result.warningCount > 0)\n console.log(` ⚠️ Warnings: ${result.warningCount}`);\n if (result.infoCount > 0) console.log(` ℹ️ Info: ${result.infoCount}`);\n console.log(` 📋 Total Issues: ${result.totalFindings}`);\n\n if (result.findings && result.findings.length > 0) {\n console.log(`\\n📄 Findings:`);\n result.findings.slice(0, 5).forEach((finding) => {\n const priorityIcon =\n finding.priority === 1 ? '❌' : finding.priority === 2 ? '⚠️' : 'ℹ️';\n const objectLink = finding.location\n ? adtLink(finding.objectName, finding.location, systemName)\n : finding.objectName;\n console.log(\n ` ${priorityIcon} ${finding.checkTitle || finding.checkId}`,\n );\n console.log(` ${finding.messageText}`);\n console.log(` Object: ${objectLink} (${finding.objectType})`);\n });\n\n if (result.findings.length > 5) {\n console.log(` ... (${result.findings.length - 5} more findings)`);\n }\n }\n\n console.log(`\\n💡 Use --debug for detailed results`);\n}\n\n/**\n * ATC Command Plugin\n */\nexport const atcCommand: CliCommandPlugin = {\n name: 'atc',\n description: 'Run ABAP Test Cockpit (ATC) checks',\n\n options: [\n { flags: '-p, --package <package>', description: 'Run ATC on package' },\n {\n flags: '-o, --object <uri>',\n description:\n 'Run ATC on specific object (e.g., /sap/bc/adt/oo/classes/zcl_my_class)',\n },\n {\n flags: '-t, --transport <transport>',\n description: 'Run ATC on transport request (e.g., TRLK942970)',\n },\n {\n flags: '-f, --from-file <file>',\n description: 'Run ATC on objects listed in file (one URI per line)',\n },\n {\n flags: '--variant <variant>',\n description: 'ATC check variant (default: from system customizing)',\n },\n {\n flags: '--max-results <number>',\n description: 'Maximum number of results',\n default: '100',\n },\n {\n flags: '--format <format>',\n description: 'Output format: console, json, gitlab, sarif',\n default: 'console',\n },\n {\n flags: '--output <file>',\n description: 'Output file (required for gitlab/sarif format)',\n },\n {\n flags: '--resolver <name>',\n description:\n 'Finding resolver plugin for path/line resolution (e.g., abapgit)',\n },\n ],\n\n async execute(args, ctx: CliContext) {\n const options = args as {\n package?: string;\n object?: string;\n transport?: string;\n fromFile?: string;\n variant?: string;\n maxResults?: string;\n format?: OutputFormat;\n output?: string;\n resolver?: string;\n };\n\n // Validate mutually exclusive options\n const targetCount = [\n options.package,\n options.object,\n options.transport,\n options.fromFile,\n ].filter(Boolean).length;\n\n if (targetCount === 0) {\n ctx.logger.error(\n '❌ One of --package, --object, --transport, or --from-file is required',\n );\n process.exit(1);\n }\n\n if (targetCount > 1) {\n ctx.logger.error(\n '❌ Only one of --package, --object, --transport, or --from-file can be specified',\n );\n process.exit(1);\n }\n\n // Validate output file for gitlab/sarif formats\n if (\n (options.format === 'gitlab' || options.format === 'sarif') &&\n !options.output\n ) {\n ctx.logger.error(`❌ --output is required for ${options.format} format`);\n process.exit(1);\n }\n\n // Get ADT client from context\n if (!ctx.getAdtClient) {\n ctx.logger.error(\n '❌ ADT client not available. This command requires authentication.',\n );\n ctx.logger.error(' Run: adt auth login');\n process.exit(1);\n }\n\n const client = (await ctx.getAdtClient()) as AdtClient;\n\n ctx.logger.info('🔍 Running ABAP Test Cockpit checks...');\n\n // Determine target(s)\n let targetUris: string[];\n let targetName: string;\n\n if (options.fromFile) {\n // Read URIs from file\n const { readFileSync } = await import('fs');\n const fileContent = readFileSync(options.fromFile, 'utf-8');\n targetUris = fileContent\n .split('\\n')\n .map((line) => line.trim())\n .filter((line) => line && !line.startsWith('#')); // Skip empty lines and comments\n\n if (targetUris.length === 0) {\n ctx.logger.error(`❌ No objects found in ${options.fromFile}`);\n process.exit(1);\n }\n targetName = `${targetUris.length} objects from ${options.fromFile}`;\n ctx.logger.info(`📄 Target: ${targetName}`);\n } else if (options.transport) {\n targetUris = [`/sap/bc/adt/cts/transportrequests/${options.transport}`];\n targetName = `Transport ${options.transport}`;\n ctx.logger.info(`🚚 Target: ${targetName}`);\n } else if (options.package) {\n targetUris = [`/sap/bc/adt/packages/${options.package.toUpperCase()}`];\n targetName = `Package ${options.package.toUpperCase()}`;\n ctx.logger.info(`📦 Target: ${targetName}`);\n } else {\n targetUris = [options.object!];\n targetName = options.object!;\n ctx.logger.info(`📄 Target: ${targetName}`);\n }\n\n // Step 1: Get ATC customizing to find default check variant\n ctx.logger.info('📋 Reading ATC customizing...');\n const customizing = await client.adt.atc.customizing.get();\n\n // Find systemCheckVariant in properties\n let checkVariant = options.variant;\n if (!checkVariant) {\n const variantProp = customizing.customizing.properties.property?.find(\n (p) => p.name === 'systemCheckVariant',\n );\n if (variantProp?.value) {\n checkVariant = variantProp.value;\n } else {\n checkVariant = 'DEFAULT';\n }\n }\n ctx.logger.info(`🎯 Check variant: ${checkVariant}`);\n\n // Step 2: Create worklist (just with checkVariant, no objects)\n ctx.logger.info('📋 Creating ATC worklist...');\n const worklistCreateResponse = await client.adt.atc.worklists.create({\n checkVariant,\n });\n\n // Extract worklist ID - SAP can return plain string or object\n let worklistId: string;\n if (typeof worklistCreateResponse === 'string') {\n // Plain string response - extract ID from XML or use as-is\n const idMatch = worklistCreateResponse.match(/id=\"([^\"]+)\"/);\n worklistId = idMatch ? idMatch[1] : worklistCreateResponse.trim();\n } else if (\n 'worklist' in worklistCreateResponse &&\n worklistCreateResponse.worklist?.id\n ) {\n worklistId = worklistCreateResponse.worklist.id;\n } else if (\n 'worklistRun' in worklistCreateResponse &&\n worklistCreateResponse.worklistRun?.worklistId\n ) {\n worklistId = worklistCreateResponse.worklistRun.worklistId;\n } else {\n ctx.logger.error('❌ Failed to get worklist ID from response');\n process.exit(1);\n }\n ctx.logger.info(`📋 Worklist created: ${worklistId}`);\n\n // Step 3: Run ATC checks with objects\n ctx.logger.info('⏳ Running ATC analysis...');\n const maxResults = Number.parseInt(options.maxResults || '100', 10);\n const runData = {\n run: {\n maximumVerdicts: maxResults,\n objectSets: {\n objectSet: [\n {\n kind: 'inclusive',\n objectReferences: {\n objectReference: targetUris.map((uri) => ({ uri })),\n },\n },\n ],\n },\n },\n };\n await client.adt.atc.runs.post({ worklistId }, runData);\n\n // Step 4: Get results\n ctx.logger.info('📊 Fetching results...');\n const worklistResult = await client.adt.atc.worklists.get(worklistId, {\n includeExemptedFindings: 'false',\n });\n\n // Convert to AtcResult\n const result = convertWorklistToResult(\n worklistResult as WorklistResponse,\n checkVariant,\n );\n\n // Enrich findings with check categories from the run's check variant\n // form template (best-effort: never fails the run itself)\n const categoryIndex = await fetchCheckCategoryIndex(\n client,\n checkVariant,\n ctx.logger,\n );\n const enriched = enrichResultWithCategories(result, categoryIndex);\n if (enriched > 0) {\n ctx.logger.info(\n `🏷️ Enriched ${enriched}/${result.totalFindings} finding(s) with check categories`,\n );\n }\n\n // Bridge remaining findings through their ATC documentation page: the\n // worklist checkId is a check-instance GUID, not the implementing class\n // the form-template catalog lists (live evidence, 2026-10-22), so\n // the direct index match cannot resolve them. The documentation page\n // names the check class, which resolves through the same index.\n // Best-effort: never fails the run.\n const bridged = await resolveCategoriesViaDocumentation(\n client,\n result.findings,\n categoryIndex,\n { logger: ctx.logger },\n );\n if (bridged > 0) {\n ctx.logger.info(\n `🏷 Bridged ${bridged} finding(s) to check categories via ATC documentation`,\n );\n }\n\n // Load finding resolver if requested\n let resolver: import('../types').FindingResolver | undefined;\n if (options.resolver) {\n const name = options.resolver;\n if (name === 'abapgit') {\n // Built-in abapgit resolver — no external dependencies\n const { createAbapGitResolver } = await import('../resolvers/abapgit');\n resolver = createAbapGitResolver();\n } else {\n // External resolver — try dynamic import\n try {\n ctx.logger.info(`📂 Loading external resolver: ${name}`);\n const mod = await import(name);\n if (typeof mod.createFindingResolver === 'function') {\n resolver = mod.createFindingResolver();\n } else {\n ctx.logger.warn(\n `⚠️ Module ${name} does not export createFindingResolver()`,\n );\n }\n } catch (e) {\n ctx.logger.warn(\n `⚠️ Failed to load resolver \"${name}\": ${e instanceof Error ? e.message : e}`,\n );\n }\n }\n }\n\n // Output based on format\n if (options.format === 'json') {\n console.log(JSON.stringify(result, null, 2));\n } else if (options.format === 'gitlab' && options.output) {\n await outputGitLabCodeQuality(result, options.output, { resolver });\n } else if (options.format === 'sarif' && options.output) {\n await outputSarifReport(result, options.output, targetName);\n } else {\n displayAtcResults(result, ctx.adtSystemName);\n }\n\n // Exit with error code if there are errors\n if (result.errorCount > 0) {\n process.exit(1);\n }\n },\n};\n\nexport default atcCommand;\n"],"mappings":";;;;;;;;AASA,eAAsB,kBACpB,QACA,YACA,aACe;CAEf,MAAM,QAAQ,MAAM,KAClB,IAAI,IACF,OAAO,SAAS,KAAK,MAAM,CACzB,EAAE,SACF;EACE,IAAI,EAAE;EACN,MAAM,EAAE,cAAc,EAAE;EACxB,kBAAkB,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ;EACpD,iBAAiB,EAAE,MAAM,EAAE,cAAc,EAAE,QAAQ;EACnD,sBAAsB,EACpB,OACE,EAAE,aAAa,IACX,UACA,EAAE,aAAa,IACb,YACA,OACV;EACA,YAAY;GACV,MAAM,CAAC,QAAQ,cAAc;GAC7B,WAAW;GACX,GAAI,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc;EACrD;CACF,CACF,CAAC,CACH,CAAC,CAAC,OAAO,CACX;CAEA,MAAM,UAAU,OAAO,SAAS,KAAK,YAAwB;EAC3D,MAAM,WAAW,OAAO,QAAQ,WAAW,YAAY,EAAE,GAAG,QAAQ,WAAW,YAAY,EAAE,GAAG,QAAQ,WAAW,YAAY;EAG/H,MAAM,YAAY,QAAQ,UAAU,MAAM,aAAa;EACvD,MAAM,OAAO,YAAY,OAAO,SAAS,UAAU,IAAI,EAAE,IAAI;EAE7D,OAAO;GACL,QAAQ,QAAQ;GAChB,OACE,QAAQ,aAAa,IACjB,UACA,QAAQ,aAAa,IACnB,YACA;GACR,SAAS,EAAE,MAAM,QAAQ,YAAY;GACrC,WAAW,CACT,EACE,kBAAkB;IAChB,kBAAkB,EAAE,KAAK,SAAS;IAClC,QAAQ;KACN,WAAW;KACX,aAAa;IACf;GACF,EACF,CACF;GACA,qBAAqB,EACnB,yBAAyB,GAAG,QAAQ,QAAQ,GAAG,QAAQ,WAAW,GAAG,OACvE;EACF;CACF,CAAC;CAED,MAAM,cAAc;EAClB,SAAS;EACT,SAAS;EACT,MAAM,CACJ;GACE,MAAM,EACJ,QAAQ;IACN,MAAM;IACN,SAAS;IACT,gBACE;IACF,OAAO,MAAM,KAAK,KAAK;GACzB,EACF;GACA;EACF,CACF;CACF;CAEA,MAAM,UAAU,YAAY,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;CAChE,QAAQ,IAAI,iCAAiC,YAAY;CACzD,QAAQ,IACN,MAAM,OAAO,cAAc,0CAC7B;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;AC1DA,SAAS,kBAAkB,UAAuC;CAChE,IAAI,CAAC,UAAU,OAAO,KAAA;CACtB,MAAM,QACJ,SAAS,MAAM,mBAAmB,KAAK,SAAS,MAAM,kBAAkB;CAC1E,OAAO,QAAQ,MAAM,EAAE,CAAC,YAAY,IAAI,KAAA;AAC1C;AAIA,SAAS,WAAW,SAA6B;CAC/C,OAAO,OAAO,QAAQ,WAAW,YAAY,EAAE,GAAG,QAAQ,WAAW,YAAY,EAAE,GAAG,QAAQ,WAAW,YAAY,EAAE;AACzH;AAIA,eAAsB,wBACpB,QACA,YACA,SACe;CACf,MAAM,WAAW,SAAS;CAG1B,MAAM,eAAe,MAAM,QAAQ,IACjC,OAAO,SAAS,IAAI,OAAO,YAAwB;EAEjD,IAAI;EACJ,QAAQ,QAAQ,UAAhB;GACE,KAAK;IACH,WAAW;IACX;GACF,KAAK;IACH,WAAW;IACX;GACF,KAAK;IACH,WAAW;IACX;GACF,SACE,WAAW;EAEf;EAGA,MAAM,YAAY,QAAQ,UAAU,MAAM,aAAa;EACvD,MAAM,UAAU,YAAY,OAAO,SAAS,UAAU,IAAI,EAAE,IAAI;EAGhE,MAAM,aAAa,kBAAkB,QAAQ,QAAQ;EAGrD,IAAI;EACJ,IAAI;EAEJ,IAAI,UAAU;GACZ,MAAM,WAAW,MAAM,SAAS,QAC9B,QAAQ,YACR,QAAQ,YACR,SACA,YACA,QAAQ,QACV;GACA,IAAI,UAAU;IACZ,WAAW,SAAS;IACpB,WAAW,SAAS;GACtB,OAAO;IACL,WAAW,WAAW,OAAO;IAC7B,WAAW;GACb;EACF,OAAO;GACL,WAAW,WAAW,OAAO;GAC7B,WAAW;EACb;EAGA,MAAM,cAAc,GAAG,QAAQ,QAAQ,GAAG,QAAQ,WAAW,GAAG;EAEhE,OAAO;GACL,aAAa,QAAQ;GACrB,YAAY,QAAQ,cAAc,QAAQ;GAM1C,UAAU,QAAQ,WAAW,KAAA;GAC7B;GACA;GACA,UAAU;IACR,MAAM;IACN,OAAO;KACL,OAAO;KACP,KAAK;IACP;GACF;GAEA,GAAI,cAAc,EAAE,QAAQ,WAAW;GACvC,GAAI,QAAQ,YAAY,EAAE,cAAc,QAAQ,SAAS;GACzD,GAAI,QAAQ,iBAAiB,EAC3B,gBAAgB,QAAQ,cAC1B;EACF;CACF,CAAC,CACH;CAEA,MAAM,UAAU,YAAY,KAAK,UAAU,cAAc,MAAM,CAAC,CAAC;CACjE,QAAQ,IAAI,+CAA+C,YAAY;CACvE,QAAQ,IAAI,MAAM,OAAO,cAAc,iBAAiB;AAC1D;;;;;;;;;;;;;AC1EA,SAAgB,wBACd,KACqB;CACrB,MAAM,wBAAQ,IAAI,IAAoB;CACtC,MAAM,aACJ,KAAK,cAAc,0BAA0B,0BAA0B,CAAC;CAC1E,KAAK,MAAM,YAAY,YACrB,sBAAsB,UAAU,KAAK;CAEvC,OAAO;AACT;;AAGA,SAAS,sBACP,UACA,OACM;CACN,MAAM,eAAe,SAAS,qBAAqB;CACnD,IAAI,CAAC,cAAc;CACnB,MAAM,YAAY,SAAS,SAAS,CAAC,EAAA,CAClC,KAAK,UAAU,MAAM,aAAa,SAAS,mBAAmB,IAAI,CAAC,CACnE,QAAQ,YAA+B,CAAC,CAAC,OAAO;CACnD,KAAK,MAAM,WAAW,UACpB,MAAM,IAAI,SAAS,YAAY;AAEnC;;;;;;;;;;;;;;AA6BA,eAAsB,wBACpB,QACA,cACA,QAI8B;CAC9B,IAAI;EAIF,OAAO,wBAAwB,MAHb,OAAO,IAAI,IAAI,cAAc,aAAa,EAC1D,UAAU,aACZ,CAAC,CACiC;CACpC,SAAS,OAAO;EACd,QAAQ,OACN,6CACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEzD;EACA,uBAAO,IAAI,IAAI;CACjB;AACF;;;;;;;;;;;;AAaA,SAAgB,6BAEd,UAAe,OAAoC;CACnD,IAAI,WAAW;CACf,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,gBAAgB,MAAM,IAAI,QAAQ,OAAO;EAC/C,IAAI,eAAe;GACjB,QAAQ,gBAAgB;GACxB;EACF;CACF;CACA,OAAO;AACT;;;;;;;;AASA,SAAgB,2BAEd,QAA4B,OAAoC;CAChE,OAAO,6BAA6B,OAAO,YAAY,CAAC,GAAG,KAAK;AAClE;;;;;;;;;;;;;;;;;ACpJA,SAAgB,iCACd,MACoB;CAIpB,OAHc,KAAK,MACjB,gHAES,CAAC,GAAG;AACjB;;;;;AAqBA,eAAe,wBACb,kBACA,SACe;CACf,IAAI,QAAQ,wBAAwB,IAAI,gBAAgB,GAAG;CAC3D,IAAI;EACF,MAAM,OAAO,MAAM,QAAQ,KAAK,kBAAkB,QAAQ,UAAU,EAClE,SAAS,EAAE,QAAQ,uCAAuC,EAC5D,CAAC;EACD,QAAQ,wBAAwB,IAC9B,kBACA,iCAAiC,IAAI,CACvC;CACF,SAAS,OAAO;EACd,QAAQ,QAAQ,OACd,wCAAwC,iBAAiB,IACvD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAEzD;EACA,QAAQ,wBAAwB,IAAI,kBAAkB,KAAA,CAAS;CACjE;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,eAAsB,kCACpB,QACA,UACA,iBACA,UAGI,CAAC,GACY;CACjB,MAAM,OAAO,OAAO;CACpB,IAAI,CAAC,QAAQ,gBAAgB,SAAS,GAAG,OAAO;CAEhD,MAAM,UAAU,SAAS,QACtB,YAAY,CAAC,QAAQ,iBAAiB,QAAQ,gBACjD;CACA,IAAI,QAAQ,WAAW,GAAG,OAAO;CAEjC,MAAM,UAAqC;EACzC;EACA,UAAU,QAAQ,YAAY;EAC9B,yCAAyB,IAAI,IAAgC;EAC7D,QAAQ,QAAQ;CAClB;CACA,IAAI,WAAW;CACf,KAAK,MAAM,WAAW,SAAS;EAC7B,MAAM,wBAAwB,QAAQ,kBAAmB,OAAO;EAChE,MAAM,aAAa,QAAQ,wBAAwB,IACjD,QAAQ,gBACV;EACA,MAAM,gBAAgB,aAClB,gBAAgB,IAAI,UAAU,IAC9B,KAAA;EACJ,IAAI,eAAe;GACjB,QAAQ,gBAAgB;GACxB;EACF;CACF;CACA,OAAO;AACT;;;;;ACtCA,SAAS,UAAU,MAAc,KAAqB;CACpD,MAAM,MAAM;CACZ,MAAM,MAAM;CACZ,MAAM,MAAM;CACZ,OAAO,GAAG,IAAI,GAAG,MAAM,MAAM,MAAM,MAAM,OAAO,IAAI,GAAG,MAAM,MAAM;AACrE;;;;AAKA,SAAS,QAAQ,MAAc,KAAa,YAA6B;CACvE,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,KAC3B,OAAO,OAAO,MAAM,KAAK,IAAI,IAAI;CAGnC,MAAM,MAAM,SAAS,aADR,IAAI,WAAW,aAAa,IAAI,MAAM,cAAc;CAEjE,OAAO,UAAU,MAAM,KAAK,IAAI,GAAG,GAAG;AACxC;;;;;;;;AASA,SAAS,4BACP,SAKoB;CACpB,OAAO,QAAQ,MAAM,MAClB,SAAS,KAAK,QAAQ,gDACzB,CAAC,EAAE;AACL;;;;AAKA,SAAS,wBACP,kBACA,cACW;CACX,MAAM,WAAyB,CAAC;CAEhC,IAAI,iBAAiB,UAAU;EAC7B,MAAM,UAAU,iBAAiB,SAAS,QAAQ,UAAU,CAAC;EAE7D,KAAK,MAAM,OAAO,SAAS;GACzB,MAAM,cAAc,IAAI,SAAS,WAAW,CAAC;GAC7C,KAAK,MAAM,WAAW,aACpB,SAAS,KAAK;IACZ,SAAS,QAAQ,WAAW;IAC5B,YAAY,QAAQ,cAAc;IAClC,WAAW,QAAQ,aAAa;IAChC,UAAU,OAAO,SAAS,QAAQ,YAAY,KAAK,EAAE;IACrD,aAAa,QAAQ,gBAAgB;IACrC,WAAW,IAAI,OAAO;IACtB,YAAY,IAAI,QAAQ;IACxB,YAAY,IAAI,QAAQ;IACxB,UAAU,QAAQ;IAClB,YAAY,QAAQ;IACpB,kBAAkB,4BAA4B,OAAO;GACvD,CAAC;EAEL;CACF;CAEA,MAAM,aAAa,SAAS,QAAQ,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;CAC5D,MAAM,eAAe,SAAS,QAAQ,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;CAC9D,MAAM,YAAY,SAAS,QAAQ,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC;CAE1D,OAAO;EACL;EACA,eAAe,SAAS;EACxB;EACA;EACA;EACA;CACF;AACF;;;;AAKA,SAAS,kBAAkB,QAAmB,YAA2B;CACvE,IAAI,OAAO,kBAAkB,GAAG;EAC9B,QAAQ,IAAI,yCAAyC;EACrD,QAAQ,IAAI,kBAAkB,OAAO,cAAc;EACnD;CACF;CAEA,QAAQ,IAAI,2BAA2B;CACvC,QAAQ,IAAI,kBAAkB,OAAO,cAAc;CACnD,IAAI,OAAO,aAAa,GAAG,QAAQ,IAAI,gBAAgB,OAAO,YAAY;CAC1E,IAAI,OAAO,eAAe,GACxB,QAAQ,IAAI,mBAAmB,OAAO,cAAc;CACtD,IAAI,OAAO,YAAY,GAAG,QAAQ,IAAI,eAAe,OAAO,WAAW;CACvE,QAAQ,IAAI,uBAAuB,OAAO,eAAe;CAEzD,IAAI,OAAO,YAAY,OAAO,SAAS,SAAS,GAAG;EACjD,QAAQ,IAAI,gBAAgB;EAC5B,OAAO,SAAS,MAAM,GAAG,CAAC,CAAC,CAAC,SAAS,YAAY;GAC/C,MAAM,eACJ,QAAQ,aAAa,IAAI,MAAM,QAAQ,aAAa,IAAI,OAAO;GACjE,MAAM,aAAa,QAAQ,WACvB,QAAQ,QAAQ,YAAY,QAAQ,UAAU,UAAU,IACxD,QAAQ;GACZ,QAAQ,IACN,MAAM,aAAa,GAAG,QAAQ,cAAc,QAAQ,SACtD;GACA,QAAQ,IAAI,SAAS,QAAQ,aAAa;GAC1C,QAAQ,IAAI,iBAAiB,WAAW,IAAI,QAAQ,WAAW,EAAE;EACnE,CAAC;EAED,IAAI,OAAO,SAAS,SAAS,GAC3B,QAAQ,IAAI,WAAW,OAAO,SAAS,SAAS,EAAE,gBAAgB;CAEtE;CAEA,QAAQ,IAAI,uCAAuC;AACrD;;;;AAKA,MAAa,aAA+B;CAC1C,MAAM;CACN,aAAa;CAEb,SAAS;EACP;GAAE,OAAO;GAA2B,aAAa;EAAqB;EACtE;GACE,OAAO;GACP,aACE;EACJ;EACA;GACE,OAAO;GACP,aAAa;EACf;EACA;GACE,OAAO;GACP,aAAa;EACf;EACA;GACE,OAAO;GACP,aAAa;EACf;EACA;GACE,OAAO;GACP,aAAa;GACb,SAAS;EACX;EACA;GACE,OAAO;GACP,aAAa;GACb,SAAS;EACX;EACA;GACE,OAAO;GACP,aAAa;EACf;EACA;GACE,OAAO;GACP,aACE;EACJ;CACF;CAEA,MAAM,QAAQ,MAAM,KAAiB;EACnC,MAAM,UAAU;EAahB,MAAM,cAAc;GAClB,QAAQ;GACR,QAAQ;GACR,QAAQ;GACR,QAAQ;EACV,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC;EAElB,IAAI,gBAAgB,GAAG;GACrB,IAAI,OAAO,MACT,uEACF;GACA,QAAQ,KAAK,CAAC;EAChB;EAEA,IAAI,cAAc,GAAG;GACnB,IAAI,OAAO,MACT,iFACF;GACA,QAAQ,KAAK,CAAC;EAChB;EAGA,KACG,QAAQ,WAAW,YAAY,QAAQ,WAAW,YACnD,CAAC,QAAQ,QACT;GACA,IAAI,OAAO,MAAM,8BAA8B,QAAQ,OAAO,QAAQ;GACtE,QAAQ,KAAK,CAAC;EAChB;EAGA,IAAI,CAAC,IAAI,cAAc;GACrB,IAAI,OAAO,MACT,mEACF;GACA,IAAI,OAAO,MAAM,wBAAwB;GACzC,QAAQ,KAAK,CAAC;EAChB;EAEA,MAAM,SAAU,MAAM,IAAI,aAAa;EAEvC,IAAI,OAAO,KAAK,wCAAwC;EAGxD,IAAI;EACJ,IAAI;EAEJ,IAAI,QAAQ,UAAU;GAEpB,MAAM,EAAE,iBAAiB,MAAM,OAAO;GAEtC,aADoB,aAAa,QAAQ,UAAU,OAC5B,CAAC,CACrB,MAAM,IAAI,CAAC,CACX,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,CAC1B,QAAQ,SAAS,QAAQ,CAAC,KAAK,WAAW,GAAG,CAAC;GAEjD,IAAI,WAAW,WAAW,GAAG;IAC3B,IAAI,OAAO,MAAM,yBAAyB,QAAQ,UAAU;IAC5D,QAAQ,KAAK,CAAC;GAChB;GACA,aAAa,GAAG,WAAW,OAAO,gBAAgB,QAAQ;GAC1D,IAAI,OAAO,KAAK,cAAc,YAAY;EAC5C,OAAO,IAAI,QAAQ,WAAW;GAC5B,aAAa,CAAC,qCAAqC,QAAQ,WAAW;GACtE,aAAa,aAAa,QAAQ;GAClC,IAAI,OAAO,KAAK,cAAc,YAAY;EAC5C,OAAO,IAAI,QAAQ,SAAS;GAC1B,aAAa,CAAC,wBAAwB,QAAQ,QAAQ,YAAY,GAAG;GACrE,aAAa,WAAW,QAAQ,QAAQ,YAAY;GACpD,IAAI,OAAO,KAAK,cAAc,YAAY;EAC5C,OAAO;GACL,aAAa,CAAC,QAAQ,MAAO;GAC7B,aAAa,QAAQ;GACrB,IAAI,OAAO,KAAK,cAAc,YAAY;EAC5C;EAGA,IAAI,OAAO,KAAK,+BAA+B;EAC/C,MAAM,cAAc,MAAM,OAAO,IAAI,IAAI,YAAY,IAAI;EAGzD,IAAI,eAAe,QAAQ;EAC3B,IAAI,CAAC,cAAc;GACjB,MAAM,cAAc,YAAY,YAAY,WAAW,UAAU,MAC9D,MAAM,EAAE,SAAS,oBACpB;GACA,IAAI,aAAa,OACf,eAAe,YAAY;QAE3B,eAAe;EAEnB;EACA,IAAI,OAAO,KAAK,qBAAqB,cAAc;EAGnD,IAAI,OAAO,KAAK,6BAA6B;EAC7C,MAAM,yBAAyB,MAAM,OAAO,IAAI,IAAI,UAAU,OAAO,EACnE,aACF,CAAC;EAGD,IAAI;EACJ,IAAI,OAAO,2BAA2B,UAAU;GAE9C,MAAM,UAAU,uBAAuB,MAAM,cAAc;GAC3D,aAAa,UAAU,QAAQ,KAAK,uBAAuB,KAAK;EAClE,OAAO,IACL,cAAc,0BACd,uBAAuB,UAAU,IAEjC,aAAa,uBAAuB,SAAS;OACxC,IACL,iBAAiB,0BACjB,uBAAuB,aAAa,YAEpC,aAAa,uBAAuB,YAAY;OAC3C;GACL,IAAI,OAAO,MAAM,2CAA2C;GAC5D,QAAQ,KAAK,CAAC;EAChB;EACA,IAAI,OAAO,KAAK,wBAAwB,YAAY;EAGpD,IAAI,OAAO,KAAK,2BAA2B;EAE3C,MAAM,UAAU,EACd,KAAK;GACH,iBAHe,OAAO,SAAS,QAAQ,cAAc,OAAO,EAGlC;GAC1B,YAAY,EACV,WAAW,CACT;IACE,MAAM;IACN,kBAAkB,EAChB,iBAAiB,WAAW,KAAK,SAAS,EAAE,IAAI,EAAE,EACpD;GACF,CACF,EACF;EACF,EACF;EACA,MAAM,OAAO,IAAI,IAAI,KAAK,KAAK,EAAE,WAAW,GAAG,OAAO;EAGtD,IAAI,OAAO,KAAK,wBAAwB;EAMxC,MAAM,SAAS,wBACb,MAN2B,OAAO,IAAI,IAAI,UAAU,IAAI,YAAY,EACpE,yBAAyB,QAC3B,CAAC,GAKC,YACF;EAIA,MAAM,gBAAgB,MAAM,wBAC1B,QACA,cACA,IAAI,MACN;EACA,MAAM,WAAW,2BAA2B,QAAQ,aAAa;EACjE,IAAI,WAAW,GACb,IAAI,OAAO,KACT,gBAAgB,SAAS,GAAG,OAAO,cAAc,kCACnD;EASF,MAAM,UAAU,MAAM,kCACpB,QACA,OAAO,UACP,eACA,EAAE,QAAQ,IAAI,OAAO,CACvB;EACA,IAAI,UAAU,GACZ,IAAI,OAAO,KACT,cAAc,QAAQ,sDACxB;EAIF,IAAI;EACJ,IAAI,QAAQ,UAAU;GACpB,MAAM,OAAO,QAAQ;GACrB,IAAI,SAAS,WAAW;IAEtB,MAAM,EAAE,0BAA0B,MAAM,OAAO,yBAAuB,CAAA,MAAA,MAAA,EAAA,CAAA;IACtE,WAAW,sBAAsB;GACnC,OAEE,IAAI;IACF,IAAI,OAAO,KAAK,iCAAiC,MAAM;IACvD,MAAM,MAAM,MAAM,OAAO;IACzB,IAAI,OAAO,IAAI,0BAA0B,YACvC,WAAW,IAAI,sBAAsB;SAErC,IAAI,OAAO,KACT,aAAa,KAAK,yCACpB;GAEJ,SAAS,GAAG;IACV,IAAI,OAAO,KACT,+BAA+B,KAAK,KAAK,aAAa,QAAQ,EAAE,UAAU,GAC5E;GACF;EAEJ;EAGA,IAAI,QAAQ,WAAW,QACrB,QAAQ,IAAI,KAAK,UAAU,QAAQ,MAAM,CAAC,CAAC;OACtC,IAAI,QAAQ,WAAW,YAAY,QAAQ,QAChD,MAAM,wBAAwB,QAAQ,QAAQ,QAAQ,EAAE,SAAS,CAAC;OAC7D,IAAI,QAAQ,WAAW,WAAW,QAAQ,QAC/C,MAAM,kBAAkB,QAAQ,QAAQ,QAAQ,UAAU;OAE1D,kBAAkB,QAAQ,IAAI,aAAa;EAI7C,IAAI,OAAO,aAAa,GACtB,QAAQ,KAAK,CAAC;CAElB;AACF"}
|