@abapify/adt-client 0.3.6 → 0.4.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["exceptionSchema","createAdtContractClient"],"sources":["../src/utils/session.ts","../src/errors.ts","../src/adapter.ts","../src/services/transports.ts","../src/services/users.ts","../src/client.ts","../src/plugins/logging.ts","../src/plugins/file-logging.ts"],"sourcesContent":["/**\n * Session Management for SAP ADT\n *\n * Handles stateful sessions with cookie management and CSRF token caching.\n * Separated into testable modules for better maintainability.\n */\n\nimport type { Logger } from '@abapify/logger';\n\n/**\n * Cookie Store - Manages HTTP cookies for stateful sessions\n */\nexport class CookieStore {\n private readonly cookies = new Map<string, string>();\n\n /**\n * Parse Set-Cookie header and store cookies\n * Handles complex cookie strings with expires dates\n */\n parseCookies(setCookieHeader: string): void {\n const cookieStrings = this.splitCookieHeader(setCookieHeader);\n\n for (const cookieString of cookieStrings) {\n // Extract name=value pair (first part before semicolon)\n const nameValuePart = cookieString.split(';')[0].trim();\n const [name, value] = nameValuePart.split('=', 2);\n\n // Store only valid cookies (ignore metadata like expires, path)\n if (\n name &&\n value &&\n !name.includes('expires') &&\n !name.includes('path')\n ) {\n this.cookies.set(name.trim(), value.trim());\n }\n }\n }\n\n /**\n * Split Set-Cookie header by commas, avoiding splitting on expires dates\n */\n private splitCookieHeader(setCookieHeader: string): string[] {\n const cookies: string[] = [];\n let current = '';\n let inExpires = false;\n\n for (let i = 0; i < setCookieHeader.length; i++) {\n const char = setCookieHeader[i];\n\n if (char === ',' && !inExpires) {\n if (current.trim()) {\n cookies.push(current.trim());\n }\n current = '';\n } else {\n current += char;\n\n // Track if we're inside an expires attribute\n if (current.toLowerCase().includes('expires=')) {\n inExpires = true;\n }\n\n // End of expires when we hit semicolon or end\n if (inExpires && (char === ';' || i === setCookieHeader.length - 1)) {\n inExpires = false;\n }\n }\n }\n\n if (current.trim()) {\n cookies.push(current.trim());\n }\n\n return cookies;\n }\n\n /**\n * Get Cookie header value for requests\n */\n getCookieHeader(): string | undefined {\n if (this.cookies.size === 0) {\n return undefined;\n }\n\n return Array.from(this.cookies.entries())\n .map(([name, value]) => `${name}=${value}`)\n .join('; ');\n }\n\n /**\n * Check if we have any cookies\n */\n hasCookies(): boolean {\n return this.cookies.size > 0;\n }\n\n /**\n * Clear all cookies\n */\n clear(): void {\n this.cookies.clear();\n }\n\n /**\n * Get all cookies as a map (for testing/debugging)\n */\n getAll(): Map<string, string> {\n return new Map(this.cookies);\n }\n\n /**\n * Inject pre-existing cookies (e.g., from SAML authentication)\n * @param cookieString Cookie string in \"name=value; name2=value2\" format\n */\n injectCookie(cookieString: string): void {\n // Split by \"; \" to handle multiple cookies\n const cookies = cookieString.split('; ');\n for (const cookie of cookies) {\n const [name, ...valueParts] = cookie.split('=');\n const value = valueParts.join('='); // Handle values containing '='\n if (name && value) {\n this.cookies.set(name.trim(), value.trim());\n }\n }\n }\n}\n\n/**\n * CSRF Token Manager - Handles CSRF token caching and initialization\n */\nexport class CsrfTokenManager {\n private cachedToken?: string;\n\n /**\n * Extract CSRF token from cookies (SAP stores it there)\n */\n extractFromCookies(cookies: Map<string, string>): string | undefined {\n // Find CSRF/XSRF cookie\n const xsrfEntry = Array.from(cookies.entries()).find(\n ([key]) =>\n key.toLowerCase().includes('xsrf') ||\n key.toLowerCase().includes('csrf'),\n );\n\n if (!xsrfEntry) {\n return undefined;\n }\n\n // Decode cookie value\n const cookieValue = xsrfEntry[1];\n const decodedToken = decodeURIComponent(cookieValue);\n\n // Extract just the token part (before timestamp if present)\n const tokenMatch = decodedToken.match(/^([A-Za-z0-9+/_-]+=*)/);\n const actualToken = tokenMatch ? tokenMatch[1] : decodedToken;\n\n // Validate token (ignore placeholder values)\n if (\n actualToken &&\n actualToken !== 'Required' &&\n actualToken !== 'fetch' &&\n actualToken !== 'Fetch'\n ) {\n return actualToken;\n }\n\n return undefined;\n }\n\n /**\n * Extract CSRF token from response header\n */\n extractFromHeader(headerValue: string | null): string | undefined {\n if (\n !headerValue ||\n headerValue === 'Required' ||\n headerValue === 'fetch' ||\n headerValue === 'Fetch'\n ) {\n return undefined;\n }\n\n return headerValue;\n }\n\n /**\n * Cache a CSRF token\n */\n cache(token: string): void {\n this.cachedToken = token;\n }\n\n /**\n * Get cached CSRF token\n */\n getCached(): string | undefined {\n return this.cachedToken;\n }\n\n /**\n * Clear cached CSRF token (e.g., on 403 errors)\n */\n clear(): void {\n this.cachedToken = undefined;\n }\n\n /**\n * Check if we have a cached token\n */\n hasCached(): boolean {\n return !!this.cachedToken;\n }\n\n /**\n * Alias for hasCached() - for consistency with SessionManager API\n */\n hasToken(): boolean {\n return this.hasCached();\n }\n}\n\n/**\n * ETag Manager - Tracks ETags for optimistic locking\n */\nexport class ETagManager {\n private readonly etags = new Map<string, string>();\n\n /**\n * Extract and cache ETag from response header\n * @param url The request URL (used as key)\n * @param headerValue The ETag header value\n */\n cacheFromHeader(url: string, headerValue: string | null): void {\n if (headerValue) {\n // Normalize URL to just the path (remove query params)\n const key = this.normalizeUrl(url);\n this.etags.set(key, headerValue);\n }\n }\n\n /**\n * Get cached ETag for a URL\n */\n get(url: string): string | undefined {\n const key = this.normalizeUrl(url);\n return this.etags.get(key);\n }\n\n /**\n * Clear cached ETag for a URL\n */\n clear(url?: string): void {\n if (url) {\n const key = this.normalizeUrl(url);\n this.etags.delete(key);\n } else {\n this.etags.clear();\n }\n }\n\n /**\n * Normalize URL to use as cache key.\n * Removes query params, trailing slashes, and lowercases the path.\n * SAP ADT URLs are case-insensitive, so /fmodules/ZAGE_FM and\n * /fmodules/zage_fm must share the same ETag cache entry.\n */\n private normalizeUrl(url: string): string {\n try {\n const parsed = new URL(url);\n return parsed.pathname.replace(/\\/$/, '').toLowerCase();\n } catch {\n // If not a full URL, just use the path\n return url.split('?')[0].replace(/\\/$/, '').toLowerCase();\n }\n }\n}\n\n/**\n * Session Manager - Orchestrates cookies and CSRF tokens\n */\nexport class SessionManager {\n private readonly cookieStore = new CookieStore();\n private readonly csrfManager = new CsrfTokenManager();\n private readonly etagManager = new ETagManager();\n private securitySessionActive = false;\n\n constructor(private readonly logger?: Logger) {}\n\n /**\n * Process response to update session state\n * Extracts cookies, CSRF tokens, and ETags\n * @param response The HTTP response\n * @param url Optional URL for ETag caching (required for ETag tracking)\n */\n processResponse(response: Response, url?: string): void {\n // Cache ETag for optimistic locking\n if (url) {\n const etag = response.headers.get('etag');\n if (etag) {\n this.etagManager.cacheFromHeader(url, etag);\n this.logger?.debug(`Session: ETag cached for ${url}: ${etag}`);\n }\n }\n\n // Update cookies\n const setCookieHeader = response.headers.get('set-cookie');\n if (setCookieHeader) {\n this.cookieStore.parseCookies(setCookieHeader);\n this.logger?.debug('Session: Cookies updated from response');\n }\n\n // Try to extract and cache CSRF token from header\n const csrfHeader = response.headers.get('x-csrf-token');\n const csrfToken = this.csrfManager.extractFromHeader(csrfHeader);\n if (csrfToken) {\n this.csrfManager.cache(csrfToken);\n this.logger?.debug('Session: CSRF token cached from header');\n }\n\n // Try to extract CSRF from cookies if not in header\n if (!csrfToken && this.cookieStore.hasCookies()) {\n const cookieCsrf = this.csrfManager.extractFromCookies(\n this.cookieStore.getAll(),\n );\n if (cookieCsrf) {\n this.csrfManager.cache(cookieCsrf);\n this.logger?.debug('Session: CSRF token cached from cookies');\n }\n }\n }\n\n /**\n * Get headers for next request\n * @param method HTTP method\n * @param url Optional URL for ETag lookup (for PUT/PATCH with optimistic locking)\n */\n getRequestHeaders(method: string, url?: string): Record<string, string> {\n const headers: Record<string, string> = {};\n\n // Reuse the security session for all requests after creation\n if (this.securitySessionActive) {\n headers['x-sap-security-session'] = 'use';\n }\n\n // Add cookies if we have any\n const cookieHeader = this.cookieStore.getCookieHeader();\n if (cookieHeader) {\n headers.Cookie = cookieHeader;\n }\n\n // Add CSRF token for write operations\n const needsCsrf = ['POST', 'PUT', 'DELETE', 'PATCH'].includes(\n method.toUpperCase(),\n );\n if (needsCsrf) {\n const cachedToken = this.csrfManager.getCached();\n if (cachedToken) {\n headers['x-csrf-token'] = cachedToken;\n } else {\n // Request CSRF token if we don't have one\n headers['x-csrf-token'] = 'Fetch';\n }\n }\n\n // Add If-Match header for PUT/PATCH if we have a cached ETag\n const needsEtag = ['PUT', 'PATCH'].includes(method.toUpperCase());\n if (needsEtag && url) {\n const etag = this.etagManager.get(url);\n if (etag) {\n headers['If-Match'] = etag;\n this.logger?.debug(`Session: Using cached ETag for If-Match: ${etag}`);\n }\n }\n\n return headers;\n }\n\n /**\n * Get cached ETag for a URL\n */\n getETag(url: string): string | undefined {\n return this.etagManager.get(url);\n }\n\n /**\n * Clear cached ETag for a URL\n */\n clearETag(url?: string): void {\n this.etagManager.clear(url);\n }\n\n /**\n * Initialize CSRF token using the Eclipse ADT security session flow:\n *\n * 1. GET /sessions + x-sap-security-session: create → create security session\n * 2. GET /sessions + x-sap-security-session: use + x-csrf-token: Fetch → get CSRF token\n * 3. DELETE /sessions/<id> + x-sap-security-session: use + x-csrf-token → destroy session\n *\n * The CSRF token survives the session deletion and remains valid for\n * all subsequent lock/unlock operations. Deleting the session frees\n * the slot — SAP allows only one security session per user.\n *\n * @param baseUrl - SAP system base URL\n * @param authHeader - Authorization header (Basic/Bearer), or undefined for cookie auth\n * @param client - SAP client number\n * @param language - SAP language\n */\n async initializeCsrf(\n baseUrl: string,\n authHeader?: string,\n client?: string,\n language?: string,\n ): Promise<boolean> {\n const sessionsUrl = new URL('/sap/bc/adt/core/http/sessions', baseUrl);\n\n if (client) {\n sessionsUrl.searchParams.append('sap-client', client);\n }\n if (language) {\n sessionsUrl.searchParams.append('sap-language', language);\n }\n\n /** Build common headers, optionally including Authorization and cookies */\n const baseHeaders = (): Record<string, string> => {\n const h: Record<string, string> = {\n Accept: 'application/vnd.sap.adt.core.http.session.v3+xml',\n 'X-sap-adt-sessiontype': 'stateful',\n };\n if (authHeader) h.Authorization = authHeader;\n const cookie = this.cookieStore.getCookieHeader();\n if (cookie) h.Cookie = cookie;\n return h;\n };\n\n try {\n // ── Step 1: Create security session ──────────────────────────\n this.logger?.debug('Session: Creating security session');\n const createResponse = await fetch(sessionsUrl.toString(), {\n method: 'GET',\n headers: {\n ...baseHeaders(),\n 'x-sap-security-session': 'create',\n },\n });\n\n if (!createResponse.ok) {\n this.logger?.warn(\n `Session: Security session creation failed with status ${createResponse.status}`,\n );\n return false;\n }\n\n // Extract cookies (sap-contextid) from the create response\n this.processResponse(createResponse);\n\n // Extract session URL from response body for later DELETE\n const createBody = await createResponse.text();\n const sessionHrefMatch = createBody.match(\n /href=\"([^\"]*\\/sessions\\/[^\"]*)\"/,\n );\n const sessionPath = sessionHrefMatch?.[1];\n\n // ── Step 2: Fetch CSRF token within the session ──────────────\n this.logger?.debug('Session: Fetching CSRF token');\n const csrfResponse = await fetch(sessionsUrl.toString(), {\n method: 'GET',\n headers: {\n ...baseHeaders(),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': 'Fetch',\n },\n });\n\n if (!csrfResponse.ok) {\n this.logger?.warn(\n `Session: CSRF fetch failed with status ${csrfResponse.status}`,\n );\n return false;\n }\n\n this.processResponse(csrfResponse);\n\n const success = this.csrfManager.hasCached();\n if (!success) {\n this.logger?.warn(\n 'Session: CSRF fetch succeeded but no token found in response',\n );\n return false;\n }\n\n this.securitySessionActive = true;\n this.logger?.debug('Session: CSRF token acquired');\n\n // ── Step 3: Delete the security session (token stays valid) ──\n // Frees the session slot — SAP allows one security session per user.\n if (sessionPath) {\n const deleteUrl = new URL(sessionPath, baseUrl);\n if (client) deleteUrl.searchParams.append('sap-client', client);\n\n try {\n this.logger?.debug(\n `Session: Deleting security session ${sessionPath}`,\n );\n await fetch(deleteUrl.toString(), {\n method: 'DELETE',\n headers: {\n ...baseHeaders(),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': this.csrfManager.getCached()!,\n },\n });\n this.logger?.debug('Session: Security session deleted');\n } catch {\n // Best-effort — session will time out anyway\n this.logger?.debug(\n 'Session: Failed to delete security session (will expire)',\n );\n }\n }\n\n return true;\n } catch (error) {\n this.logger?.error(\n `Session: CSRF initialization error: ${error instanceof Error ? error.message : String(error)}`,\n );\n return false;\n }\n }\n\n /**\n * Clear all session state (cookies and CSRF)\n */\n clear(): void {\n this.cookieStore.clear();\n this.csrfManager.clear();\n this.securitySessionActive = false;\n this.logger?.debug('Session: Cleared all session state (cookies and CSRF)');\n }\n\n /**\n * Check if we have a cached CSRF token\n */\n hasCsrfToken(): boolean {\n return this.csrfManager.hasCached();\n }\n\n /**\n * Get Cookie header value for requests\n */\n getCookieHeader(): string | undefined {\n return this.cookieStore.getCookieHeader();\n }\n\n /**\n * Get session type header value\n * SAP ADT requires 'stateful' for operations that need session persistence\n */\n getSessionTypeHeader(): string {\n return 'stateful';\n }\n\n /**\n * Inject a pre-existing cookie (e.g., from SAML authentication)\n * @param cookieString Cookie string in \"name=value\" format\n */\n injectCookie(cookieString: string): void {\n this.cookieStore.injectCookie(cookieString);\n this.logger?.debug(`Session: Injected cookie from external source`);\n }\n}\n","/**\n * ADT Error Handling\n *\n * SAP ADT returns errors in a standard XML format defined by exception.xsd:\n * <exc:exception xmlns:exc=\"http://www.sap.com/abapxml/types/communicationframework\">\n * <namespace id=\"...\"/>\n * <type id=\"...\"/>\n * <message lang=\"EN\">Human readable error message</message>\n * <localizedMessage lang=\"EN\">Localized message</localizedMessage>\n * <properties>\n * <entry key=\"key1\">value1</entry>\n * </properties>\n * </exc:exception>\n *\n * Uses the generated exception schema from @abapify/adt-schemas for type-safe parsing.\n */\n\nimport {\n exception as exceptionSchema,\n type InferTypedSchema,\n} from '@abapify/adt-schemas';\n\n/** Exception schema type - inferred from the typed schema */\ntype ExceptionSchema = InferTypedSchema<typeof exceptionSchema>;\n\n/**\n * Parsed ADT exception structure - derived from ExceptionSchema\n */\nexport interface AdtExceptionData {\n /** Exception namespace ID (e.g., \"com.sap.adt.oo\") */\n namespace?: string;\n /** Exception type ID (e.g., \"OBJECT_NOT_FOUND\") */\n type?: string;\n /** Human-readable error message */\n message?: string;\n /** Localized error message */\n localizedMessage?: string;\n /** Additional properties as key-value pairs */\n properties?: Record<string, string>;\n}\n\n/** Re-export the schema type for consumers */\nexport type { ExceptionSchema };\n\n/**\n * Custom error class for ADT exceptions\n *\n * Provides structured access to SAP ADT error details.\n */\nexport class AdtError extends Error {\n /** HTTP status code */\n readonly status: number;\n /** HTTP status text */\n readonly statusText: string;\n /** Parsed ADT exception data (if available) */\n readonly exception?: AdtExceptionData;\n /** Raw response body */\n readonly rawBody: string;\n /** Request URL */\n readonly url: string;\n /** HTTP method */\n readonly method: string;\n\n constructor(options: {\n status: number;\n statusText: string;\n url: string;\n method: string;\n rawBody: string;\n exception?: AdtExceptionData;\n }) {\n // Build a meaningful error message\n const baseMsg = `HTTP ${options.status}: ${options.statusText}`;\n const detailMsg =\n options.exception?.message || options.exception?.localizedMessage;\n const fullMsg = detailMsg ? `${baseMsg} - ${detailMsg}` : baseMsg;\n\n super(fullMsg);\n this.name = 'AdtError';\n this.status = options.status;\n this.statusText = options.statusText;\n this.url = options.url;\n this.method = options.method;\n this.rawBody = options.rawBody;\n this.exception = options.exception;\n\n // Maintain proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, AdtError);\n }\n }\n\n /**\n * Get the most relevant error message\n */\n get detailMessage(): string {\n return (\n this.exception?.localizedMessage ||\n this.exception?.message ||\n this.statusText\n );\n }\n\n /**\n * Check if this is a specific error type\n */\n isType(type: string): boolean {\n return this.exception?.type === type;\n }\n\n /**\n * Check if this is from a specific namespace\n */\n isNamespace(namespace: string): boolean {\n return this.exception?.namespace === namespace;\n }\n\n /**\n * Get a property value from the exception\n */\n getProperty(key: string): string | undefined {\n return this.exception?.properties?.[key];\n }\n}\n\n/**\n * Parse ADT exception XML into structured data\n *\n * Uses the generated exception schema from @abapify/adt-schemas for type-safe parsing.\n */\nexport function parseAdtException(xml: string): AdtExceptionData | undefined {\n // Quick check if this looks like an ADT exception. We look for the\n // communicationframework namespace in an xmlns attribute context rather\n // than a free substring match (which would fire on user-controlled\n // content happening to contain the URI anywhere in the XML body).\n if (\n !xml.includes('exception') ||\n !/xmlns(?::[\\w-]+)?=\"http:\\/\\/www\\.sap\\.com\\/abapxml\\/types\\/communicationframework\"/.test(\n xml,\n )\n ) {\n return undefined;\n }\n\n try {\n // Use the generated schema for type-safe parsing\n const parsed = exceptionSchema.parse(xml);\n const exc = parsed.exception;\n\n if (!exc) {\n return undefined;\n }\n\n const result: AdtExceptionData = {};\n\n // Map parsed schema to our interface\n // Note: ts-xsd uses $value for simpleContent text values\n if (exc.namespace?.id) {\n result.namespace = exc.namespace.id;\n }\n if (exc.type?.id) {\n result.type = exc.type.id;\n }\n if (exc.message?.$value) {\n result.message = exc.message.$value;\n }\n if (exc.localizedMessage?.$value) {\n result.localizedMessage = exc.localizedMessage.$value;\n }\n if (exc.properties?.entry) {\n result.properties = {};\n const entries = Array.isArray(exc.properties.entry)\n ? exc.properties.entry\n : [exc.properties.entry];\n for (const entry of entries) {\n if (entry.key && entry.$value) {\n result.properties[entry.key] = entry.$value;\n }\n }\n }\n\n // Return undefined if we didn't find any meaningful data\n if (\n !result.namespace &&\n !result.type &&\n !result.message &&\n !result.localizedMessage\n ) {\n return undefined;\n }\n\n return result;\n } catch {\n // If schema parsing fails, return undefined (not an ADT exception)\n return undefined;\n }\n}\n\n/**\n * Create an AdtError from HTTP response details\n */\nexport function createAdtError(\n status: number,\n statusText: string,\n url: string,\n method: string,\n rawBody: string,\n): AdtError {\n const exception = parseAdtException(rawBody);\n return new AdtError({\n status,\n statusText,\n url,\n method,\n rawBody,\n exception,\n });\n}\n","/**\n * ADT HTTP Adapter with Basic Authentication\n *\n * Implements the Speci HttpAdapter interface for ADT communication.\n * Automatically handles XML parsing/building using Serializable schemas from adt-schemas.\n */\n\nimport type { AdtConnectionConfig } from './types';\nimport type { HttpAdapter, HttpRequestOptions } from '@abapify/adt-contracts';\nimport type { ResponsePlugin, ResponseContext } from './plugins/types';\nimport { SessionManager } from './utils/session';\nimport { createAdtError } from './errors';\n\n// Re-export HttpAdapter type for consumers\n/**\n * Extended ADT connection config with plugins\n */\nexport interface AdtAdapterConfig extends AdtConnectionConfig {\n /** Response plugins for intercepting and transforming responses */\n plugins?: ResponsePlugin[];\n /**\n * Callback when session expires (SAML redirect detected)\n * Return new cookie header to retry the request, or throw to abort\n */\n onSessionExpired?: () => Promise<string>;\n}\n\n/**\n * Create ADT HTTP adapter with Basic or SAML Authentication and plugin support\n */\n/** Extended adapter with ETag management */\nexport interface AdtHttpAdapter extends HttpAdapter {\n /** Clear cached ETag for a specific URL, or all ETags if no URL given */\n clearETag(url?: string): void;\n}\n\nexport function createAdtAdapter(config: AdtAdapterConfig): AdtHttpAdapter {\n const {\n baseUrl,\n username,\n password,\n cookieHeader,\n authorizationHeader,\n client,\n language,\n logger,\n plugins = [],\n onSessionExpired,\n } = config;\n\n // Determine auth method\n const isSamlAuth = !!cookieHeader;\n\n // Build Authorization header:\n // 1. Explicit authorizationHeader (e.g., \"Bearer <token>\" for OAuth)\n // 2. Basic Auth header (username/password) when not using SAML\n const basicCredentials = `${username}:${password}`;\n const authHeader =\n authorizationHeader ??\n (isSamlAuth\n ? undefined\n : `Basic ${Buffer.from(basicCredentials).toString('base64')}`);\n\n // Create session manager for stateful sessions\n const sessionManager = new SessionManager(logger);\n\n // Inject SAML cookie if provided\n if (cookieHeader) {\n sessionManager.injectCookie(cookieHeader);\n }\n\n return {\n async request<TResponse = unknown>(\n options: HttpRequestOptions,\n ): Promise<TResponse> {\n logger?.debug('=== ADAPTER REQUEST START ===');\n logger?.debug('options.url:', options.url);\n logger?.debug('options.method:', options.method);\n logger?.debug('options.body:', options.body ? 'present' : 'undefined');\n logger?.debug(\n 'options.bodySchema:',\n options.bodySchema ? 'present' : 'undefined',\n );\n\n // Build full URL\n const url = new URL(options.url, baseUrl);\n\n // Add query parameters\n if (options.query) {\n Object.entries(options.query).forEach(([key, value]) => {\n url.searchParams.append(key, String(value));\n });\n }\n\n // Add SAP client and language if provided\n if (client) {\n url.searchParams.append('sap-client', client);\n }\n if (language) {\n url.searchParams.append('sap-language', language);\n }\n\n // For write operations, ensure CSRF token is initialized\n // Uses the 3-step Eclipse ADT security session flow:\n // create session → fetch CSRF → delete session\n const needsCsrf = ['POST', 'PUT', 'DELETE', 'PATCH'].includes(\n options.method.toUpperCase(),\n );\n if (needsCsrf && !sessionManager.hasCsrfToken()) {\n logger?.debug(\n 'Adapter: Initializing CSRF token before write operation',\n );\n await sessionManager.initializeCsrf(\n baseUrl,\n authHeader, // undefined for cookie auth — SessionManager uses stored cookies\n client,\n language,\n );\n }\n\n // Prepare headers (pass URL for ETag lookup on PUT/PATCH)\n const headers: Record<string, string> = {\n 'X-sap-adt-sessiontype': sessionManager.getSessionTypeHeader(),\n ...sessionManager.getRequestHeaders(options.method, url.pathname),\n ...options.headers,\n };\n\n // Add Authorization header only for Basic Auth\n if (authHeader) {\n headers.Authorization = authHeader;\n }\n\n // Get schemas from speci's standard fields\n // Check if bodySchema is Serializable (has build method from adt-schemas)\n let bodySerializableSchema:\n | { build: (data: unknown) => string }\n | undefined;\n if (options.bodySchema && typeof options.bodySchema === 'object') {\n if (\n 'build' in options.bodySchema &&\n typeof options.bodySchema.build === 'function'\n ) {\n bodySerializableSchema = options.bodySchema as {\n build: (data: unknown) => string;\n };\n }\n }\n\n // Extract response schema from responses object (passed by speci)\n // Schemas from adt-schemas have parse() method (Serializable interface)\n let serializableSchema: { parse: (xml: string) => unknown } | undefined;\n if (options.responses) {\n const schema200 = options.responses[200];\n logger?.debug('Schema detection:', {\n hasSchema: !!schema200,\n type: typeof schema200,\n keys:\n schema200 && typeof schema200 === 'object'\n ? Object.keys(schema200)\n : [],\n });\n if (\n schema200 &&\n typeof schema200 === 'object' &&\n 'parse' in schema200 &&\n typeof schema200.parse === 'function'\n ) {\n serializableSchema = schema200 as { parse: (xml: string) => unknown };\n }\n }\n\n // Build request body using schema if available\n let requestBody: string | undefined;\n const body = options.body;\n\n logger?.debug('Request URL: ' + options.url);\n logger?.debug('Request method: ' + options.method);\n logger?.debug(\n 'options.body: ' +\n (options.body\n ? JSON.stringify(options.body).substring(0, 500)\n : 'UNDEFINED'),\n );\n logger?.debug(\n 'options.bodySchema: ' + (options.bodySchema ? 'present' : 'undefined'),\n );\n logger?.debug(\n 'bodySerializableSchema: ' +\n (bodySerializableSchema ? 'present' : 'undefined'),\n );\n logger?.debug('Body type: ' + typeof body);\n logger?.debug(\n 'Body value: ' +\n (body ? JSON.stringify(body).substring(0, 500) : 'UNDEFINED/NULL'),\n );\n if (body !== undefined && body !== null) {\n if (typeof body === 'string') {\n requestBody = body;\n logger?.debug('Body: using raw string');\n logger?.debug(\n 'Body content (first 200 chars):',\n requestBody.substring(0, 200),\n );\n } else if (bodySerializableSchema) {\n // Use Serializable schema's build method (from adt-schemas)\n try {\n logger?.debug(\n 'Calling bodySerializableSchema.build with body: ' +\n JSON.stringify(body).substring(0, 300),\n );\n requestBody = bodySerializableSchema.build(body);\n logger?.debug('Body: serialized using Serializable.build()');\n logger?.debug('Body output type: ' + typeof requestBody);\n logger?.debug('Body output length: ' + requestBody?.length);\n logger?.debug(\n 'Body content (first 500 chars): ' +\n requestBody?.substring(0, 500),\n );\n } catch (buildError) {\n logger?.error(\n 'Schema build() threw error: ' +\n (buildError instanceof Error\n ? buildError.message\n : String(buildError)),\n );\n logger?.error(\n 'Stack: ' +\n (buildError instanceof Error ? buildError.stack : 'N/A'),\n );\n throw buildError;\n }\n } else {\n requestBody = JSON.stringify(body);\n logger?.debug('Body: serialized as JSON');\n }\n }\n\n // Make request\n logger?.debug(`HTTP ${options.method} ${url.toString()}`);\n {\n const safeHeaders = {\n ...headers,\n // Mask sensitive headers for security\n ...(headers.Authorization\n ? { Authorization: headers.Authorization.substring(0, 10) + '***' }\n : {}),\n ...(headers.Cookie ? { Cookie: '***' } : {}),\n ...(headers['x-csrf-token'] ? { 'x-csrf-token': '***' } : {}),\n };\n logger?.debug(\n 'Request headers: ' + JSON.stringify(safeHeaders, null, 2),\n );\n }\n const response = await fetch(url.toString(), {\n method: options.method,\n headers,\n body: requestBody,\n });\n\n // Process response for session management (cookies, CSRF, ETags)\n sessionManager.processResponse(response, url.pathname);\n logger?.debug(`Response: ${response.status} ${response.statusText}`);\n logger?.debug(\n 'Response headers: ' +\n JSON.stringify(\n Object.fromEntries(response.headers.entries()),\n null,\n 2,\n ),\n );\n\n // Check for HTTP errors\n if (!response.ok) {\n const errorBody = await response.text();\n\n // Create structured ADT error with parsed exception details\n const adtError = createAdtError(\n response.status,\n response.statusText,\n url.toString(),\n options.method,\n errorBody,\n );\n\n // For 412 Precondition Failed, include ETag diagnostic info in error\n if (response.status === 412) {\n const sentETag = headers['If-Match'] ?? '(none)';\n const diag = `[If-Match: ${sentETag}, URL: ${url.pathname}]`;\n logger?.error(\n `ETag mismatch: ${options.method} ${url.pathname} sent If-Match: ${sentETag}`,\n );\n // Append diagnostic to the error message so it surfaces in error handlers\n Object.defineProperty(adtError, 'message', {\n value: `${adtError.message} ${diag}`,\n writable: true,\n configurable: true,\n });\n }\n\n logger?.error(\n `Request failed - ${adtError.message} (${options.method} ${url.toString()})`,\n );\n if (adtError.exception) {\n logger?.error(\n `ADT Exception: namespace=${adtError.exception.namespace}, type=${adtError.exception.type}`,\n );\n }\n if (errorBody && !adtError.exception) {\n // Log raw body only if we couldn't parse it as ADT exception\n logger?.error(`Response body: ${errorBody}`);\n }\n\n // On 403, only clear session if it looks like an auth/CSRF issue.\n // Lock conflicts (\"currently editing\") are NOT session problems —\n // clearing the session would destroy the security session context\n // and break all subsequent lock/unlock operations.\n if (response.status === 403) {\n const isLockConflict =\n adtError.exception?.type === 'ExceptionResourceNoAccess' &&\n (adtError.message.includes('currently editing') ||\n adtError.message.includes('is being edited'));\n if (!isLockConflict) {\n sessionManager.clear();\n logger?.warn('Session cleared due to 403 Forbidden response');\n } else {\n logger?.warn(\n 'Lock conflict (403) — session preserved (object locked by another session)',\n );\n }\n }\n throw adtError;\n }\n\n // Parse response\n const contentType = response.headers.get('content-type') || '';\n const rawText = await response.text();\n\n // Detect SAML redirect (server returns 200 OK with HTML containing SAMLRequest form)\n // This happens when the session cookie has expired\n if (\n contentType.includes('text/html') &&\n rawText.includes('SAMLRequest')\n ) {\n sessionManager.clear();\n logger?.warn('SAML redirect detected - session expired');\n\n // If callback provided, try to re-authenticate and retry the request\n if (onSessionExpired) {\n logger?.info('Attempting automatic re-authentication...');\n try {\n const newCookie = await onSessionExpired();\n sessionManager.injectCookie(newCookie);\n logger?.info('Re-authentication successful, retrying request...');\n // Retry the request with new cookie\n return this.request(options);\n } catch (refreshError) {\n logger?.error('Re-authentication failed');\n throw new Error('Session expired - re-authentication failed', {\n cause: refreshError,\n });\n }\n }\n\n throw new Error('Session expired - SAML re-authentication required');\n }\n let data: any;\n\n // Check for JSON content (including vendor-specific types like application/vnd.sap.*+json)\n if (\n contentType.includes('application/json') ||\n contentType.includes('+json')\n ) {\n data = JSON.parse(rawText);\n } else if (contentType.includes('text/') || contentType.includes('xml')) {\n // If serializable schema available (has parse method), use it\n if (serializableSchema && contentType.includes('xml')) {\n data = serializableSchema.parse(rawText);\n } else {\n data = rawText;\n }\n } else {\n data = rawText;\n }\n\n // Apply plugins if any\n if (plugins.length > 0) {\n const context: ResponseContext = {\n rawText,\n parsedData: data,\n url: url.toString(),\n method: options.method,\n contentType,\n };\n\n // Run plugins in sequence\n for (const plugin of plugins) {\n const result = await plugin.process(context);\n // Plugin can modify the data\n if (result !== undefined) {\n data = result;\n context.parsedData = result;\n }\n }\n }\n\n // Return just the data (matching Speci's HttpAdapter interface)\n return data as TResponse;\n },\n\n clearETag(url?: string) {\n sessionManager.clearETag(url);\n },\n };\n}\nexport type { HttpAdapter } from '@abapify/adt-contracts';\n","/**\n * Transport Service - Business logic layer for CTS operations\n *\n * Bridges the gap between:\n * - Contracts (granular HTTP operations)\n * - ADK (object-focused logic)\n *\n * Provides orchestrated transport operations for CLI and SDK consumers.\n */\n\nimport type { AdtClientType } from '@abapify/adt-contracts';\n\n/**\n * Transport data structure (simplified for service consumers)\n */\nexport interface Transport {\n number: string;\n desc?: string;\n owner?: string;\n status?: string;\n type?: string;\n target?: string;\n tasks?: TransportTask[];\n}\n\nexport interface TransportTask {\n number: string;\n owner?: string;\n status?: string;\n}\n\n/**\n * Options for creating a transport\n */\nexport interface CreateTransportOptions {\n description: string;\n type?: 'K' | 'W'; // K = Workbench, W = Customizing\n target?: string;\n project?: string;\n owner?: string;\n}\n\n/**\n * Options for listing transports\n */\nexport interface ListTransportsOptions {\n targets?: string;\n configUri?: string;\n}\n\n/**\n * Transport Service - orchestrates CTS operations\n */\nexport class TransportService {\n constructor(private readonly adt: AdtClientType) {}\n\n /**\n * List transport requests\n */\n async list(options?: ListTransportsOptions): Promise<Transport[]> {\n const response = await this.adt.cts.transportrequests.list(options);\n\n // Transform contract response to service format\n // The response structure is: { request: [...] } or single request\n const requests = this.extractRequests(response);\n\n return requests.map((req) => this.mapToTransport(req));\n }\n\n /**\n * Get a single transport by number\n */\n async get(trkorr: string): Promise<Transport> {\n const response = await this.adt.cts.transportrequests.get(trkorr);\n\n // Single transport response may be shaped as\n // { root: { request: { ... } } } (XML — ts-xsd keeps the root wrapper)\n // { request: { ... } } (when the root is already unwrapped)\n // { ... } (flat)\n const data = response as Record<string, unknown>;\n const unwrapped =\n data?.root && typeof data.root === 'object'\n ? (data.root as Record<string, unknown>)\n : data;\n const request =\n (unwrapped?.request as Record<string, unknown> | undefined) ?? unwrapped;\n return this.mapToTransport(request);\n }\n\n /**\n * Create a new transport request\n *\n * Note: Full create implementation requires XML body building.\n * For now, this is a placeholder that throws a helpful error.\n * Use ADK's AdkTransportRequest.create() for full functionality.\n */\n async create(_options: CreateTransportOptions): Promise<Transport> {\n // NOTE: XML body building for transport creation not yet implemented\n // The contract exists but we need to build the request XML\n throw new Error(\n 'Transport creation via service layer not yet implemented. ' +\n \"Use ADK's AdkTransportRequest.create() for now.\",\n );\n }\n\n /**\n * Delete a transport request\n */\n async delete(trkorr: string): Promise<void> {\n await this.adt.cts.transportrequests.delete(trkorr);\n }\n\n /**\n * Release a transport request\n *\n * Note: Release requires POST with specific action parameter.\n * This is a placeholder for the full implementation.\n */\n async release(_trkorr: string): Promise<void> {\n // NOTE: Release action not yet implemented via service layer\n throw new Error('Transport release via service layer not yet implemented.');\n }\n\n /**\n * Extract requests array from various response formats\n */\n private extractRequests(response: unknown): unknown[] {\n if (!response) return [];\n\n const data = response as Record<string, unknown>;\n\n // Format 1: { request: [...] }\n if (Array.isArray(data.request)) {\n return data.request;\n }\n\n // Format 2: { request: { ... } } (single)\n if (data.request && typeof data.request === 'object') {\n return [data.request];\n }\n\n // Format 3: Direct array\n if (Array.isArray(response)) {\n return response;\n }\n\n // Format 4: Root wrapper { root: { request: [...] } }\n if (data.root && typeof data.root === 'object') {\n return this.extractRequests(data.root);\n }\n\n return [];\n }\n\n /**\n * Map raw request data to Transport interface\n */\n private mapToTransport(req: unknown): Transport {\n const data = req as Record<string, unknown>;\n\n return {\n number: String(\n (data.trkorr as string | undefined) ??\n (data.number as string | undefined) ??\n '',\n ),\n desc:\n (data.as4text as string | undefined) ??\n (data.desc as string | undefined),\n owner:\n (data.as4user as string | undefined) ??\n (data.owner as string | undefined),\n status:\n (data.trstatus as string | undefined) ??\n (data.status as string | undefined),\n type:\n (data.trfunction as string | undefined) ??\n (data.type as string | undefined),\n target:\n (data.tarsystem as string | undefined) ??\n (data.target as string | undefined),\n tasks: this.extractTasks(data),\n };\n }\n\n /**\n * Extract tasks from transport data\n */\n private extractTasks(data: Record<string, unknown>): TransportTask[] {\n const tasks = data.task ?? data.tasks;\n\n if (!tasks) return [];\n\n const taskArray = Array.isArray(tasks) ? tasks : [tasks];\n\n return taskArray.map((task) => {\n const t = task as Record<string, unknown>;\n return {\n number: String(\n (t.trkorr as string | undefined) ??\n (t.number as string | undefined) ??\n '',\n ),\n owner:\n (t.as4user as string | undefined) ?? (t.owner as string | undefined),\n status:\n (t.trstatus as string | undefined) ??\n (t.status as string | undefined),\n };\n });\n }\n}\n\n/**\n * Create transport service instance\n */\nexport function createTransportService(adt: AdtClientType): TransportService {\n return new TransportService(adt);\n}\n","/**\n * User Service - Business logic layer for system user operations\n *\n * Provides user lookup and search by orchestrating:\n * - system.users contract (Atom feed responses)\n * - core.http.systeminformation contract (current user info)\n */\n\nimport type { AdtClientType } from '@abapify/adt-contracts';\n\n/** Normalized user info from Atom feed entries */\nexport interface UserInfo {\n username: string;\n fullName?: string;\n uri?: string;\n}\n\n/** Current authenticated user info from systeminformation */\nexport interface CurrentUserInfo {\n userName: string;\n userFullName?: string;\n systemID: string;\n client: string;\n}\n\n/** Extract entry array from atomFeed parsed response */\nfunction extractEntries(\n data: unknown,\n): { id?: string; title?: string; link?: { href: string }[] }[] {\n const feed = data as Record<string, unknown>;\n const feedData = feed.feed as Record<string, unknown> | undefined;\n if (!feedData) return [];\n\n const rawEntries = feedData.entry;\n if (!rawEntries) return [];\n return Array.isArray(rawEntries) ? rawEntries : [rawEntries];\n}\n\n/** Map raw Atom entries to normalized UserInfo */\nfunction toUserInfoList(\n entries: { id?: string; title?: string; link?: { href: string }[] }[],\n): UserInfo[] {\n return entries.map((e) => ({\n username: e.id ?? '',\n fullName: e.title,\n uri: e.link?.[0]?.href,\n }));\n}\n\n/**\n * User Service - orchestrates user lookup operations\n */\nexport class UserService {\n constructor(private readonly adt: AdtClientType) {}\n\n /** Get the currently authenticated user via systeminformation */\n async getCurrentUser(): Promise<CurrentUserInfo> {\n const sysInfo = await this.adt.core.http.systeminformation.getSystemInfo();\n return {\n userName: sysInfo.userName ?? '',\n userFullName: sysInfo.userFullName,\n systemID: sysInfo.systemID ?? '',\n client: sysInfo.client ?? '',\n };\n }\n\n /** Look up a specific user by exact username */\n async getUserByName(username: string): Promise<UserInfo[]> {\n const normalized = username.trim().toUpperCase();\n if (!normalized) {\n throw new Error('username is required');\n }\n const result = await this.adt.system.users.get(normalized);\n return toUserInfoList(extractEntries(result));\n }\n\n /** Search users by wildcard query */\n async searchUsers(query: string, maxcount = 50): Promise<UserInfo[]> {\n const normalizedQuery = query.trim();\n if (!normalizedQuery) {\n throw new Error('query is required');\n }\n if (!Number.isInteger(maxcount) || maxcount <= 0) {\n throw new Error('maxcount must be a positive integer');\n }\n const result = await this.adt.system.users.search({\n querystring: normalizedQuery,\n maxcount,\n });\n return toUserInfoList(extractEntries(result));\n }\n}\n\nexport function createUserService(adt: AdtClientType): UserService {\n return new UserService(adt);\n}\n","/**\n * ADT Client V2 - Contract-Based Architecture\n *\n * Provides typed access to SAP ADT REST APIs:\n * - client.adt.* - Typed ADT REST contracts (speci-generated)\n * - client.fetch() - Generic HTTP utility for raw requests\n *\n * Note: Business logic (transport management, etc.) has moved to @abapify/adk\n * Use AdkTransportRequest for transport operations.\n */\n\nimport { createAdtAdapter, type AdtAdapterConfig } from './adapter';\nimport {\n createAdtClient as createAdtContractClient,\n type AdtClientType,\n type HttpRequestOptions,\n} from '@abapify/adt-contracts';\nimport { createTransportService, type TransportService } from './services';\n\n/**\n * Fetch options for generic HTTP requests\n */\nexport interface FetchOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n headers?: Record<string, string>;\n body?: string;\n}\n\n/**\n * Create ADT client with contract-based architecture\n *\n * @example\n * // Basic usage\n * const client = createAdtClient({\n * baseUrl: 'https://sap-system.com:8000',\n * username: 'user',\n * password: 'pass',\n * client: '100'\n * });\n *\n * // Contract access\n * const session = await client.adt.core.http.sessions.getSession();\n * const transport = await client.adt.cts.transportrequests.get('S0DK900001');\n *\n * // Generic fetch utility\n * const response = await client.fetch('/sap/bc/adt/arbitrary/endpoint', {\n * method: 'GET',\n * headers: { Accept: 'application/xml' }\n * });\n *\n * // For business logic, use ADK:\n * // import { initializeAdk, AdkTransportRequest } from '@abapify/adk';\n * // initializeAdk(client);\n * // const transport = await AdkTransportRequest.get('S0DK900001');\n */\n\n// Re-export AdtClientType from adt-contracts for consumers\nexport type { AdtClientType } from '@abapify/adt-contracts';\n\n/**\n * Services layer - business logic orchestration\n */\ninterface AdtServices {\n transports: TransportService;\n}\n\n// Return type explicitly defined to avoid TS7056 \"exceeds maximum length\" error\ninterface AdtClientReturn {\n adt: AdtClientType;\n services: AdtServices;\n fetch: (url: string, options?: FetchOptions) => Promise<unknown>;\n /** Clear cached ETag for a specific URL, or all ETags if no URL given */\n clearETag: (url?: string) => void;\n}\n\nexport function createAdtClient(config: AdtAdapterConfig): AdtClientReturn {\n const adapter = createAdtAdapter(config);\n const adtClient = createAdtContractClient({\n baseUrl: config.baseUrl,\n adapter,\n });\n\n // Create fetch function for raw HTTP access\n const fetchFn = async (\n url: string,\n options?: FetchOptions,\n ): Promise<unknown> => {\n const method = options?.method || 'GET';\n const headers = options?.headers || {};\n const body = options?.body;\n\n const requestOptions: HttpRequestOptions = {\n method,\n url,\n headers,\n body,\n };\n\n return adapter.request<unknown>(requestOptions);\n };\n\n // Create services layer\n const services: AdtServices = {\n transports: createTransportService(adtClient),\n };\n\n return {\n /**\n * Typed ADT REST contracts\n * Direct access to speci-generated client methods\n */\n adt: adtClient,\n\n /**\n * Services layer - business logic orchestration\n * Higher-level operations that combine multiple contract calls\n */\n services,\n\n /**\n * Generic fetch utility for arbitrary ADT endpoints\n * Useful for debugging, testing, or accessing undocumented APIs\n *\n * @param url - The ADT endpoint path (e.g., '/sap/bc/adt/core/http/sessions')\n * @param options - Request options (method, headers, body)\n * @returns Raw response\n */\n fetch: fetchFn,\n\n /**\n * Clear cached ETag for a specific URL, or all ETags if no URL given.\n * Use after operations that may invalidate server-side ETags.\n */\n clearETag: (url?: string) => adapter.clearETag(url),\n };\n}\n\nexport type AdtClient = ReturnType<typeof createAdtClient>;\n","/**\n * Logging Plugin - Logs requests and responses\n */\n\nimport type { ResponsePlugin, ResponseContext } from './types';\n\nexport type LogFunction = (message: string, data?: any) => void;\n\n/**\n * Logging plugin - logs requests and responses\n */\nexport class LoggingPlugin implements ResponsePlugin {\n name = 'logging';\n\n constructor(private readonly logger: LogFunction = console.log) {}\n\n process(context: ResponseContext): unknown {\n this.logger(`[${context.method}] ${context.url}`, {\n contentType: context.contentType,\n hasParsedData: !!context.parsedData,\n rawSize: context.rawText.length,\n });\n return context.parsedData;\n }\n}\n","/**\n * File Logging Plugin - Saves HTTP responses to files\n */\n\nimport { mkdirSync, writeFileSync } from 'fs';\nimport { dirname, join } from 'path';\nimport type { ResponsePlugin, ResponseContext } from './types';\nimport type { Logger } from '@abapify/logger';\n\nexport interface FileLoggingConfig {\n outputDir: string;\n writeMetadata?: boolean;\n logger?: Logger;\n}\n\n/**\n * Plugin that logs HTTP responses to files\n * Similar to v1's FileLogger functionality\n */\nexport class FileLoggingPlugin implements ResponsePlugin {\n name = 'file-logging';\n\n constructor(private readonly config: FileLoggingConfig) {}\n\n process(context: ResponseContext): unknown {\n try {\n // Generate file path based on endpoint\n const filePath = this.generateFilePath(context.url, context.method);\n const fullPath = join(this.config.outputDir, filePath);\n\n // Ensure directory exists\n mkdirSync(dirname(fullPath), { recursive: true });\n\n // Write response content\n writeFileSync(fullPath, context.rawText, 'utf8');\n this.config.logger?.trace(`Wrote response to: ${fullPath}`);\n\n // Write metadata if enabled\n if (this.config.writeMetadata) {\n const metadataPath = this.getMetadataPath(fullPath);\n const metadata = {\n url: context.url,\n method: context.method,\n contentType: context.contentType,\n timestamp: new Date().toISOString(),\n size: context.rawText.length,\n };\n writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), 'utf8');\n this.config.logger?.trace(`Wrote metadata to: ${metadataPath}`);\n }\n } catch (error) {\n this.config.logger?.error(\n `Failed to write response file: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n return context.parsedData;\n }\n\n /**\n * Generate file path for HTTP response\n * Converts URL to fixture-style path structure\n */\n private generateFilePath(url: string, method: string): string {\n // Parse URL\n const urlObj = new URL(url);\n let path = urlObj.pathname;\n\n // Remove /sap/bc/adt prefix\n path = path.replace(/^\\/sap\\/bc\\/adt\\/?/, '');\n\n // Handle root/discovery\n if (!path || path === 'discovery') {\n return './adt/core/discovery.xml';\n }\n\n // Parse segments\n const [basePath] = path.split('?');\n const segments = basePath.split('/').filter((s) => s);\n\n // Check if source endpoint (no extension)\n const sourceTypes = [\n 'main',\n 'definitions',\n 'implementations',\n 'macros',\n 'testclasses',\n ];\n const lastSegment = segments[segments.length - 1];\n\n if (sourceTypes.includes(lastSegment)) {\n return `./adt/${segments.join('/')}`;\n }\n\n // Generate request ID\n const requestId =\n Date.now().toString() + Math.random().toString(36).slice(2, 7);\n\n // Build directory path\n let dirPath = `./adt/${segments.join('/')}`;\n\n // Add query string to directory if present\n if (urlObj.search) {\n const sanitizedQuery = urlObj.search\n .slice(1)\n .replaceAll(/[^a-zA-Z0-9_-]/g, '_');\n dirPath += `/${sanitizedQuery}`;\n }\n\n // Use method in filename if not GET\n const methodPrefix = method !== 'GET' ? `${method.toLowerCase()}-` : '';\n\n return `${dirPath}/${methodPrefix}${requestId}-response.xml`;\n }\n\n /**\n * Get metadata file path for a response file\n */\n private getMetadataPath(responsePath: string): string {\n if (/-response\\.xml$/.test(responsePath)) {\n return responsePath.replace(/-response\\.xml$/, '-metadata.json');\n }\n if (/\\.xml$/.test(responsePath)) {\n return responsePath.replace(/\\.xml$/, '.metadata.json');\n }\n return `${responsePath}.metadata.json`;\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abapify/adt-client",
3
- "version": "0.3.6",
3
+ "version": "0.4.1",
4
4
  "description": "Minimalistic speci-based ADT client for SAP ABAP Development Tools",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -17,9 +17,9 @@
17
17
  "README.md"
18
18
  ],
19
19
  "dependencies": {
20
- "@abapify/logger": "0.3.6",
21
- "@abapify/adt-contracts": "0.3.6",
22
- "@abapify/adt-schemas": "0.3.6"
20
+ "@abapify/logger": "0.4.1",
21
+ "@abapify/adt-contracts": "0.4.1",
22
+ "@abapify/adt-schemas": "0.4.1"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",