@abapify/adt-client 0.4.16 → 0.4.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +0 -24
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +10 -20
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { exception } from "@abapify/adt-schemas";
|
|
2
2
|
import { AdtClientType, AdtContract, ClassResponse, CrudContract, CrudContractBase, CrudQueryParams, DataElementResponse, DomainResponse, FunctionGroupResponse, FunctionModuleResponse, HttpAdapter, HttpAdapter as HttpAdapter$1, IncludeResponse, IncludesContractSkeleton, InterfaceResponse, LockOptions, ObjectStructureOptions, Package as PackageResponse, ProgramResponse, RestContract, RestEndpointDescriptor, Serializable, SourceOperations, SourcesContract, StructureResponse, TableResponse, TableTypeResponse, TransportResponse as TransportGetResponse, UnlockOptions, adtContract } from "@abapify/adt-contracts";
|
|
3
3
|
import { Logger, Logger as Logger$1 } from "@abapify/logger";
|
|
4
|
-
//#region src/errors.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Parsed ADT exception structure - derived from ExceptionSchema
|
|
7
6
|
*/
|
|
@@ -60,8 +59,6 @@ declare class AdtError extends Error {
|
|
|
60
59
|
*/
|
|
61
60
|
getProperty(key: string): string | undefined;
|
|
62
61
|
}
|
|
63
|
-
//#endregion
|
|
64
|
-
//#region src/types.d.ts
|
|
65
62
|
interface AdtConnectionConfig {
|
|
66
63
|
baseUrl: string;
|
|
67
64
|
username?: string;
|
|
@@ -82,8 +79,6 @@ interface OperationResult {
|
|
|
82
79
|
success: boolean;
|
|
83
80
|
message?: string;
|
|
84
81
|
}
|
|
85
|
-
//#endregion
|
|
86
|
-
//#region src/plugins/types.d.ts
|
|
87
82
|
/**
|
|
88
83
|
* Plugin Types - Core interfaces for the response plugin system
|
|
89
84
|
*/
|
|
@@ -119,8 +114,6 @@ interface ResponsePlugin {
|
|
|
119
114
|
*/
|
|
120
115
|
process(context: ResponseContext): Promise<unknown> | unknown;
|
|
121
116
|
}
|
|
122
|
-
//#endregion
|
|
123
|
-
//#region src/adapter.d.ts
|
|
124
117
|
/**
|
|
125
118
|
* Extended ADT connection config with plugins
|
|
126
119
|
*/
|
|
@@ -159,8 +152,6 @@ interface AdtHttpAdapter extends HttpAdapter$1 {
|
|
|
159
152
|
readTextBounded(options: BoundedTextRequestOptions, maxBytes: number): Promise<string>;
|
|
160
153
|
}
|
|
161
154
|
declare function createAdtAdapter(config: AdtAdapterConfig): AdtHttpAdapter;
|
|
162
|
-
//#endregion
|
|
163
|
-
//#region src/services/transports.d.ts
|
|
164
155
|
/**
|
|
165
156
|
* Transport data structure (simplified for service consumers)
|
|
166
157
|
*/
|
|
@@ -256,8 +247,6 @@ declare class TransportService {
|
|
|
256
247
|
* Create transport service instance
|
|
257
248
|
*/
|
|
258
249
|
declare function createTransportService(adt: AdtClientType): TransportService;
|
|
259
|
-
//#endregion
|
|
260
|
-
//#region src/services/users.d.ts
|
|
261
250
|
/** Normalized user info from Atom feed entries */
|
|
262
251
|
interface UserInfo {
|
|
263
252
|
username: string;
|
|
@@ -285,8 +274,6 @@ declare class UserService {
|
|
|
285
274
|
searchUsers(query: string, maxcount?: number): Promise<UserInfo[]>;
|
|
286
275
|
}
|
|
287
276
|
declare function createUserService(adt: AdtClientType): UserService;
|
|
288
|
-
//#endregion
|
|
289
|
-
//#region src/services/source-history.d.ts
|
|
290
277
|
type SourceHistoryProtocolErrorCode = 'SOURCE_HISTORY_URI_UNSAFE' | 'SOURCE_VERSION_FEED_INVALID' | 'SOURCE_VERSION_ID_MISSING' | 'SOURCE_VERSION_CONTENT_LINK_MISSING';
|
|
291
278
|
declare class SourceHistoryProtocolError extends Error {
|
|
292
279
|
readonly code: SourceHistoryProtocolErrorCode;
|
|
@@ -327,8 +314,6 @@ declare class SourceHistoryService {
|
|
|
327
314
|
readVersionSourceBounded(sourceUri: string, maxBytes: number): Promise<string>;
|
|
328
315
|
}
|
|
329
316
|
declare function createSourceHistoryService(adt: AdtClientType, boundedSourceReader?: BoundedSourceReader): SourceHistoryService;
|
|
330
|
-
//#endregion
|
|
331
|
-
//#region src/client.d.ts
|
|
332
317
|
/**
|
|
333
318
|
* Fetch options for generic HTTP requests
|
|
334
319
|
*/
|
|
@@ -358,16 +343,12 @@ interface AdtClientReturn {
|
|
|
358
343
|
}
|
|
359
344
|
declare function createAdtClient(config: AdtAdapterConfig): AdtClientReturn;
|
|
360
345
|
type AdtClient = ReturnType<typeof createAdtClient>;
|
|
361
|
-
//#endregion
|
|
362
|
-
//#region src/cancellation.d.ts
|
|
363
346
|
/**
|
|
364
347
|
* Runs an ADT operation with an execution-scoped abort signal. Async local
|
|
365
348
|
* storage keeps concurrent clients and invocations isolated without mutating
|
|
366
349
|
* shared client state.
|
|
367
350
|
*/
|
|
368
351
|
declare function runWithAdtAbortSignal<T>(signal: AbortSignal, operation: () => Promise<T>): Promise<T>;
|
|
369
|
-
//#endregion
|
|
370
|
-
//#region src/plugins/logging.d.ts
|
|
371
352
|
type LogFunction = (message: string, data?: any) => void;
|
|
372
353
|
/**
|
|
373
354
|
* Logging plugin - logs requests and responses
|
|
@@ -378,8 +359,6 @@ declare class LoggingPlugin implements ResponsePlugin {
|
|
|
378
359
|
constructor(logger?: LogFunction);
|
|
379
360
|
process(context: ResponseContext): unknown;
|
|
380
361
|
}
|
|
381
|
-
//#endregion
|
|
382
|
-
//#region src/plugins/file-logging.d.ts
|
|
383
362
|
interface FileLoggingConfig {
|
|
384
363
|
outputDir: string;
|
|
385
364
|
writeMetadata?: boolean;
|
|
@@ -404,8 +383,6 @@ declare class FileLoggingPlugin implements ResponsePlugin {
|
|
|
404
383
|
*/
|
|
405
384
|
private getMetadataPath;
|
|
406
385
|
}
|
|
407
|
-
//#endregion
|
|
408
|
-
//#region src/utils/session.d.ts
|
|
409
386
|
/**
|
|
410
387
|
* Fetch function injected into SessionManager so the adapter can route
|
|
411
388
|
* CSRF handshake requests through the same Undici dispatcher (and
|
|
@@ -573,6 +550,5 @@ declare class SessionManager {
|
|
|
573
550
|
*/
|
|
574
551
|
injectCookie(cookieString: string): void;
|
|
575
552
|
}
|
|
576
|
-
//#endregion
|
|
577
553
|
export { type AdtAdapterConfig, type AdtClient, type AdtConnectionConfig, type AdtContract, type AdtError, type AdtHttpAdapter, AdtResponseTooLargeError, type BoundedSourceReader, type BoundedTextFetchOptions, type BoundedTextRequestOptions, type ClassResponse, CookieStore, type CreateTransportOptions, type CrudContract, type CrudContractBase, type CrudQueryParams, CsrfTokenManager, type CurrentUserInfo, type DataElementResponse, type DomainResponse, type FileLoggingConfig, FileLoggingPlugin, type FunctionGroupResponse, type FunctionModuleResponse, type HttpAdapter, type IncludeResponse, type IncludesContractSkeleton, type InterfaceResponse, type ListTransportsOptions, type LockHandle, type LockOptions, type LogFunction, type Logger, LoggingPlugin, type ObjectStructureOptions, type OperationResult, type PackageResponse, type ProgramResponse, type ResponseContext, type ResponsePlugin, type RestContract, type RestEndpointDescriptor, type Serializable, SessionManager, SourceHistoryProtocolError, type SourceHistoryProtocolErrorCode, SourceHistoryService, type SourceOperations, type SourceVersionRef, type SourceVersionReference, SourceVersionTooLargeError, type SourcesContract, type StructureResponse, type TableResponse, type TableTypeResponse, type Transport, type TransportGetResponse, TransportService, type TransportTask, type UnlockOptions, type UserInfo, UserService, adtContract, assertAdtUri, createAdtAdapter, createAdtClient, createSourceHistoryService, createTransportService, createUserService, normalizeSourceVersionFeed, runWithAdtAbortSignal };
|
|
578
554
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/plugins/types.ts","../src/adapter.ts","../src/services/transports.ts","../src/services/users.ts","../src/services/source-history.ts","../src/client.ts","../src/cancellation.ts","../src/plugins/logging.ts","../src/plugins/file-logging.ts","../src/utils/session.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":["Logger","HttpAdapter","Logger","Logger"],"sources":["../src/errors.ts","../src/types.ts","../src/plugins/types.ts","../src/adapter.ts","../src/services/transports.ts","../src/services/users.ts","../src/services/source-history.ts","../src/client.ts","../src/cancellation.ts","../src/plugins/logging.ts","../src/plugins/file-logging.ts","../src/utils/session.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { adtContract, createAdtClient as createAdtClient$1 } from "@abapify/adt-
|
|
|
5
5
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// src/utils/session.ts
|
|
9
9
|
/**
|
|
10
10
|
* Cookie Store - Manages HTTP cookies for stateful sessions
|
|
11
11
|
*/
|
|
@@ -543,8 +543,7 @@ var SessionManager = class SessionManager {
|
|
|
543
543
|
}
|
|
544
544
|
};
|
|
545
545
|
|
|
546
|
-
|
|
547
|
-
//#region src/errors.ts
|
|
546
|
+
// src/errors.ts
|
|
548
547
|
/**
|
|
549
548
|
* ADT Error Handling
|
|
550
549
|
*
|
|
@@ -659,8 +658,7 @@ function createAdtError(status, statusText, url, method, rawBody) {
|
|
|
659
658
|
});
|
|
660
659
|
}
|
|
661
660
|
|
|
662
|
-
|
|
663
|
-
//#region src/cancellation.ts
|
|
661
|
+
// src/cancellation.ts
|
|
664
662
|
const adtAbortSignal = new AsyncLocalStorage();
|
|
665
663
|
/**
|
|
666
664
|
* Runs an ADT operation with an execution-scoped abort signal. Async local
|
|
@@ -675,8 +673,7 @@ function activeAdtAbortSignal() {
|
|
|
675
673
|
return adtAbortSignal.getStore();
|
|
676
674
|
}
|
|
677
675
|
|
|
678
|
-
|
|
679
|
-
//#region src/adapter.ts
|
|
676
|
+
// src/adapter.ts
|
|
680
677
|
/**
|
|
681
678
|
* A response exceeded an explicit byte cap before its body was retained.
|
|
682
679
|
* Deliberately carries size metadata only, never response text.
|
|
@@ -989,8 +986,7 @@ function createAdtAdapter(config) {
|
|
|
989
986
|
};
|
|
990
987
|
}
|
|
991
988
|
|
|
992
|
-
|
|
993
|
-
//#region src/services/transports.ts
|
|
989
|
+
// src/services/transports.ts
|
|
994
990
|
/**
|
|
995
991
|
* Transport Service - orchestrates CTS operations
|
|
996
992
|
*/
|
|
@@ -1108,8 +1104,7 @@ function createTransportService(adt) {
|
|
|
1108
1104
|
return new TransportService(adt);
|
|
1109
1105
|
}
|
|
1110
1106
|
|
|
1111
|
-
|
|
1112
|
-
//#region src/services/users.ts
|
|
1107
|
+
// src/services/users.ts
|
|
1113
1108
|
/** Extract entry array from atomFeed parsed response */
|
|
1114
1109
|
function extractEntries(data) {
|
|
1115
1110
|
const feedData = data.feed;
|
|
@@ -1165,8 +1160,7 @@ function createUserService(adt) {
|
|
|
1165
1160
|
return new UserService(adt);
|
|
1166
1161
|
}
|
|
1167
1162
|
|
|
1168
|
-
|
|
1169
|
-
//#region src/services/source-history.ts
|
|
1163
|
+
// src/services/source-history.ts
|
|
1170
1164
|
var SourceHistoryProtocolError = class extends Error {
|
|
1171
1165
|
code;
|
|
1172
1166
|
ordinal;
|
|
@@ -1383,8 +1377,7 @@ function createSourceHistoryService(adt, boundedSourceReader) {
|
|
|
1383
1377
|
return new SourceHistoryService(adt, boundedSourceReader);
|
|
1384
1378
|
}
|
|
1385
1379
|
|
|
1386
|
-
|
|
1387
|
-
//#region src/client.ts
|
|
1380
|
+
// src/client.ts
|
|
1388
1381
|
/**
|
|
1389
1382
|
* ADT Client V2 - Contract-Based Architecture
|
|
1390
1383
|
*
|
|
@@ -1449,8 +1442,7 @@ function createAdtClient(config) {
|
|
|
1449
1442
|
};
|
|
1450
1443
|
}
|
|
1451
1444
|
|
|
1452
|
-
|
|
1453
|
-
//#region src/plugins/logging.ts
|
|
1445
|
+
// src/plugins/logging.ts
|
|
1454
1446
|
/**
|
|
1455
1447
|
* Logging plugin - logs requests and responses
|
|
1456
1448
|
*/
|
|
@@ -1470,8 +1462,7 @@ var LoggingPlugin = class {
|
|
|
1470
1462
|
}
|
|
1471
1463
|
};
|
|
1472
1464
|
|
|
1473
|
-
|
|
1474
|
-
//#region src/plugins/file-logging.ts
|
|
1465
|
+
// src/plugins/file-logging.ts
|
|
1475
1466
|
/**
|
|
1476
1467
|
* File Logging Plugin - Saves HTTP responses to files
|
|
1477
1468
|
*/
|
|
@@ -1548,6 +1539,5 @@ var FileLoggingPlugin = class {
|
|
|
1548
1539
|
}
|
|
1549
1540
|
};
|
|
1550
1541
|
|
|
1551
|
-
//#endregion
|
|
1552
1542
|
export { AdtResponseTooLargeError, CookieStore, CsrfTokenManager, FileLoggingPlugin, LoggingPlugin, SessionManager, SourceHistoryProtocolError, SourceHistoryService, SourceVersionTooLargeError, TransportService, UserService, adtContract, assertAdtUri, createAdtAdapter, createAdtClient, createSourceHistoryService, createTransportService, createUserService, normalizeSourceVersionFeed, runWithAdtAbortSignal };
|
|
1553
1543
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["exceptionSchema","undiciFetch","feed","createAdtContractClient"],"sources":["../src/utils/session.ts","../src/errors.ts","../src/cancellation.ts","../src/adapter.ts","../src/services/transports.ts","../src/services/users.ts","../src/services/source-history.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 * Fetch function injected into SessionManager so the adapter can route\n * CSRF handshake requests through the same Undici dispatcher (and\n * headersTimeout) used for the final adapter request. Defaults to the\n * global fetch when no dispatcher is configured.\n */\nexport type SessionFetchFn = (\n input: string | URL,\n init?: RequestInit,\n) => Promise<Response>;\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 static readonly SESSION_PATH_PREFIX =\n '/sap/bc/adt/core/http/sessions/';\n\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(\n private readonly logger?: Logger,\n private readonly fetchFn: SessionFetchFn = globalThis.fetch,\n ) {}\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 * Reject session paths that could send cleanup requests off-target.\n * Only relative paths under the ADT sessions endpoint (or same-origin\n * absolute URLs with that path) are accepted.\n */\n private isTrustedSessionPath({\n baseUrl,\n sessionPath,\n }: {\n baseUrl: string;\n sessionPath: string;\n }): boolean {\n try {\n const parsed = new URL(sessionPath, baseUrl);\n const base = new URL(baseUrl);\n return (\n parsed.origin === base.origin &&\n parsed.pathname.startsWith(SessionManager.SESSION_PATH_PREFIX)\n );\n } catch {\n return false;\n }\n }\n\n private extractTrustedSessionPath({\n baseUrl,\n body,\n }: {\n baseUrl: string;\n body: string;\n }): string | undefined {\n const match = body.match(/href=\"([^\"]*\\/sessions\\/[^\"]*)\"/);\n const sessionPath = match?.[1];\n if (!sessionPath || !this.isTrustedSessionPath({ baseUrl, sessionPath })) {\n this.logger?.warn(\n 'Session: Created security session returned an untrusted path',\n );\n return undefined;\n }\n return sessionPath;\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 private resolveInitializeCsrfArgs(...args: unknown[]): {\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n } {\n if (args.length === 1) {\n const first = args[0];\n if (first !== null) {\n if (typeof first === 'object') {\n return first as {\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n };\n }\n }\n }\n return {\n authHeader: typeof args[0] === 'string' ? args[0] : undefined,\n client: typeof args[1] === 'string' ? args[1] : undefined,\n language: typeof args[2] === 'string' ? args[2] : undefined,\n signal: args[3] instanceof AbortSignal ? args[3] : undefined,\n };\n }\n\n async initializeCsrf(\n baseUrl: string,\n authHeader?: string,\n client?: string,\n language?: string,\n signal?: AbortSignal,\n ): Promise<boolean>;\n async initializeCsrf(\n baseUrl: string,\n options?: {\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n },\n ): Promise<boolean>;\n async initializeCsrf(baseUrl: string, ...args: unknown[]): Promise<boolean> {\n const { authHeader, client, language, signal } =\n this.resolveInitializeCsrfArgs(...args);\n\n signal?.throwIfAborted();\n const sessionsUrl = this.buildSessionsUrl(baseUrl, client, language);\n let sessionPath: string | undefined;\n\n try {\n sessionPath = await this.createSecuritySession(\n baseUrl,\n sessionsUrl,\n authHeader,\n signal,\n );\n if (!sessionPath) return false;\n if (!(await this.acquireCsrfToken(sessionsUrl, authHeader, signal))) {\n this.deleteSecuritySessionWithFallback({\n sessionPath,\n baseUrl,\n authHeader,\n client,\n language,\n }).catch(() => undefined);\n return false;\n }\n\n const acquiredCsrfToken = this.csrfManager.getCached();\n if (acquiredCsrfToken) {\n await this.deleteSecuritySession({\n sessionPath,\n csrfToken: acquiredCsrfToken,\n baseUrl,\n authHeader,\n client,\n signal,\n });\n }\n signal?.throwIfAborted();\n\n return true;\n } catch (error) {\n return this.handleCsrfInitializationError(error, {\n baseUrl,\n authHeader,\n client,\n language,\n sessionPath,\n signal,\n });\n }\n }\n\n private buildSessionsUrl(\n baseUrl: string,\n client?: string,\n language?: string,\n ): URL {\n const url = new URL('/sap/bc/adt/core/http/sessions', baseUrl);\n if (client) url.searchParams.append('sap-client', client);\n if (language) url.searchParams.append('sap-language', language);\n return url;\n }\n\n private buildSessionHeaders(authHeader?: string): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.sap.adt.core.http.session.v3+xml',\n 'X-sap-adt-sessiontype': 'stateful',\n };\n if (authHeader) headers.Authorization = authHeader;\n const cookie = this.cookieStore.getCookieHeader();\n if (cookie) headers.Cookie = cookie;\n return headers;\n }\n\n private async createSecuritySession(\n baseUrl: string,\n sessionsUrl: URL,\n authHeader?: string,\n signal?: AbortSignal,\n ): Promise<string | undefined> {\n this.logger?.debug('Session: Creating security session');\n // nosemgrep\n const response = await this.fetchFn(sessionsUrl, {\n method: 'GET',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'create',\n },\n signal,\n });\n signal?.throwIfAborted();\n\n if (!response.ok) {\n this.logger?.warn(\n `Session: Security session creation failed with status ${response.status}`,\n );\n return undefined;\n }\n this.processResponse(response);\n\n const body = await response.text();\n signal?.throwIfAborted();\n return this.extractTrustedSessionPath({ baseUrl, body });\n }\n\n private async acquireCsrfToken(\n sessionsUrl: URL,\n authHeader?: string,\n signal?: AbortSignal,\n ): Promise<boolean> {\n this.logger?.debug('Session: Fetching CSRF token');\n // nosemgrep\n const response = await this.fetchFn(sessionsUrl, {\n method: 'GET',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': 'Fetch',\n },\n signal,\n });\n signal?.throwIfAborted();\n\n if (!response.ok) {\n this.logger?.warn(\n `Session: CSRF fetch failed with status ${response.status}`,\n );\n return false;\n }\n this.processResponse(response);\n\n if (!this.csrfManager.hasCached()) {\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 return true;\n }\n\n private async deleteSecuritySession({\n sessionPath,\n csrfToken,\n baseUrl,\n authHeader,\n client,\n signal,\n }: {\n sessionPath: string;\n csrfToken: string;\n baseUrl: string;\n authHeader?: string;\n client?: string;\n signal?: AbortSignal;\n }): Promise<void> {\n if (!this.isTrustedSessionPath({ baseUrl, sessionPath })) {\n this.logger?.warn(\n 'Session: Refusing to delete security session with untrusted path',\n );\n return;\n }\n const deleteUrl = new URL(sessionPath, baseUrl);\n if (client) deleteUrl.searchParams.append('sap-client', client);\n try {\n this.logger?.debug(`Session: Deleting security session ${sessionPath}`);\n // nosemgrep\n await this.fetchFn(deleteUrl, {\n method: 'DELETE',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': csrfToken,\n },\n signal: signal ?? AbortSignal.timeout(5_000),\n });\n this.logger?.debug('Session: Security session deleted');\n } catch {\n this.logger?.debug(\n 'Session: Failed to delete security session (will expire)',\n );\n }\n }\n\n private async fetchSecuritySessionCsrfToken({\n baseUrl,\n authHeader,\n client,\n language,\n signal,\n }: {\n baseUrl: string;\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n }): Promise<string | undefined> {\n const sessionsUrl = this.buildSessionsUrl(baseUrl, client, language);\n try {\n this.logger?.debug(\n 'Session: Fetching CSRF token for security session cleanup',\n );\n // nosemgrep\n const response = await this.fetchFn(sessionsUrl, {\n method: 'GET',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': 'Fetch',\n },\n signal: signal ?? AbortSignal.timeout(5_000),\n });\n if (!response.ok) {\n this.logger?.debug(\n `Session: Cleanup CSRF fetch failed with status ${response.status}`,\n );\n return undefined;\n }\n // Parse Set-Cookie for the cleanup session, but do not cache the\n // CSRF token or mark the session active — cleanup must not pollute\n // shared state that later requests rely on.\n const setCookieHeader = response.headers.get('set-cookie');\n if (setCookieHeader) this.cookieStore.parseCookies(setCookieHeader);\n return this.csrfManager.extractFromHeader(\n response.headers.get('x-csrf-token'),\n );\n } catch {\n this.logger?.debug('Session: Failed to fetch cleanup CSRF token');\n return undefined;\n }\n }\n\n private async deleteSecuritySessionWithFallback({\n sessionPath,\n baseUrl,\n authHeader,\n client,\n language,\n signal,\n }: {\n sessionPath: string;\n baseUrl: string;\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n }): Promise<void> {\n // Always fetch a fresh cleanup token; the shared cache may contain a\n // stale value and cleanup must never pollute shared CSRF/session state.\n const token = await this.fetchSecuritySessionCsrfToken({\n baseUrl,\n authHeader,\n client,\n language,\n signal,\n });\n if (token) {\n await this.deleteSecuritySession({\n sessionPath,\n csrfToken: token,\n baseUrl,\n authHeader,\n client,\n signal,\n });\n }\n }\n\n private handleCsrfInitializationError(\n error: unknown,\n {\n baseUrl,\n authHeader,\n client,\n language,\n sessionPath,\n signal,\n }: {\n baseUrl: string;\n authHeader?: string;\n client?: string;\n language?: string;\n sessionPath?: string;\n signal?: AbortSignal;\n },\n ): boolean {\n if (sessionPath) {\n // Use a detached timeout for cleanup; the caller signal may already\n // be aborted and we still want to free the SAP session slot.\n this.deleteSecuritySessionWithFallback({\n sessionPath,\n baseUrl,\n authHeader,\n client,\n language,\n }).catch(() => undefined);\n }\n if (signal?.aborted) {\n throw error;\n }\n this.logger?.error(\n `Session: CSRF initialization error: ${error instanceof Error ? error.message : String(error)}`,\n );\n return false;\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","import { AsyncLocalStorage } from 'node:async_hooks';\n\nconst adtAbortSignal = new AsyncLocalStorage<AbortSignal>();\n\n/**\n * Runs an ADT operation with an execution-scoped abort signal. Async local\n * storage keeps concurrent clients and invocations isolated without mutating\n * shared client state.\n */\nexport async function runWithAdtAbortSignal<T>(\n signal: AbortSignal,\n operation: () => Promise<T>,\n): Promise<T> {\n signal.throwIfAborted();\n return await adtAbortSignal.run(signal, operation);\n}\n\nexport function activeAdtAbortSignal(): AbortSignal | undefined {\n return adtAbortSignal.getStore();\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';\nimport { activeAdtAbortSignal } from './cancellation';\nimport { Agent, fetch as undiciFetch, type Dispatcher } from 'undici';\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/** Options for an authenticated GET that returns plain text. */\nexport interface BoundedTextRequestOptions {\n url: string;\n headers?: Record<string, string>;\n}\n\n/**\n * A response exceeded an explicit byte cap before its body was retained.\n * Deliberately carries size metadata only, never response text.\n */\nexport class AdtResponseTooLargeError extends Error {\n readonly code = 'ADT_RESPONSE_TOO_LARGE' as const;\n\n constructor(\n readonly maxBytes: number,\n readonly receivedBytes?: number,\n ) {\n super(`ADT response exceeds the ${maxBytes}-byte limit.`);\n this.name = 'AdtResponseTooLargeError';\n }\n}\n\nfunction contentLengthExceedsLimit(\n contentLength: string | null,\n maxBytes: number,\n): number | undefined {\n if (!contentLength || !/^\\d+$/.test(contentLength)) return undefined;\n\n const parsed = Number(contentLength);\n return Number.isSafeInteger(parsed) && parsed > maxBytes ? parsed : undefined;\n}\n\nasync function abortResponse(\n response: Response,\n abortController: AbortController,\n): Promise<void> {\n abortController.abort();\n try {\n await response.body?.cancel();\n } catch {\n // A reader may already own the stream; its cancellation is handled there.\n }\n}\n\nasync function readResponseTextBounded(\n response: Response,\n maxBytes: number,\n abortController: AbortController,\n): Promise<string> {\n const declaredBytes = contentLengthExceedsLimit(\n response.headers.get('content-length'),\n maxBytes,\n );\n if (declaredBytes !== undefined) {\n await abortResponse(response, abortController);\n throw new AdtResponseTooLargeError(maxBytes, declaredBytes);\n }\n\n if (!response.body) return '';\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let receivedBytes = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n\n const nextReceivedBytes = receivedBytes + value.byteLength;\n if (nextReceivedBytes > maxBytes) {\n abortController.abort();\n try {\n await reader.cancel();\n } catch {\n // Abort is already enough to stop the transport if stream cancel fails.\n }\n throw new AdtResponseTooLargeError(maxBytes, nextReceivedBytes);\n }\n\n chunks.push(value);\n receivedBytes = nextReceivedBytes;\n }\n } finally {\n reader.releaseLock();\n }\n\n const combined = new Uint8Array(receivedBytes);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return new TextDecoder().decode(combined);\n}\n\nfunction executionAbortController(): {\n abortController: AbortController;\n dispose: () => void;\n} {\n const abortController = new AbortController();\n const executionSignal = activeAdtAbortSignal();\n if (!executionSignal) {\n return { abortController, dispose: () => undefined };\n }\n\n const abortFromExecution = () =>\n abortController.abort(executionSignal.reason);\n if (executionSignal.aborted) {\n abortFromExecution();\n } else {\n executionSignal.addEventListener('abort', abortFromExecution, {\n once: true,\n });\n }\n return {\n abortController,\n dispose: () =>\n executionSignal.removeEventListener('abort', abortFromExecution),\n };\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 /** Read an authenticated plain-text response without exceeding maxBytes. */\n readTextBounded(\n options: BoundedTextRequestOptions,\n maxBytes: number,\n ): Promise<string>;\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 headersTimeoutMs,\n } = config;\n\n const dispatcher =\n headersTimeoutMs === undefined\n ? undefined\n : new Agent({ headersTimeout: headersTimeoutMs });\n\n /** Merge the optional Undici dispatcher into a fetch RequestInit. */\n function withDispatcher<T extends RequestInit>(\n init: T,\n ): T & { dispatcher?: Dispatcher } {\n return dispatcher ? { ...init, dispatcher } : init;\n }\n\n /** Keep a configured Agent paired with the Undici fetch ABI that created it. */\n async function requestFetch(\n input: string,\n init: RequestInit & { dispatcher?: Dispatcher },\n ): Promise<Response> {\n if (!dispatcher) return globalThis.fetch(input, init);\n return (await undiciFetch(\n input,\n init as Parameters<typeof undiciFetch>[1],\n )) as unknown as Response;\n }\n\n /**\n * Route a session-manager request through the same paired Undici\n * fetch/dispatcher used for the final adapter request, so the\n * configured headersTimeout applies to the entire write flow\n * (CSRF handshake included), not just the last hop.\n */\n async function sessionFetch(\n input: string | URL,\n init?: RequestInit,\n ): Promise<Response> {\n return requestFetch(input.toString(), withDispatcher(init ?? {}));\n }\n\n /**\n * Resolve `options.url` against the configured `baseUrl` and reject\n * absolute URLs that would send credentials to a different origin.\n */\n function resolveAdtUrl(requestUrl: string): URL {\n const resolved = new URL(requestUrl, baseUrl);\n const baseOrigin = new URL(baseUrl).origin;\n if (resolved.origin !== baseOrigin) {\n throw new RangeError(\n `Refusing to fetch ${resolved.origin} — ADT requests must target the configured base origin ${baseOrigin}`,\n );\n }\n return resolved;\n }\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, sessionFetch);\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 const executionSignal = activeAdtAbortSignal();\n executionSignal?.throwIfAborted();\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 (validated against the configured base origin)\n const url = resolveAdtUrl(options.url);\n\n // Add query parameters\n if (options.query) {\n Object.entries(options.query).forEach(([key, value]) => {\n const values = Array.isArray(value) ? value : [value];\n values.forEach((entry) => {\n if (entry !== undefined) {\n url.searchParams.append(key, String(entry));\n }\n });\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(baseUrl, {\n authHeader, // undefined for cookie auth — SessionManager uses stored cookies\n client,\n language,\n signal: executionSignal,\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 } | 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 headerNames = Object.keys(headers).sort();\n logger?.debug(\n 'Request headers (names only): ' + JSON.stringify(headerNames),\n );\n }\n const response = await requestFetch(\n url.toString(),\n withDispatcher({\n method: options.method,\n headers,\n body: requestBody,\n signal: executionSignal,\n }),\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 async readTextBounded(\n options: BoundedTextRequestOptions,\n maxBytes: number,\n ): Promise<string> {\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {\n throw new RangeError('maxBytes must be a non-negative safe integer.');\n }\n\n const url = resolveAdtUrl(options.url);\n if (client) url.searchParams.append('sap-client', client);\n if (language) url.searchParams.append('sap-language', language);\n\n const headers: Record<string, string> = {\n 'X-sap-adt-sessiontype': sessionManager.getSessionTypeHeader(),\n ...sessionManager.getRequestHeaders('GET', url.pathname),\n ...options.headers,\n };\n if (authHeader) headers.Authorization = authHeader;\n\n const { abortController, dispose } = executionAbortController();\n try {\n const response = await requestFetch(\n url.toString(), // nosemgrep — origin validated by resolveAdtUrl above\n withDispatcher({\n method: 'GET',\n headers,\n signal: abortController.signal,\n }),\n );\n sessionManager.processResponse(response, url.pathname);\n\n const text = await readResponseTextBounded(\n response,\n maxBytes,\n abortController,\n );\n\n if (!response.ok) {\n throw createAdtError(\n response.status,\n response.statusText,\n url.toString(),\n 'GET',\n text,\n );\n }\n\n return text;\n } finally {\n dispose();\n }\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 parent?: string;\n lastChangedTimestamp?: string;\n tasks?: TransportTask[];\n}\n\nexport interface TransportTask {\n number: string;\n desc?: string;\n owner?: string;\n status?: string;\n type?: string;\n parent?: string;\n lastChangedTimestamp?: 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 // SAP returns the parent request and the requested task as siblings\n // (e.g. GET .../DEVK900002 yields root.request=DEVK900001 and\n // root.task=DEVK900002). Prefer the unit whose number matches the\n // requested trkorr so a task fetch does not map its parent request.\n const wanted = trkorr.trim().toUpperCase();\n const request =\n this.findUnit(unwrapped?.task, wanted) ??\n this.findUnit(unwrapped?.request, wanted) ??\n (unwrapped?.request as Record<string, unknown> | undefined) ??\n (unwrapped?.task as Record<string, unknown> | undefined) ??\n 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 * Find a request/task unit whose number matches the wanted transport id.\n * Handles both single-object and array shapes from ts-xsd.\n */\n private findUnit(\n node: unknown,\n wanted: string,\n ): Record<string, unknown> | undefined {\n if (!node) return undefined;\n const arr = Array.isArray(node) ? node : [node];\n return arr.find((item) => {\n const u = item as Record<string, unknown>;\n return String(u?.trkorr ?? u?.number ?? '').toUpperCase() === wanted;\n }) as Record<string, unknown> | undefined;\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 parent: data.parent as string | undefined,\n lastChangedTimestamp:\n (data.lastchanged_timestamp as string | undefined) ??\n (data.lastChangedTimestamp 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 desc:\n (t.as4text as string | undefined) ?? (t.desc as string | undefined),\n type:\n (t.trfunction as string | undefined) ??\n (t.type as string | undefined),\n parent: t.parent as string | undefined,\n lastChangedTimestamp:\n (t.lastchanged_timestamp as string | undefined) ??\n (t.lastChangedTimestamp 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","import type { AdtClientType } from '@abapify/adt-contracts';\nimport { AdtResponseTooLargeError } from '../adapter';\n\nexport type SourceHistoryProtocolErrorCode =\n | 'SOURCE_HISTORY_URI_UNSAFE'\n | 'SOURCE_VERSION_FEED_INVALID'\n | 'SOURCE_VERSION_ID_MISSING'\n | 'SOURCE_VERSION_CONTENT_LINK_MISSING';\n\nexport class SourceHistoryProtocolError extends Error {\n constructor(\n readonly code: SourceHistoryProtocolErrorCode,\n message: string,\n readonly ordinal?: number,\n ) {\n super(message);\n this.name = 'SourceHistoryProtocolError';\n }\n}\n\nexport interface SourceVersionRef {\n id: string;\n ordinal: number;\n title?: string;\n sourceUri: string;\n contentType?: string;\n etag?: string;\n updatedAt?: string;\n author?: string;\n transports: string[];\n}\n\nexport type SourceVersionReference = SourceVersionRef;\n\nexport type BoundedSourceReader = (\n sourceUri: string,\n maxBytes: number,\n) => Promise<string>;\n\n/**\n * An immutable source body crossed its configured byte limit.\n * This error intentionally does not retain the source URI or any source text.\n */\nexport class SourceVersionTooLargeError extends Error {\n readonly code = 'SOURCE_VERSION_TOO_LARGE' as const;\n\n constructor(\n readonly maxBytes: number,\n readonly receivedBytes?: number,\n ) {\n super(`Source version exceeds the ${maxBytes}-byte limit.`);\n this.name = 'SourceVersionTooLargeError';\n }\n}\n\ninterface AtomLink {\n href?: unknown;\n rel?: unknown;\n type?: unknown;\n title?: unknown;\n etag?: unknown;\n}\n\ninterface AtomEntry {\n id?: unknown;\n title?: unknown;\n updated?: unknown;\n author?: unknown;\n link?: unknown;\n content?: unknown;\n}\n\nconst ADT_PATH_PREFIX = '/sap/bc/adt/';\nconst TRAVERSAL_SEGMENT = /(?:^|\\/)(?:\\.{1,2}|%2e(?:%2e)?)(?:\\/|$)/i;\nconst TRANSPORT_NUMBER = /^[A-Z0-9]{10}$/;\n\nfunction asOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction asArray<T>(value: T | T[] | undefined): T[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * Validate a server-relative ADT URI before it reaches the authenticated\n * adapter. This deliberately rejects absolute and protocol-relative URLs so a\n * source-history link cannot redirect credentials to another origin.\n */\nfunction isUnsafeAdtUri(uri: string): boolean {\n if (uri !== uri.trim()) return true;\n if (!uri.startsWith(ADT_PATH_PREFIX)) return true;\n if (uri.startsWith('//')) return true;\n if (uri.includes('#')) return true;\n if (uri.includes('\\\\')) return true;\n return TRAVERSAL_SEGMENT.test(uri);\n}\n\nexport function assertAdtUri(uri: string): string {\n if (isUnsafeAdtUri(uri)) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_HISTORY_URI_UNSAFE',\n 'Source-history URI must be a server-relative SAP ADT path.',\n );\n }\n return uri;\n}\n\nfunction isUnsafeRelativeHref(href: string): boolean {\n if (href.startsWith('//')) return true;\n if (href.includes('\\\\')) return true;\n if (href.includes(':')) return true;\n return TRAVERSAL_SEGMENT.test(href);\n}\n\nfunction resolveRelativeAdtUri(href: string, versionsUri: string): string {\n const validatedBase = assertAdtUri(versionsUri);\n const collectionBase = validatedBase.endsWith('/')\n ? validatedBase\n : `${validatedBase}/`;\n const resolved = new URL(href, `https://adt.invalid${collectionBase}`);\n return assertAdtUri(`${resolved.pathname}${resolved.search}${resolved.hash}`);\n}\n\nfunction resolveAdtUri(href: string, versionsUri: string): string {\n if (href.startsWith('/')) return assertAdtUri(href);\n if (isUnsafeRelativeHref(href)) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_HISTORY_URI_UNSAFE',\n 'Source-history link must stay within the SAP ADT origin.',\n );\n }\n return resolveRelativeAdtUri(href, versionsUri);\n}\n\nfunction normalizeLinks(value: unknown): AtomLink[] {\n return asArray(value as AtomLink | AtomLink[] | undefined).filter(\n (link): link is AtomLink => Boolean(link) && typeof link === 'object',\n );\n}\n\nfunction isContentLink(link: AtomLink): boolean {\n const rel = asOptionalString(link.rel)?.toLowerCase();\n return Boolean(rel?.endsWith('/content') || rel === 'content');\n}\n\nfunction isPlainTextContentLink(link: AtomLink): boolean {\n const contentType = asOptionalString(link.type)?.toLowerCase();\n return Boolean(contentType?.startsWith('text/plain'));\n}\n\n/**\n * Some SAP source-version feeds represent the immutable source body with the\n * standard Atom `content` element rather than an Atom link. Treat it exactly\n * like a content link, but only when it is explicitly plain text.\n */\nfunction contentElementAsLink(entry: AtomEntry): AtomLink | undefined {\n const content = entry.content;\n if (!content) return undefined;\n if (typeof content !== 'object') return undefined;\n if (Array.isArray(content)) return undefined;\n const src = asOptionalString((content as Record<string, unknown>).src);\n if (!src) return undefined;\n const type = asOptionalString((content as Record<string, unknown>).type);\n if (!type) return undefined;\n if (!type.toLowerCase().startsWith('text/plain')) return undefined;\n return { href: src, type };\n}\n\nfunction isTransportRel(rel: string): boolean {\n return rel.endsWith('/transport') || rel.endsWith('/transport/request');\n}\n\nfunction transportFromTitle(link: AtomLink): string | undefined {\n const title = asOptionalString(link.title)?.toUpperCase();\n if (!title) return undefined;\n if (TRANSPORT_NUMBER.test(title)) return title;\n return undefined;\n}\n\nfunction transportFromHref(link: AtomLink): string | undefined {\n const href = asOptionalString(link.href);\n if (!href) return undefined;\n const path = href.split(/[?#]/, 1)[0];\n const segments = path?.split('/').filter((segment) => segment !== '');\n const lastSegment = segments?.pop();\n if (!lastSegment) return undefined;\n try {\n const candidate = decodeURIComponent(lastSegment).toUpperCase();\n return TRANSPORT_NUMBER.test(candidate) ? candidate : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction transportFromLink(link: AtomLink): string | undefined {\n const rel = asOptionalString(link.rel)?.toLowerCase();\n if (!rel || !isTransportRel(rel)) return undefined;\n return transportFromTitle(link) ?? transportFromHref(link);\n}\n\nfunction authorName(value: unknown): string | undefined {\n const author = asArray(\n value as Record<string, unknown> | Record<string, unknown>[] | undefined,\n ).find((candidate) => Boolean(candidate) && typeof candidate === 'object');\n\n return asOptionalString(author?.name);\n}\n\n/**\n * Normalize the parsed Atom versions feed while preserving SAP's feed order.\n * The result contains references and provenance only; source bodies are fetched\n * separately and are never retained by this function.\n */\nfunction assertSourceVersionPayload(payload: unknown): { feed?: unknown } {\n if (!payload || typeof payload !== 'object') {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history response is not an Atom feed.',\n );\n }\n if (!('feed' in payload)) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history response is not an Atom feed.',\n );\n }\n return payload as { feed?: unknown };\n}\n\nfunction assertSourceVersionFeed(feed: unknown): { entry?: unknown } {\n if (!feed) throw invalidFeedError();\n if (typeof feed !== 'object') throw invalidFeedError();\n if (Array.isArray(feed)) throw invalidFeedError();\n return feed as { entry?: unknown };\n}\n\nfunction invalidFeedError(): SourceHistoryProtocolError {\n return new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history feed has an invalid root.',\n );\n}\n\nfunction readSourceVersionEntries(feed: { entry?: unknown }): AtomEntry[] {\n if (feed.entry === undefined) return [];\n const entries = asArray(feed.entry as AtomEntry | AtomEntry[]);\n if (entries.some((entry) => !entry || typeof entry !== 'object')) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history feed contains an invalid entry.',\n );\n }\n return entries;\n}\n\nfunction extractContentLink(\n entry: AtomEntry,\n ordinal: number,\n): AtomLink & { href: string } {\n const links = normalizeLinks(entry.link);\n const contentLink =\n links.find(isContentLink) ??\n links.find(isPlainTextContentLink) ??\n contentElementAsLink(entry);\n const href = contentLink && asOptionalString(contentLink.href);\n if (!contentLink || !href) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_CONTENT_LINK_MISSING',\n `Source-history entry ${ordinal} has no immutable content link.`,\n ordinal,\n );\n }\n return { ...contentLink, href };\n}\n\nfunction extractEntryTransports(links: AtomLink[]): string[] {\n return [\n ...new Set(\n links\n .map(transportFromLink)\n .filter((transport): transport is string => Boolean(transport)),\n ),\n ];\n}\n\nfunction sourceVersionRefFromEntry(\n entry: AtomEntry,\n ordinal: number,\n versionsUri: string,\n): SourceVersionRef {\n const id = asOptionalString(entry.id);\n if (!id) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_ID_MISSING',\n `Source-history entry ${ordinal} has no stable version id.`,\n ordinal,\n );\n }\n const contentLink = extractContentLink(entry, ordinal);\n return {\n id,\n ordinal,\n title: asOptionalString(entry.title),\n sourceUri: resolveAdtUri(contentLink.href, versionsUri),\n contentType: asOptionalString(contentLink.type),\n etag: asOptionalString(contentLink.etag),\n updatedAt: asOptionalString(entry.updated),\n author: authorName(entry.author),\n transports: extractEntryTransports(normalizeLinks(entry.link)),\n };\n}\n\nexport function normalizeSourceVersionFeed(\n payload: unknown,\n versionsUri: string,\n): SourceVersionRef[] {\n assertAdtUri(versionsUri);\n const wrapper = assertSourceVersionPayload(payload);\n const feed = assertSourceVersionFeed(wrapper.feed);\n const entries = readSourceVersionEntries(feed);\n return entries.map((entry, ordinal) =>\n sourceVersionRefFromEntry(entry, ordinal, versionsUri),\n );\n}\n\nexport class SourceHistoryService {\n constructor(\n private readonly adt: AdtClientType,\n private readonly boundedSourceReader?: BoundedSourceReader,\n ) {}\n\n async listVersions(versionsUri: string): Promise<SourceVersionRef[]> {\n assertAdtUri(versionsUri);\n const feed = await this.adt.repository.sourceversions.list({ versionsUri });\n return normalizeSourceVersionFeed(feed, versionsUri);\n }\n\n async readVersionSource(sourceUri: string): Promise<string> {\n assertAdtUri(sourceUri);\n return this.adt.repository.sourceversions.get({ sourceUri });\n }\n\n async readVersionSourceBounded(\n sourceUri: string,\n maxBytes: number,\n ): Promise<string> {\n assertAdtUri(sourceUri);\n if (!this.boundedSourceReader) {\n throw new Error('Bounded source reads are not configured.');\n }\n try {\n return await this.boundedSourceReader(sourceUri, maxBytes);\n } catch (error) {\n if (error instanceof AdtResponseTooLargeError) {\n throw new SourceVersionTooLargeError(\n error.maxBytes,\n error.receivedBytes,\n );\n }\n throw error;\n }\n }\n}\n\nexport function createSourceHistoryService(\n adt: AdtClientType,\n boundedSourceReader?: BoundedSourceReader,\n): SourceHistoryService {\n return new SourceHistoryService(adt, boundedSourceReader);\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 {\n createSourceHistoryService,\n createTransportService,\n type SourceHistoryService,\n type TransportService,\n} 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/** Options for a bounded authenticated GET that returns plain text. */\nexport interface BoundedTextFetchOptions {\n headers?: Record<string, 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('TRLK900001');\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('TRLK900001');\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 sourceHistory: SourceHistoryService;\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 readTextBounded: (\n url: string,\n maxBytes: number,\n options?: BoundedTextFetchOptions,\n ) => Promise<string>;\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 const readTextBounded = async (\n url: string,\n maxBytes: number,\n options?: BoundedTextFetchOptions,\n ): Promise<string> =>\n adapter.readTextBounded(\n {\n url,\n headers: options?.headers,\n },\n maxBytes,\n );\n\n // Create services layer\n const services: AdtServices = {\n transports: createTransportService(adtClient),\n sourceHistory: createSourceHistoryService(\n adtClient,\n (sourceUri, maxBytes) =>\n readTextBounded(sourceUri, maxBytes, {\n headers: { Accept: 'text/plain' },\n }),\n ),\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 * Read an authenticated plain-text endpoint while enforcing a byte limit.\n */\n readTextBounded,\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 'node:fs';\nimport { dirname, join } from 'node: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":";;;;;;;;;;;AAuBA,IAAa,cAAb,MAAyB;CACvB,AAAiB,0BAAU,IAAI,IAAoB;;;;;CAMnD,aAAa,iBAA+B;EAC1C,MAAM,gBAAgB,KAAK,kBAAkB,eAAe;EAE5D,KAAK,MAAM,gBAAgB,eAAe;GAGxC,MAAM,CAAC,MAAM,SADS,aAAa,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,KACf,CAAC,CAAC,MAAM,KAAK,CAAC;GAGhD,IACE,QACA,SACA,CAAC,KAAK,SAAS,SAAS,KACxB,CAAC,KAAK,SAAS,MAAM,GAErB,KAAK,QAAQ,IAAI,KAAK,KAAK,GAAG,MAAM,KAAK,CAAC;EAE9C;CACF;;;;CAKA,AAAQ,kBAAkB,iBAAmC;EAC3D,MAAM,UAAoB,CAAC;EAC3B,IAAI,UAAU;EACd,IAAI,YAAY;EAEhB,KAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,KAAK;GAC/C,MAAM,OAAO,gBAAgB;GAE7B,IAAI,SAAS,OAAO,CAAC,WAAW;IAC9B,IAAI,QAAQ,KAAK,GACf,QAAQ,KAAK,QAAQ,KAAK,CAAC;IAE7B,UAAU;GACZ,OAAO;IACL,WAAW;IAGX,IAAI,QAAQ,YAAY,CAAC,CAAC,SAAS,UAAU,GAC3C,YAAY;IAId,IAAI,cAAc,SAAS,OAAO,MAAM,gBAAgB,SAAS,IAC/D,YAAY;GAEhB;EACF;EAEA,IAAI,QAAQ,KAAK,GACf,QAAQ,KAAK,QAAQ,KAAK,CAAC;EAG7B,OAAO;CACT;;;;CAKA,kBAAsC;EACpC,IAAI,KAAK,QAAQ,SAAS,GACxB;EAGF,OAAO,MAAM,KAAK,KAAK,QAAQ,QAAQ,CAAC,CAAC,CACtC,KAAK,CAAC,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC,CAC1C,KAAK,IAAI;CACd;;;;CAKA,aAAsB;EACpB,OAAO,KAAK,QAAQ,OAAO;CAC7B;;;;CAKA,QAAc;EACZ,KAAK,QAAQ,MAAM;CACrB;;;;CAKA,SAA8B;EAC5B,OAAO,IAAI,IAAI,KAAK,OAAO;CAC7B;;;;;CAMA,aAAa,cAA4B;EAEvC,MAAM,UAAU,aAAa,MAAM,IAAI;EACvC,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,CAAC,MAAM,GAAG,cAAc,OAAO,MAAM,GAAG;GAC9C,MAAM,QAAQ,WAAW,KAAK,GAAG;GACjC,IAAI,QAAQ,OACV,KAAK,QAAQ,IAAI,KAAK,KAAK,GAAG,MAAM,KAAK,CAAC;EAE9C;CACF;AACF;;;;AAKA,IAAa,mBAAb,MAA8B;CAC5B,AAAQ;;;;CAKR,mBAAmB,SAAkD;EAEnE,MAAM,YAAY,MAAM,KAAK,QAAQ,QAAQ,CAAC,CAAC,CAAC,MAC7C,CAAC,SACA,IAAI,YAAY,CAAC,CAAC,SAAS,MAAM,KACjC,IAAI,YAAY,CAAC,CAAC,SAAS,MAAM,CACrC;EAEA,IAAI,CAAC,WACH;EAIF,MAAM,cAAc,UAAU;EAC9B,MAAM,eAAe,mBAAmB,WAAW;EAGnD,MAAM,aAAa,aAAa,MAAM,uBAAuB;EAC7D,MAAM,cAAc,aAAa,WAAW,KAAK;EAGjD,IACE,eACA,gBAAgB,cAChB,gBAAgB,WAChB,gBAAgB,SAEhB,OAAO;CAIX;;;;CAKA,kBAAkB,aAAgD;EAChE,IACE,CAAC,eACD,gBAAgB,cAChB,gBAAgB,WAChB,gBAAgB,SAEhB;EAGF,OAAO;CACT;;;;CAKA,MAAM,OAAqB;EACzB,KAAK,cAAc;CACrB;;;;CAKA,YAAgC;EAC9B,OAAO,KAAK;CACd;;;;CAKA,QAAc;EACZ,KAAK,cAAc;CACrB;;;;CAKA,YAAqB;EACnB,OAAO,CAAC,CAAC,KAAK;CAChB;;;;CAKA,WAAoB;EAClB,OAAO,KAAK,UAAU;CACxB;AACF;;;;AAKA,IAAa,cAAb,MAAyB;CACvB,AAAiB,wBAAQ,IAAI,IAAoB;;;;;;CAOjD,gBAAgB,KAAa,aAAkC;EAC7D,IAAI,aAAa;GAEf,MAAM,MAAM,KAAK,aAAa,GAAG;GACjC,KAAK,MAAM,IAAI,KAAK,WAAW;EACjC;CACF;;;;CAKA,IAAI,KAAiC;EACnC,MAAM,MAAM,KAAK,aAAa,GAAG;EACjC,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;CAKA,MAAM,KAAoB;EACxB,IAAI,KAAK;GACP,MAAM,MAAM,KAAK,aAAa,GAAG;GACjC,KAAK,MAAM,OAAO,GAAG;EACvB,OACE,KAAK,MAAM,MAAM;CAErB;;;;;;;CAQA,AAAQ,aAAa,KAAqB;EACxC,IAAI;GAEF,OAAO,IADY,IAAI,GACX,CAAC,CAAC,SAAS,QAAQ,OAAO,EAAE,CAAC,CAAC,YAAY;EACxD,QAAQ;GAEN,OAAO,IAAI,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC,YAAY;EAC1D;CACF;AACF;;;;AAKA,IAAa,iBAAb,MAAa,eAAe;CAUP;CACA;CAVnB,OAAwB,sBACtB;CAEF,AAAiB,cAAc,IAAI,YAAY;CAC/C,AAAiB,cAAc,IAAI,iBAAiB;CACpD,AAAiB,cAAc,IAAI,YAAY;CAC/C,AAAQ,wBAAwB;CAEhC,YACE,AAAiB,QACjB,AAAiB,UAA0B,WAAW,OACtD;EAFiB;EACA;CAChB;;;;;;;CAQH,gBAAgB,UAAoB,KAAoB;EAEtD,IAAI,KAAK;GACP,MAAM,OAAO,SAAS,QAAQ,IAAI,MAAM;GACxC,IAAI,MAAM;IACR,KAAK,YAAY,gBAAgB,KAAK,IAAI;IAC1C,KAAK,QAAQ,MAAM,4BAA4B,IAAI,IAAI,MAAM;GAC/D;EACF;EAGA,MAAM,kBAAkB,SAAS,QAAQ,IAAI,YAAY;EACzD,IAAI,iBAAiB;GACnB,KAAK,YAAY,aAAa,eAAe;GAC7C,KAAK,QAAQ,MAAM,wCAAwC;EAC7D;EAGA,MAAM,aAAa,SAAS,QAAQ,IAAI,cAAc;EACtD,MAAM,YAAY,KAAK,YAAY,kBAAkB,UAAU;EAC/D,IAAI,WAAW;GACb,KAAK,YAAY,MAAM,SAAS;GAChC,KAAK,QAAQ,MAAM,wCAAwC;EAC7D;EAGA,IAAI,CAAC,aAAa,KAAK,YAAY,WAAW,GAAG;GAC/C,MAAM,aAAa,KAAK,YAAY,mBAClC,KAAK,YAAY,OAAO,CAC1B;GACA,IAAI,YAAY;IACd,KAAK,YAAY,MAAM,UAAU;IACjC,KAAK,QAAQ,MAAM,yCAAyC;GAC9D;EACF;CACF;;;;;;CAOA,kBAAkB,QAAgB,KAAsC;EACtE,MAAM,UAAkC,CAAC;EAGzC,IAAI,KAAK,uBACP,QAAQ,4BAA4B;EAItC,MAAM,eAAe,KAAK,YAAY,gBAAgB;EACtD,IAAI,cACF,QAAQ,SAAS;EAOnB,IAHkB;GAAC;GAAQ;GAAO;GAAU;EAAO,CAAC,CAAC,SACnD,OAAO,YAAY,CAET,GAAG;GACb,MAAM,cAAc,KAAK,YAAY,UAAU;GAC/C,IAAI,aACF,QAAQ,kBAAkB;QAG1B,QAAQ,kBAAkB;EAE9B;EAIA,IADkB,CAAC,OAAO,OAAO,CAAC,CAAC,SAAS,OAAO,YAAY,CACnD,KAAK,KAAK;GACpB,MAAM,OAAO,KAAK,YAAY,IAAI,GAAG;GACrC,IAAI,MAAM;IACR,QAAQ,cAAc;IACtB,KAAK,QAAQ,MAAM,4CAA4C,MAAM;GACvE;EACF;EAEA,OAAO;CACT;;;;CAKA,QAAQ,KAAiC;EACvC,OAAO,KAAK,YAAY,IAAI,GAAG;CACjC;;;;CAKA,UAAU,KAAoB;EAC5B,KAAK,YAAY,MAAM,GAAG;CAC5B;;;;;;CAOA,AAAQ,qBAAqB,EAC3B,SACA,eAIU;EACV,IAAI;GACF,MAAM,SAAS,IAAI,IAAI,aAAa,OAAO;GAC3C,MAAM,OAAO,IAAI,IAAI,OAAO;GAC5B,OACE,OAAO,WAAW,KAAK,UACvB,OAAO,SAAS,WAAW,eAAe,mBAAmB;EAEjE,QAAQ;GACN,OAAO;EACT;CACF;CAEA,AAAQ,0BAA0B,EAChC,SACA,QAIqB;EAErB,MAAM,cADQ,KAAK,MAAM,iCACD,CAAC,GAAG;EAC5B,IAAI,CAAC,eAAe,CAAC,KAAK,qBAAqB;GAAE;GAAS;EAAY,CAAC,GAAG;GACxE,KAAK,QAAQ,KACX,8DACF;GACA;EACF;EACA,OAAO;CACT;;;;;;;;;;;;CAaA,AAAQ,0BAA0B,GAAG,MAKnC;EACA,IAAI,KAAK,WAAW,GAAG;GACrB,MAAM,QAAQ,KAAK;GACnB,IAAI,UAAU,MACZ;QAAI,OAAO,UAAU,UACnB,OAAO;GAMT;EAEJ;EACA,OAAO;GACL,YAAY,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;GACpD,QAAQ,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;GAChD,UAAU,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;GAClD,QAAQ,KAAK,cAAc,cAAc,KAAK,KAAK;EACrD;CACF;CAkBA,MAAM,eAAe,SAAiB,GAAG,MAAmC;EAC1E,MAAM,EAAE,YAAY,QAAQ,UAAU,WACpC,KAAK,0BAA0B,GAAG,IAAI;EAExC,QAAQ,eAAe;EACvB,MAAM,cAAc,KAAK,iBAAiB,SAAS,QAAQ,QAAQ;EACnE,IAAI;EAEJ,IAAI;GACF,cAAc,MAAM,KAAK,sBACvB,SACA,aACA,YACA,MACF;GACA,IAAI,CAAC,aAAa,OAAO;GACzB,IAAI,CAAE,MAAM,KAAK,iBAAiB,aAAa,YAAY,MAAM,GAAI;IACnE,KAAK,kCAAkC;KACrC;KACA;KACA;KACA;KACA;IACF,CAAC,CAAC,CAAC,YAAY,MAAS;IACxB,OAAO;GACT;GAEA,MAAM,oBAAoB,KAAK,YAAY,UAAU;GACrD,IAAI,mBACF,MAAM,KAAK,sBAAsB;IAC/B;IACA,WAAW;IACX;IACA;IACA;IACA;GACF,CAAC;GAEH,QAAQ,eAAe;GAEvB,OAAO;EACT,SAAS,OAAO;GACd,OAAO,KAAK,8BAA8B,OAAO;IAC/C;IACA;IACA;IACA;IACA;IACA;GACF,CAAC;EACH;CACF;CAEA,AAAQ,iBACN,SACA,QACA,UACK;EACL,MAAM,MAAM,IAAI,IAAI,kCAAkC,OAAO;EAC7D,IAAI,QAAQ,IAAI,aAAa,OAAO,cAAc,MAAM;EACxD,IAAI,UAAU,IAAI,aAAa,OAAO,gBAAgB,QAAQ;EAC9D,OAAO;CACT;CAEA,AAAQ,oBAAoB,YAA6C;EACvE,MAAM,UAAkC;GACtC,QAAQ;GACR,yBAAyB;EAC3B;EACA,IAAI,YAAY,QAAQ,gBAAgB;EACxC,MAAM,SAAS,KAAK,YAAY,gBAAgB;EAChD,IAAI,QAAQ,QAAQ,SAAS;EAC7B,OAAO;CACT;CAEA,MAAc,sBACZ,SACA,aACA,YACA,QAC6B;EAC7B,KAAK,QAAQ,MAAM,oCAAoC;EAEvD,MAAM,WAAW,MAAM,KAAK,QAAQ,aAAa;GAC/C,QAAQ;GACR,SAAS;IACP,GAAG,KAAK,oBAAoB,UAAU;IACtC,0BAA0B;GAC5B;GACA;EACF,CAAC;EACD,QAAQ,eAAe;EAEvB,IAAI,CAAC,SAAS,IAAI;GAChB,KAAK,QAAQ,KACX,yDAAyD,SAAS,QACpE;GACA;EACF;EACA,KAAK,gBAAgB,QAAQ;EAE7B,MAAM,OAAO,MAAM,SAAS,KAAK;EACjC,QAAQ,eAAe;EACvB,OAAO,KAAK,0BAA0B;GAAE;GAAS;EAAK,CAAC;CACzD;CAEA,MAAc,iBACZ,aACA,YACA,QACkB;EAClB,KAAK,QAAQ,MAAM,8BAA8B;EAEjD,MAAM,WAAW,MAAM,KAAK,QAAQ,aAAa;GAC/C,QAAQ;GACR,SAAS;IACP,GAAG,KAAK,oBAAoB,UAAU;IACtC,0BAA0B;IAC1B,gBAAgB;GAClB;GACA;EACF,CAAC;EACD,QAAQ,eAAe;EAEvB,IAAI,CAAC,SAAS,IAAI;GAChB,KAAK,QAAQ,KACX,0CAA0C,SAAS,QACrD;GACA,OAAO;EACT;EACA,KAAK,gBAAgB,QAAQ;EAE7B,IAAI,CAAC,KAAK,YAAY,UAAU,GAAG;GACjC,KAAK,QAAQ,KACX,8DACF;GACA,OAAO;EACT;EAEA,KAAK,wBAAwB;EAC7B,KAAK,QAAQ,MAAM,8BAA8B;EACjD,OAAO;CACT;CAEA,MAAc,sBAAsB,EAClC,aACA,WACA,SACA,YACA,QACA,UAQgB;EAChB,IAAI,CAAC,KAAK,qBAAqB;GAAE;GAAS;EAAY,CAAC,GAAG;GACxD,KAAK,QAAQ,KACX,kEACF;GACA;EACF;EACA,MAAM,YAAY,IAAI,IAAI,aAAa,OAAO;EAC9C,IAAI,QAAQ,UAAU,aAAa,OAAO,cAAc,MAAM;EAC9D,IAAI;GACF,KAAK,QAAQ,MAAM,sCAAsC,aAAa;GAEtE,MAAM,KAAK,QAAQ,WAAW;IAC5B,QAAQ;IACR,SAAS;KACP,GAAG,KAAK,oBAAoB,UAAU;KACtC,0BAA0B;KAC1B,gBAAgB;IAClB;IACA,QAAQ,UAAU,YAAY,QAAQ,GAAK;GAC7C,CAAC;GACD,KAAK,QAAQ,MAAM,mCAAmC;EACxD,QAAQ;GACN,KAAK,QAAQ,MACX,0DACF;EACF;CACF;CAEA,MAAc,8BAA8B,EAC1C,SACA,YACA,QACA,UACA,UAO8B;EAC9B,MAAM,cAAc,KAAK,iBAAiB,SAAS,QAAQ,QAAQ;EACnE,IAAI;GACF,KAAK,QAAQ,MACX,2DACF;GAEA,MAAM,WAAW,MAAM,KAAK,QAAQ,aAAa;IAC/C,QAAQ;IACR,SAAS;KACP,GAAG,KAAK,oBAAoB,UAAU;KACtC,0BAA0B;KAC1B,gBAAgB;IAClB;IACA,QAAQ,UAAU,YAAY,QAAQ,GAAK;GAC7C,CAAC;GACD,IAAI,CAAC,SAAS,IAAI;IAChB,KAAK,QAAQ,MACX,kDAAkD,SAAS,QAC7D;IACA;GACF;GAIA,MAAM,kBAAkB,SAAS,QAAQ,IAAI,YAAY;GACzD,IAAI,iBAAiB,KAAK,YAAY,aAAa,eAAe;GAClE,OAAO,KAAK,YAAY,kBACtB,SAAS,QAAQ,IAAI,cAAc,CACrC;EACF,QAAQ;GACN,KAAK,QAAQ,MAAM,6CAA6C;GAChE;EACF;CACF;CAEA,MAAc,kCAAkC,EAC9C,aACA,SACA,YACA,QACA,UACA,UAQgB;EAGhB,MAAM,QAAQ,MAAM,KAAK,8BAA8B;GACrD;GACA;GACA;GACA;GACA;EACF,CAAC;EACD,IAAI,OACF,MAAM,KAAK,sBAAsB;GAC/B;GACA,WAAW;GACX;GACA;GACA;GACA;EACF,CAAC;CAEL;CAEA,AAAQ,8BACN,OACA,EACE,SACA,YACA,QACA,UACA,aACA,UASO;EACT,IAAI,aAGF,KAAK,kCAAkC;GACrC;GACA;GACA;GACA;GACA;EACF,CAAC,CAAC,CAAC,YAAY,MAAS;EAE1B,IAAI,QAAQ,SACV,MAAM;EAER,KAAK,QAAQ,MACX,uCAAuC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC9F;EACA,OAAO;CACT;;;;CAKA,QAAc;EACZ,KAAK,YAAY,MAAM;EACvB,KAAK,YAAY,MAAM;EACvB,KAAK,wBAAwB;EAC7B,KAAK,QAAQ,MAAM,uDAAuD;CAC5E;;;;CAKA,eAAwB;EACtB,OAAO,KAAK,YAAY,UAAU;CACpC;;;;CAKA,kBAAsC;EACpC,OAAO,KAAK,YAAY,gBAAgB;CAC1C;;;;;CAMA,uBAA+B;EAC7B,OAAO;CACT;;;;;CAMA,aAAa,cAA4B;EACvC,KAAK,YAAY,aAAa,YAAY;EAC1C,KAAK,QAAQ,MAAM,+CAA+C;CACpE;AACF;;;;;;;;;;;;;;;;;;;;;;;;;ACryBA,IAAa,WAAb,MAAa,iBAAiB,MAAM;;CAElC,AAAS;;CAET,AAAS;;CAET,AAAS;;CAET,AAAS;;CAET,AAAS;;CAET,AAAS;CAET,YAAY,SAOT;EAED,MAAM,UAAU,QAAQ,QAAQ,OAAO,IAAI,QAAQ;EACnD,MAAM,YACJ,QAAQ,WAAW,WAAW,QAAQ,WAAW;EACnD,MAAM,UAAU,YAAY,GAAG,QAAQ,KAAK,cAAc;EAE1D,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,SAAS,QAAQ;EACtB,KAAK,aAAa,QAAQ;EAC1B,KAAK,MAAM,QAAQ;EACnB,KAAK,SAAS,QAAQ;EACtB,KAAK,UAAU,QAAQ;EACvB,KAAK,YAAY,QAAQ;EAGzB,IAAI,MAAM,mBACR,MAAM,kBAAkB,MAAM,QAAQ;CAE1C;;;;CAKA,IAAI,gBAAwB;EAC1B,OACE,KAAK,WAAW,oBAChB,KAAK,WAAW,WAChB,KAAK;CAET;;;;CAKA,OAAO,MAAuB;EAC5B,OAAO,KAAK,WAAW,SAAS;CAClC;;;;CAKA,YAAY,WAA4B;EACtC,OAAO,KAAK,WAAW,cAAc;CACvC;;;;CAKA,YAAY,KAAiC;EAC3C,OAAO,KAAK,WAAW,aAAa;CACtC;AACF;;;;;;AAOA,SAAgB,kBAAkB,KAA2C;CAK3E,IACE,CAAC,IAAI,SAAS,WAAW,KACzB,CAAC,qFAAqF,KACpF,GACF,GAEA;CAGF,IAAI;EAGF,MAAM,MADSA,UAAgB,MAAM,GACpB,CAAC,CAAC;EAEnB,IAAI,CAAC,KACH;EAGF,MAAM,SAA2B,CAAC;EAIlC,IAAI,IAAI,WAAW,IACjB,OAAO,YAAY,IAAI,UAAU;EAEnC,IAAI,IAAI,MAAM,IACZ,OAAO,OAAO,IAAI,KAAK;EAEzB,IAAI,IAAI,SAAS,QACf,OAAO,UAAU,IAAI,QAAQ;EAE/B,IAAI,IAAI,kBAAkB,QACxB,OAAO,mBAAmB,IAAI,iBAAiB;EAEjD,IAAI,IAAI,YAAY,OAAO;GACzB,OAAO,aAAa,CAAC;GACrB,MAAM,UAAU,MAAM,QAAQ,IAAI,WAAW,KAAK,IAC9C,IAAI,WAAW,QACf,CAAC,IAAI,WAAW,KAAK;GACzB,KAAK,MAAM,SAAS,SAClB,IAAI,MAAM,OAAO,MAAM,QACrB,OAAO,WAAW,MAAM,OAAO,MAAM;EAG3C;EAGA,IACE,CAAC,OAAO,aACR,CAAC,OAAO,QACR,CAAC,OAAO,WACR,CAAC,OAAO,kBAER;EAGF,OAAO;CACT,QAAQ;EAEN;CACF;AACF;;;;AAKA,SAAgB,eACd,QACA,YACA,KACA,QACA,SACU;CACV,MAAM,YAAY,kBAAkB,OAAO;CAC3C,OAAO,IAAI,SAAS;EAClB;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH;;;;ACvNA,MAAM,iBAAiB,IAAI,kBAA+B;;;;;;AAO1D,eAAsB,sBACpB,QACA,WACY;CACZ,OAAO,eAAe;CACtB,OAAO,MAAM,eAAe,IAAI,QAAQ,SAAS;AACnD;AAEA,SAAgB,uBAAgD;CAC9D,OAAO,eAAe,SAAS;AACjC;;;;;;;;ACoBA,IAAa,2BAAb,cAA8C,MAAM;CAIvC;CACA;CAJX,AAAS,OAAO;CAEhB,YACE,AAAS,UACT,AAAS,eACT;EACA,MAAM,4BAA4B,SAAS,aAAa;EAH/C;EACA;EAGT,KAAK,OAAO;CACd;AACF;AAEA,SAAS,0BACP,eACA,UACoB;CACpB,IAAI,CAAC,iBAAiB,CAAC,QAAQ,KAAK,aAAa,GAAG,OAAO;CAE3D,MAAM,SAAS,OAAO,aAAa;CACnC,OAAO,OAAO,cAAc,MAAM,KAAK,SAAS,WAAW,SAAS;AACtE;AAEA,eAAe,cACb,UACA,iBACe;CACf,gBAAgB,MAAM;CACtB,IAAI;EACF,MAAM,SAAS,MAAM,OAAO;CAC9B,QAAQ,CAER;AACF;AAEA,eAAe,wBACb,UACA,UACA,iBACiB;CACjB,MAAM,gBAAgB,0BACpB,SAAS,QAAQ,IAAI,gBAAgB,GACrC,QACF;CACA,IAAI,kBAAkB,QAAW;EAC/B,MAAM,cAAc,UAAU,eAAe;EAC7C,MAAM,IAAI,yBAAyB,UAAU,aAAa;CAC5D;CAEA,IAAI,CAAC,SAAS,MAAM,OAAO;CAE3B,MAAM,SAAS,SAAS,KAAK,UAAU;CACvC,MAAM,SAAuB,CAAC;CAC9B,IAAI,gBAAgB;CAEpB,IAAI;EACF,OAAO,MAAM;GACX,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,IAAI,CAAC,OAAO;GAEZ,MAAM,oBAAoB,gBAAgB,MAAM;GAChD,IAAI,oBAAoB,UAAU;IAChC,gBAAgB,MAAM;IACtB,IAAI;KACF,MAAM,OAAO,OAAO;IACtB,QAAQ,CAER;IACA,MAAM,IAAI,yBAAyB,UAAU,iBAAiB;GAChE;GAEA,OAAO,KAAK,KAAK;GACjB,gBAAgB;EAClB;CACF,UAAU;EACR,OAAO,YAAY;CACrB;CAEA,MAAM,WAAW,IAAI,WAAW,aAAa;CAC7C,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,QAAQ;EAC1B,SAAS,IAAI,OAAO,MAAM;EAC1B,UAAU,MAAM;CAClB;CAEA,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,QAAQ;AAC1C;AAEA,SAAS,2BAGP;CACA,MAAM,kBAAkB,IAAI,gBAAgB;CAC5C,MAAM,kBAAkB,qBAAqB;CAC7C,IAAI,CAAC,iBACH,OAAO;EAAE;EAAiB,eAAe;CAAU;CAGrD,MAAM,2BACJ,gBAAgB,MAAM,gBAAgB,MAAM;CAC9C,IAAI,gBAAgB,SAClB,mBAAmB;MAEnB,gBAAgB,iBAAiB,SAAS,oBAAoB,EAC5D,MAAM,KACR,CAAC;CAEH,OAAO;EACL;EACA,eACE,gBAAgB,oBAAoB,SAAS,kBAAkB;CACnE;AACF;AAgBA,SAAgB,iBAAiB,QAA0C;CACzE,MAAM,EACJ,SACA,UACA,UACA,cACA,qBACA,QACA,UACA,QACA,UAAU,CAAC,GACX,kBACA,qBACE;CAEJ,MAAM,aACJ,qBAAqB,SACjB,SACA,IAAI,MAAM,EAAE,gBAAgB,iBAAiB,CAAC;;CAGpD,SAAS,eACP,MACiC;EACjC,OAAO,aAAa;GAAE,GAAG;GAAM;EAAW,IAAI;CAChD;;CAGA,eAAe,aACb,OACA,MACmB;EACnB,IAAI,CAAC,YAAY,OAAO,WAAW,MAAM,OAAO,IAAI;EACpD,OAAQ,MAAMC,MACZ,OACA,IACF;CACF;;;;;;;CAQA,eAAe,aACb,OACA,MACmB;EACnB,OAAO,aAAa,MAAM,SAAS,GAAG,eAAe,QAAQ,CAAC,CAAC,CAAC;CAClE;;;;;CAMA,SAAS,cAAc,YAAyB;EAC9C,MAAM,WAAW,IAAI,IAAI,YAAY,OAAO;EAC5C,MAAM,aAAa,IAAI,IAAI,OAAO,CAAC,CAAC;EACpC,IAAI,SAAS,WAAW,YACtB,MAAM,IAAI,WACR,qBAAqB,SAAS,OAAO,yDAAyD,YAChG;EAEF,OAAO;CACT;CAGA,MAAM,aAAa,CAAC,CAAC;CAKrB,MAAM,mBAAmB,GAAG,SAAS,GAAG;CACxC,MAAM,aACJ,wBACC,aACG,SACA,SAAS,OAAO,KAAK,gBAAgB,CAAC,CAAC,SAAS,QAAQ;CAG9D,MAAM,iBAAiB,IAAI,eAAe,QAAQ,YAAY;CAG9D,IAAI,cACF,eAAe,aAAa,YAAY;CAG1C,OAAO;EACL,MAAM,QACJ,SACoB;GACpB,MAAM,kBAAkB,qBAAqB;GAC7C,iBAAiB,eAAe;GAChC,QAAQ,MAAM,+BAA+B;GAC7C,QAAQ,MAAM,gBAAgB,QAAQ,GAAG;GACzC,QAAQ,MAAM,mBAAmB,QAAQ,MAAM;GAC/C,QAAQ,MAAM,iBAAiB,QAAQ,OAAO,YAAY,WAAW;GACrE,QAAQ,MACN,uBACA,QAAQ,aAAa,YAAY,WACnC;GAGA,MAAM,MAAM,cAAc,QAAQ,GAAG;GAGrC,IAAI,QAAQ,OACV,OAAO,QAAQ,QAAQ,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;IAEtD,CADe,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,EAC9C,CAAC,SAAS,UAAU;KACxB,IAAI,UAAU,QACZ,IAAI,aAAa,OAAO,KAAK,OAAO,KAAK,CAAC;IAE9C,CAAC;GACH,CAAC;GAIH,IAAI,QACF,IAAI,aAAa,OAAO,cAAc,MAAM;GAE9C,IAAI,UACF,IAAI,aAAa,OAAO,gBAAgB,QAAQ;GASlD,IAHkB;IAAC;IAAQ;IAAO;IAAU;GAAO,CAAC,CAAC,SACnD,QAAQ,OAAO,YAAY,CAEjB,KAAK,CAAC,eAAe,aAAa,GAAG;IAC/C,QAAQ,MACN,yDACF;IACA,MAAM,eAAe,eAAe,SAAS;KAC3C;KACA;KACA;KACA,QAAQ;IACV,CAAC;GACH;GAGA,MAAM,UAAkC;IACtC,yBAAyB,eAAe,qBAAqB;IAC7D,GAAG,eAAe,kBAAkB,QAAQ,QAAQ,IAAI,QAAQ;IAChE,GAAG,QAAQ;GACb;GAGA,IAAI,YACF,QAAQ,gBAAgB;GAK1B,IAAI;GAEJ,IAAI,QAAQ,cAAc,OAAO,QAAQ,eAAe,UACtD;QACE,WAAW,QAAQ,cACnB,OAAO,QAAQ,WAAW,UAAU,YAEpC,yBAAyB,QAAQ;GAGnC;GAKF,IAAI;GACJ,IAAI,QAAQ,WAAW;IACrB,MAAM,YAAY,QAAQ,UAAU;IACpC,QAAQ,MAAM,qBAAqB;KACjC,WAAW,CAAC,CAAC;KACb,MAAM,OAAO;KACb,MACE,aAAa,OAAO,cAAc,WAC9B,OAAO,KAAK,SAAS,IACrB,CAAC;IACT,CAAC;IACD,IACE,aACA,OAAO,cAAc,YACrB,WAAW,aACX,OAAO,UAAU,UAAU,YAE3B,qBAAqB;GAEzB;GAGA,IAAI;GACJ,MAAM,OAAO,QAAQ;GAErB,QAAQ,MAAM,kBAAkB,QAAQ,GAAG;GAC3C,QAAQ,MAAM,qBAAqB,QAAQ,MAAM;GACjD,QAAQ,MACN,oBACG,QAAQ,OACL,KAAK,UAAU,QAAQ,IAAI,CAAC,CAAC,UAAU,GAAG,GAAG,IAC7C,YACR;GACA,QAAQ,MACN,0BAA0B,QAAQ,aAAa,YAAY,YAC7D;GACA,QAAQ,MACN,8BACG,yBAAyB,YAAY,YAC1C;GACA,QAAQ,MAAM,gBAAgB,OAAO,IAAI;GACzC,QAAQ,MACN,kBACG,OAAO,KAAK,UAAU,IAAI,CAAC,CAAC,UAAU,GAAG,GAAG,IAAI,iBACrD;GACA,IAAI,SAAS,UAAa,SAAS,MAAM;IACvC,IAAI,OAAO,SAAS,UAAU;KAC5B,cAAc;KACd,QAAQ,MAAM,wBAAwB;KACtC,QAAQ,MACN,mCACA,YAAY,UAAU,GAAG,GAAG,CAC9B;IACF,OAAO,IAAI,wBAET,IAAI;KACF,QAAQ,MACN,qDACE,KAAK,UAAU,IAAI,CAAC,CAAC,UAAU,GAAG,GAAG,CACzC;KACA,cAAc,uBAAuB,MAAM,IAAI;KAC/C,QAAQ,MAAM,6CAA6C;KAC3D,QAAQ,MAAM,uBAAuB,OAAO,WAAW;KACvD,QAAQ,MAAM,yBAAyB,aAAa,MAAM;KAC1D,QAAQ,MACN,qCACE,aAAa,UAAU,GAAG,GAAG,CACjC;IACF,SAAS,YAAY;KACnB,QAAQ,MACN,kCACG,sBAAsB,QACnB,WAAW,UACX,OAAO,UAAU,EACzB;KACA,QAAQ,MACN,aACG,sBAAsB,QAAQ,WAAW,QAAQ,MACtD;KACA,MAAM;IACR;SACK;KACL,cAAc,KAAK,UAAU,IAAI;KACjC,QAAQ,MAAM,0BAA0B;IAC1C;GACF;GAGA,QAAQ,MAAM,QAAQ,QAAQ,OAAO,GAAG,IAAI,SAAS,GAAG;GACxD;IACE,MAAM,cAAc,OAAO,KAAK,OAAO,CAAC,CAAC,KAAK;IAC9C,QAAQ,MACN,mCAAmC,KAAK,UAAU,WAAW,CAC/D;GACF;GACA,MAAM,WAAW,MAAM,aACrB,IAAI,SAAS,GACb,eAAe;IACb,QAAQ,QAAQ;IAChB;IACA,MAAM;IACN,QAAQ;GACV,CAAC,CACH;GAGA,eAAe,gBAAgB,UAAU,IAAI,QAAQ;GACrD,QAAQ,MAAM,aAAa,SAAS,OAAO,GAAG,SAAS,YAAY;GACnE,QAAQ,MACN,uBACE,KAAK,UACH,OAAO,YAAY,SAAS,QAAQ,QAAQ,CAAC,GAC7C,MACA,CACF,CACJ;GAGA,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,YAAY,MAAM,SAAS,KAAK;IAGtC,MAAM,WAAW,eACf,SAAS,QACT,SAAS,YACT,IAAI,SAAS,GACb,QAAQ,QACR,SACF;IAGA,IAAI,SAAS,WAAW,KAAK;KAC3B,MAAM,WAAW,QAAQ,eAAe;KACxC,MAAM,OAAO,cAAc,SAAS,SAAS,IAAI,SAAS;KAC1D,QAAQ,MACN,kBAAkB,QAAQ,OAAO,GAAG,IAAI,SAAS,kBAAkB,UACrE;KAEA,OAAO,eAAe,UAAU,WAAW;MACzC,OAAO,GAAG,SAAS,QAAQ,GAAG;MAC9B,UAAU;MACV,cAAc;KAChB,CAAC;IACH;IAEA,QAAQ,MACN,oBAAoB,SAAS,QAAQ,IAAI,QAAQ,OAAO,GAAG,IAAI,SAAS,EAAE,EAC5E;IACA,IAAI,SAAS,WACX,QAAQ,MACN,4BAA4B,SAAS,UAAU,UAAU,SAAS,SAAS,UAAU,MACvF;IAEF,IAAI,aAAa,CAAC,SAAS,WAEzB,QAAQ,MAAM,kBAAkB,WAAW;IAO7C,IAAI,SAAS,WAAW,KAAK;KAK3B,IAAI,EAHF,SAAS,WAAW,SAAS,gCAC5B,SAAS,QAAQ,SAAS,mBAAmB,KAC5C,SAAS,QAAQ,SAAS,iBAAiB,KAC1B;MACnB,eAAe,MAAM;MACrB,QAAQ,KAAK,+CAA+C;KAC9D,OACE,QAAQ,KACN,4EACF;IAEJ;IACA,MAAM;GACR;GAGA,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;GAC5D,MAAM,UAAU,MAAM,SAAS,KAAK;GAIpC,IACE,YAAY,SAAS,WAAW,KAChC,QAAQ,SAAS,aAAa,GAC9B;IACA,eAAe,MAAM;IACrB,QAAQ,KAAK,0CAA0C;IAGvD,IAAI,kBAAkB;KACpB,QAAQ,KAAK,2CAA2C;KACxD,IAAI;MACF,MAAM,YAAY,MAAM,iBAAiB;MACzC,eAAe,aAAa,SAAS;MACrC,QAAQ,KAAK,mDAAmD;MAEhE,OAAO,KAAK,QAAQ,OAAO;KAC7B,SAAS,cAAc;MACrB,QAAQ,MAAM,0BAA0B;MACxC,MAAM,IAAI,MAAM,8CAA8C,EAC5D,OAAO,aACT,CAAC;KACH;IACF;IAEA,MAAM,IAAI,MAAM,mDAAmD;GACrE;GACA,IAAI;GAGJ,IACE,YAAY,SAAS,kBAAkB,KACvC,YAAY,SAAS,OAAO,GAE5B,OAAO,KAAK,MAAM,OAAO;QACpB,IAAI,YAAY,SAAS,OAAO,KAAK,YAAY,SAAS,KAAK,GAAG;IAEvE,IAAI,sBAAsB,YAAY,SAAS,KAAK,GAClD,OAAO,mBAAmB,MAAM,OAAO;SAEvC,OAAO;GAEX,OACE,OAAO;GAIT,IAAI,QAAQ,SAAS,GAAG;IACtB,MAAM,UAA2B;KAC/B;KACA,YAAY;KACZ,KAAK,IAAI,SAAS;KAClB,QAAQ,QAAQ;KAChB;IACF;IAGA,KAAK,MAAM,UAAU,SAAS;KAC5B,MAAM,SAAS,MAAM,OAAO,QAAQ,OAAO;KAE3C,IAAI,WAAW,QAAW;MACxB,OAAO;MACP,QAAQ,aAAa;KACvB;IACF;GACF;GAGA,OAAO;EACT;EAEA,UAAU,KAAc;GACtB,eAAe,UAAU,GAAG;EAC9B;EAEA,MAAM,gBACJ,SACA,UACiB;GACjB,IAAI,CAAC,OAAO,cAAc,QAAQ,KAAK,WAAW,GAChD,MAAM,IAAI,WAAW,+CAA+C;GAGtE,MAAM,MAAM,cAAc,QAAQ,GAAG;GACrC,IAAI,QAAQ,IAAI,aAAa,OAAO,cAAc,MAAM;GACxD,IAAI,UAAU,IAAI,aAAa,OAAO,gBAAgB,QAAQ;GAE9D,MAAM,UAAkC;IACtC,yBAAyB,eAAe,qBAAqB;IAC7D,GAAG,eAAe,kBAAkB,OAAO,IAAI,QAAQ;IACvD,GAAG,QAAQ;GACb;GACA,IAAI,YAAY,QAAQ,gBAAgB;GAExC,MAAM,EAAE,iBAAiB,YAAY,yBAAyB;GAC9D,IAAI;IACF,MAAM,WAAW,MAAM,aACrB,IAAI,SAAS,GACb,eAAe;KACb,QAAQ;KACR;KACA,QAAQ,gBAAgB;IAC1B,CAAC,CACH;IACA,eAAe,gBAAgB,UAAU,IAAI,QAAQ;IAErD,MAAM,OAAO,MAAM,wBACjB,UACA,UACA,eACF;IAEA,IAAI,CAAC,SAAS,IACZ,MAAM,eACJ,SAAS,QACT,SAAS,YACT,IAAI,SAAS,GACb,OACA,IACF;IAGF,OAAO;GACT,UAAU;IACR,QAAQ;GACV;EACF;CACF;AACF;;;;;;;ACjlBA,IAAa,mBAAb,MAA8B;CACC;CAA7B,YAAY,AAAiB,KAAoB;EAApB;CAAqB;;;;CAKlD,MAAM,KAAK,SAAuD;EAChE,MAAM,WAAW,MAAM,KAAK,IAAI,IAAI,kBAAkB,KAAK,OAAO;EAMlE,OAFiB,KAAK,gBAAgB,QAExB,CAAC,CAAC,KAAK,QAAQ,KAAK,eAAe,GAAG,CAAC;CACvD;;;;CAKA,MAAM,IAAI,QAAoC;EAO5C,MAAM,OAAO,MANU,KAAK,IAAI,IAAI,kBAAkB,IAAI,MAAM;EAOhE,MAAM,YACJ,MAAM,QAAQ,OAAO,KAAK,SAAS,WAC9B,KAAK,OACN;EAKN,MAAM,SAAS,OAAO,KAAK,CAAC,CAAC,YAAY;EACzC,MAAM,UACJ,KAAK,SAAS,WAAW,MAAM,MAAM,KACrC,KAAK,SAAS,WAAW,SAAS,MAAM,KACvC,WAAW,WACX,WAAW,QACZ;EACF,OAAO,KAAK,eAAe,OAAO;CACpC;;;;;;;;CASA,MAAM,OAAO,UAAsD;EAGjE,MAAM,IAAI,MACR,2GAEF;CACF;;;;CAKA,MAAM,OAAO,QAA+B;EAC1C,MAAM,KAAK,IAAI,IAAI,kBAAkB,OAAO,MAAM;CACpD;;;;;;;CAQA,MAAM,QAAQ,SAAgC;EAE5C,MAAM,IAAI,MAAM,0DAA0D;CAC5E;;;;;CAMA,AAAQ,SACN,MACA,QACqC;EACrC,IAAI,CAAC,MAAM,OAAO;EAElB,QADY,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,IAAI,EACpC,CAAC,MAAM,SAAS;GACxB,MAAM,IAAI;GACV,OAAO,OAAO,GAAG,UAAU,GAAG,UAAU,EAAE,CAAC,CAAC,YAAY,MAAM;EAChE,CAAC;CACH;;;;CAKA,AAAQ,gBAAgB,UAA8B;EACpD,IAAI,CAAC,UAAU,OAAO,CAAC;EAEvB,MAAM,OAAO;EAGb,IAAI,MAAM,QAAQ,KAAK,OAAO,GAC5B,OAAO,KAAK;EAId,IAAI,KAAK,WAAW,OAAO,KAAK,YAAY,UAC1C,OAAO,CAAC,KAAK,OAAO;EAItB,IAAI,MAAM,QAAQ,QAAQ,GACxB,OAAO;EAIT,IAAI,KAAK,QAAQ,OAAO,KAAK,SAAS,UACpC,OAAO,KAAK,gBAAgB,KAAK,IAAI;EAGvC,OAAO,CAAC;CACV;;;;CAKA,AAAQ,eAAe,KAAyB;EAC9C,MAAM,OAAO;EAEb,OAAO;GACL,QAAQ,OACL,KAAK,UACH,KAAK,UACN,EACJ;GACA,MACG,KAAK,WACL,KAAK;GACR,OACG,KAAK,WACL,KAAK;GACR,QACG,KAAK,YACL,KAAK;GACR,MACG,KAAK,cACL,KAAK;GACR,QACG,KAAK,aACL,KAAK;GACR,QAAQ,KAAK;GACb,sBACG,KAAK,yBACL,KAAK;GACR,OAAO,KAAK,aAAa,IAAI;EAC/B;CACF;;;;CAKA,AAAQ,aAAa,MAAgD;EACnE,MAAM,QAAQ,KAAK,QAAQ,KAAK;EAEhC,IAAI,CAAC,OAAO,OAAO,CAAC;EAIpB,QAFkB,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,EAEvC,CAAC,KAAK,SAAS;GAC7B,MAAM,IAAI;GACV,OAAO;IACL,QAAQ,OACL,EAAE,UACA,EAAE,UACH,EACJ;IACA,OACG,EAAE,WAAmC,EAAE;IAC1C,QACG,EAAE,YACF,EAAE;IACL,MACG,EAAE,WAAmC,EAAE;IAC1C,MACG,EAAE,cACF,EAAE;IACL,QAAQ,EAAE;IACV,sBACG,EAAE,yBACF,EAAE;GACP;EACF,CAAC;CACH;AACF;;;;AAKA,SAAgB,uBAAuB,KAAsC;CAC3E,OAAO,IAAI,iBAAiB,GAAG;AACjC;;;;;AC5OA,SAAS,eACP,MAC8D;CAE9D,MAAM,WAAWC,KAAK;CACtB,IAAI,CAAC,UAAU,OAAO,CAAC;CAEvB,MAAM,aAAa,SAAS;CAC5B,IAAI,CAAC,YAAY,OAAO,CAAC;CACzB,OAAO,MAAM,QAAQ,UAAU,IAAI,aAAa,CAAC,UAAU;AAC7D;;AAGA,SAAS,eACP,SACY;CACZ,OAAO,QAAQ,KAAK,OAAO;EACzB,UAAU,EAAE,MAAM;EAClB,UAAU,EAAE;EACZ,KAAK,EAAE,OAAO,EAAE,EAAE;CACpB,EAAE;AACJ;;;;AAKA,IAAa,cAAb,MAAyB;CACM;CAA7B,YAAY,AAAiB,KAAoB;EAApB;CAAqB;;CAGlD,MAAM,iBAA2C;EAC/C,MAAM,UAAU,MAAM,KAAK,IAAI,KAAK,KAAK,kBAAkB,cAAc;EACzE,OAAO;GACL,UAAU,QAAQ,YAAY;GAC9B,cAAc,QAAQ;GACtB,UAAU,QAAQ,YAAY;GAC9B,QAAQ,QAAQ,UAAU;EAC5B;CACF;;CAGA,MAAM,cAAc,UAAuC;EACzD,MAAM,aAAa,SAAS,KAAK,CAAC,CAAC,YAAY;EAC/C,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,sBAAsB;EAGxC,OAAO,eAAe,eAAe,MADhB,KAAK,IAAI,OAAO,MAAM,IAAI,UAAU,CACd,CAAC;CAC9C;;CAGA,MAAM,YAAY,OAAe,WAAW,IAAyB;EACnE,MAAM,kBAAkB,MAAM,KAAK;EACnC,IAAI,CAAC,iBACH,MAAM,IAAI,MAAM,mBAAmB;EAErC,IAAI,CAAC,OAAO,UAAU,QAAQ,KAAK,YAAY,GAC7C,MAAM,IAAI,MAAM,qCAAqC;EAMvD,OAAO,eAAe,eAAe,MAJhB,KAAK,IAAI,OAAO,MAAM,OAAO;GAChD,aAAa;GACb;EACF,CAAC,CAC0C,CAAC;CAC9C;AACF;AAEA,SAAgB,kBAAkB,KAAiC;CACjE,OAAO,IAAI,YAAY,GAAG;AAC5B;;;;ACtFA,IAAa,6BAAb,cAAgD,MAAM;CAEzC;CAEA;CAHX,YACE,AAAS,MACT,SACA,AAAS,SACT;EACA,MAAM,OAAO;EAJJ;EAEA;EAGT,KAAK,OAAO;CACd;AACF;;;;;AAyBA,IAAa,6BAAb,cAAgD,MAAM;CAIzC;CACA;CAJX,AAAS,OAAO;CAEhB,YACE,AAAS,UACT,AAAS,eACT;EACA,MAAM,8BAA8B,SAAS,aAAa;EAHjD;EACA;EAGT,KAAK,OAAO;CACd;AACF;AAmBA,MAAM,kBAAkB;AACxB,MAAM,oBAAoB;AAC1B,MAAM,mBAAmB;AAEzB,SAAS,iBAAiB,OAAoC;CAC5D,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AACjE;AAEA,SAAS,QAAW,OAAiC;CACnD,IAAI,UAAU,QAAW,OAAO,CAAC;CACjC,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAC9C;;;;;;AAOA,SAAS,eAAe,KAAsB;CAC5C,IAAI,QAAQ,IAAI,KAAK,GAAG,OAAO;CAC/B,IAAI,CAAC,IAAI,WAAW,eAAe,GAAG,OAAO;CAC7C,IAAI,IAAI,WAAW,IAAI,GAAG,OAAO;CACjC,IAAI,IAAI,SAAS,GAAG,GAAG,OAAO;CAC9B,IAAI,IAAI,SAAS,IAAI,GAAG,OAAO;CAC/B,OAAO,kBAAkB,KAAK,GAAG;AACnC;AAEA,SAAgB,aAAa,KAAqB;CAChD,IAAI,eAAe,GAAG,GACpB,MAAM,IAAI,2BACR,6BACA,4DACF;CAEF,OAAO;AACT;AAEA,SAAS,qBAAqB,MAAuB;CACnD,IAAI,KAAK,WAAW,IAAI,GAAG,OAAO;CAClC,IAAI,KAAK,SAAS,IAAI,GAAG,OAAO;CAChC,IAAI,KAAK,SAAS,GAAG,GAAG,OAAO;CAC/B,OAAO,kBAAkB,KAAK,IAAI;AACpC;AAEA,SAAS,sBAAsB,MAAc,aAA6B;CACxE,MAAM,gBAAgB,aAAa,WAAW;CAC9C,MAAM,iBAAiB,cAAc,SAAS,GAAG,IAC7C,gBACA,GAAG,cAAc;CACrB,MAAM,WAAW,IAAI,IAAI,MAAM,sBAAsB,gBAAgB;CACrE,OAAO,aAAa,GAAG,SAAS,WAAW,SAAS,SAAS,SAAS,MAAM;AAC9E;AAEA,SAAS,cAAc,MAAc,aAA6B;CAChE,IAAI,KAAK,WAAW,GAAG,GAAG,OAAO,aAAa,IAAI;CAClD,IAAI,qBAAqB,IAAI,GAC3B,MAAM,IAAI,2BACR,6BACA,0DACF;CAEF,OAAO,sBAAsB,MAAM,WAAW;AAChD;AAEA,SAAS,eAAe,OAA4B;CAClD,OAAO,QAAQ,KAA0C,CAAC,CAAC,QACxD,SAA2B,QAAQ,IAAI,KAAK,OAAO,SAAS,QAC/D;AACF;AAEA,SAAS,cAAc,MAAyB;CAC9C,MAAM,MAAM,iBAAiB,KAAK,GAAG,CAAC,EAAE,YAAY;CACpD,OAAO,QAAQ,KAAK,SAAS,UAAU,KAAK,QAAQ,SAAS;AAC/D;AAEA,SAAS,uBAAuB,MAAyB;CACvD,MAAM,cAAc,iBAAiB,KAAK,IAAI,CAAC,EAAE,YAAY;CAC7D,OAAO,QAAQ,aAAa,WAAW,YAAY,CAAC;AACtD;;;;;;AAOA,SAAS,qBAAqB,OAAwC;CACpE,MAAM,UAAU,MAAM;CACtB,IAAI,CAAC,SAAS,OAAO;CACrB,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,IAAI,MAAM,QAAQ,OAAO,GAAG,OAAO;CACnC,MAAM,MAAM,iBAAkB,QAAoC,GAAG;CACrE,IAAI,CAAC,KAAK,OAAO;CACjB,MAAM,OAAO,iBAAkB,QAAoC,IAAI;CACvE,IAAI,CAAC,MAAM,OAAO;CAClB,IAAI,CAAC,KAAK,YAAY,CAAC,CAAC,WAAW,YAAY,GAAG,OAAO;CACzD,OAAO;EAAE,MAAM;EAAK;CAAK;AAC3B;AAEA,SAAS,eAAe,KAAsB;CAC5C,OAAO,IAAI,SAAS,YAAY,KAAK,IAAI,SAAS,oBAAoB;AACxE;AAEA,SAAS,mBAAmB,MAAoC;CAC9D,MAAM,QAAQ,iBAAiB,KAAK,KAAK,CAAC,EAAE,YAAY;CACxD,IAAI,CAAC,OAAO,OAAO;CACnB,IAAI,iBAAiB,KAAK,KAAK,GAAG,OAAO;AAE3C;AAEA,SAAS,kBAAkB,MAAoC;CAC7D,MAAM,OAAO,iBAAiB,KAAK,IAAI;CACvC,IAAI,CAAC,MAAM,OAAO;CAGlB,MAAM,eAFO,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC,EACd,EAAE,MAAM,GAAG,CAAC,CAAC,QAAQ,YAAY,YAAY,EAAE,EACxC,EAAE,IAAI;CAClC,IAAI,CAAC,aAAa,OAAO;CACzB,IAAI;EACF,MAAM,YAAY,mBAAmB,WAAW,CAAC,CAAC,YAAY;EAC9D,OAAO,iBAAiB,KAAK,SAAS,IAAI,YAAY;CACxD,QAAQ;EACN;CACF;AACF;AAEA,SAAS,kBAAkB,MAAoC;CAC7D,MAAM,MAAM,iBAAiB,KAAK,GAAG,CAAC,EAAE,YAAY;CACpD,IAAI,CAAC,OAAO,CAAC,eAAe,GAAG,GAAG,OAAO;CACzC,OAAO,mBAAmB,IAAI,KAAK,kBAAkB,IAAI;AAC3D;AAEA,SAAS,WAAW,OAAoC;CAKtD,OAAO,iBAJQ,QACb,KACF,CAAC,CAAC,MAAM,cAAc,QAAQ,SAAS,KAAK,OAAO,cAAc,QAEpC,CAAC,EAAE,IAAI;AACtC;;;;;;AAOA,SAAS,2BAA2B,SAAsC;CACxE,IAAI,CAAC,WAAW,OAAO,YAAY,UACjC,MAAM,IAAI,2BACR,+BACA,sDACF;CAEF,IAAI,EAAE,UAAU,UACd,MAAM,IAAI,2BACR,+BACA,sDACF;CAEF,OAAO;AACT;AAEA,SAAS,wBAAwB,MAAoC;CACnE,IAAI,CAAC,MAAM,MAAM,iBAAiB;CAClC,IAAI,OAAO,SAAS,UAAU,MAAM,iBAAiB;CACrD,IAAI,MAAM,QAAQ,IAAI,GAAG,MAAM,iBAAiB;CAChD,OAAO;AACT;AAEA,SAAS,mBAA+C;CACtD,OAAO,IAAI,2BACT,+BACA,kDACF;AACF;AAEA,SAAS,yBAAyB,MAAwC;CACxE,IAAI,KAAK,UAAU,QAAW,OAAO,CAAC;CACtC,MAAM,UAAU,QAAQ,KAAK,KAAgC;CAC7D,IAAI,QAAQ,MAAM,UAAU,CAAC,SAAS,OAAO,UAAU,QAAQ,GAC7D,MAAM,IAAI,2BACR,+BACA,wDACF;CAEF,OAAO;AACT;AAEA,SAAS,mBACP,OACA,SAC6B;CAC7B,MAAM,QAAQ,eAAe,MAAM,IAAI;CACvC,MAAM,cACJ,MAAM,KAAK,aAAa,KACxB,MAAM,KAAK,sBAAsB,KACjC,qBAAqB,KAAK;CAC5B,MAAM,OAAO,eAAe,iBAAiB,YAAY,IAAI;CAC7D,IAAI,CAAC,eAAe,CAAC,MACnB,MAAM,IAAI,2BACR,uCACA,wBAAwB,QAAQ,kCAChC,OACF;CAEF,OAAO;EAAE,GAAG;EAAa;CAAK;AAChC;AAEA,SAAS,uBAAuB,OAA6B;CAC3D,OAAO,CACL,GAAG,IAAI,IACL,MACG,IAAI,iBAAiB,CAAC,CACtB,QAAQ,cAAmC,QAAQ,SAAS,CAAC,CAClE,CACF;AACF;AAEA,SAAS,0BACP,OACA,SACA,aACkB;CAClB,MAAM,KAAK,iBAAiB,MAAM,EAAE;CACpC,IAAI,CAAC,IACH,MAAM,IAAI,2BACR,6BACA,wBAAwB,QAAQ,6BAChC,OACF;CAEF,MAAM,cAAc,mBAAmB,OAAO,OAAO;CACrD,OAAO;EACL;EACA;EACA,OAAO,iBAAiB,MAAM,KAAK;EACnC,WAAW,cAAc,YAAY,MAAM,WAAW;EACtD,aAAa,iBAAiB,YAAY,IAAI;EAC9C,MAAM,iBAAiB,YAAY,IAAI;EACvC,WAAW,iBAAiB,MAAM,OAAO;EACzC,QAAQ,WAAW,MAAM,MAAM;EAC/B,YAAY,uBAAuB,eAAe,MAAM,IAAI,CAAC;CAC/D;AACF;AAEA,SAAgB,2BACd,SACA,aACoB;CACpB,aAAa,WAAW;CAIxB,OADgB,yBADH,wBADG,2BAA2B,OACA,CAAC,CAAC,IACD,CAC/B,CAAC,CAAC,KAAK,OAAO,YACzB,0BAA0B,OAAO,SAAS,WAAW,CACvD;AACF;AAEA,IAAa,uBAAb,MAAkC;CAEb;CACA;CAFnB,YACE,AAAiB,KACjB,AAAiB,qBACjB;EAFiB;EACA;CAChB;CAEH,MAAM,aAAa,aAAkD;EACnE,aAAa,WAAW;EAExB,OAAO,2BAA2B,MADf,KAAK,IAAI,WAAW,eAAe,KAAK,EAAE,YAAY,CAAC,GAClC,WAAW;CACrD;CAEA,MAAM,kBAAkB,WAAoC;EAC1D,aAAa,SAAS;EACtB,OAAO,KAAK,IAAI,WAAW,eAAe,IAAI,EAAE,UAAU,CAAC;CAC7D;CAEA,MAAM,yBACJ,WACA,UACiB;EACjB,aAAa,SAAS;EACtB,IAAI,CAAC,KAAK,qBACR,MAAM,IAAI,MAAM,0CAA0C;EAE5D,IAAI;GACF,OAAO,MAAM,KAAK,oBAAoB,WAAW,QAAQ;EAC3D,SAAS,OAAO;GACd,IAAI,iBAAiB,0BACnB,MAAM,IAAI,2BACR,MAAM,UACN,MAAM,aACR;GAEF,MAAM;EACR;CACF;AACF;AAEA,SAAgB,2BACd,KACA,qBACsB;CACtB,OAAO,IAAI,qBAAqB,KAAK,mBAAmB;AAC1D;;;;;;;;;;;;;;ACxRA,SAAgB,gBAAgB,QAA2C;CACzE,MAAM,UAAU,iBAAiB,MAAM;CACvC,MAAM,YAAYC,kBAAwB;EACxC,SAAS,OAAO;EAChB;CACF,CAAC;CAGD,MAAM,UAAU,OACd,KACA,YACqB;EAKrB,MAAM,iBAAqC;GACzC,QALa,SAAS,UAAU;GAMhC;GACA,SANc,SAAS,WAAW,CAAC;GAOnC,MANW,SAAS;EAOtB;EAEA,OAAO,QAAQ,QAAiB,cAAc;CAChD;CAEA,MAAM,kBAAkB,OACtB,KACA,UACA,YAEA,QAAQ,gBACN;EACE;EACA,SAAS,SAAS;CACpB,GACA,QACF;CAcF,OAAO;;;;;EAKL,KAAK;;;;;EAML;GArBA,YAAY,uBAAuB,SAAS;GAC5C,eAAe,2BACb,YACC,WAAW,aACV,gBAAgB,WAAW,UAAU,EACnC,SAAS,EAAE,QAAQ,aAAa,EAClC,CAAC,CACL;EAcO;;;;;;;;;EAUP,OAAO;;;;EAKP;;;;;EAMA,YAAY,QAAiB,QAAQ,UAAU,GAAG;CACpD;AACF;;;;;;;ACrKA,IAAa,gBAAb,MAAqD;CAGtB;CAF7B,OAAO;CAEP,YAAY,AAAiB,SAAsB,QAAQ,KAAK;EAAnC;CAAoC;CAEjE,QAAQ,SAAmC;EACzC,KAAK,OAAO,IAAI,QAAQ,OAAO,IAAI,QAAQ,OAAO;GAChD,aAAa,QAAQ;GACrB,eAAe,CAAC,CAAC,QAAQ;GACzB,SAAS,QAAQ,QAAQ;EAC3B,CAAC;EACD,OAAO,QAAQ;CACjB;AACF;;;;;;;;;;;ACLA,IAAa,oBAAb,MAAyD;CAG1B;CAF7B,OAAO;CAEP,YAAY,AAAiB,QAA2B;EAA3B;CAA4B;CAEzD,QAAQ,SAAmC;EACzC,IAAI;GAEF,MAAM,WAAW,KAAK,iBAAiB,QAAQ,KAAK,QAAQ,MAAM;GAClE,MAAM,WAAW,KAAK,KAAK,OAAO,WAAW,QAAQ;GAGrD,UAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;GAGhD,cAAc,UAAU,QAAQ,SAAS,MAAM;GAC/C,KAAK,OAAO,QAAQ,MAAM,sBAAsB,UAAU;GAG1D,IAAI,KAAK,OAAO,eAAe;IAC7B,MAAM,eAAe,KAAK,gBAAgB,QAAQ;IAClD,MAAM,WAAW;KACf,KAAK,QAAQ;KACb,QAAQ,QAAQ;KAChB,aAAa,QAAQ;KACrB,4BAAW,IAAI,KAAK,EAAC,CAAC,YAAY;KAClC,MAAM,QAAQ,QAAQ;IACxB;IACA,cAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,MAAM;IACrE,KAAK,OAAO,QAAQ,MAAM,sBAAsB,cAAc;GAChE;EACF,SAAS,OAAO;GACd,KAAK,OAAO,QAAQ,MAClB,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACzF;EACF;EAEA,OAAO,QAAQ;CACjB;;;;;CAMA,AAAQ,iBAAiB,KAAa,QAAwB;EAE5D,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,IAAI,OAAO,OAAO;EAGlB,OAAO,KAAK,QAAQ,sBAAsB,EAAE;EAG5C,IAAI,CAAC,QAAQ,SAAS,aACpB,OAAO;EAIT,MAAM,CAAC,YAAY,KAAK,MAAM,GAAG;EACjC,MAAM,WAAW,SAAS,MAAM,GAAG,CAAC,CAAC,QAAQ,MAAM,CAAC;EAGpD,MAAM,cAAc;GAClB;GACA;GACA;GACA;GACA;EACF;EACA,MAAM,cAAc,SAAS,SAAS,SAAS;EAE/C,IAAI,YAAY,SAAS,WAAW,GAClC,OAAO,SAAS,SAAS,KAAK,GAAG;EAInC,MAAM,YACJ,KAAK,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC;EAG/D,IAAI,UAAU,SAAS,SAAS,KAAK,GAAG;EAGxC,IAAI,OAAO,QAAQ;GACjB,MAAM,iBAAiB,OAAO,OAC3B,MAAM,CAAC,CAAC,CACR,WAAW,mBAAmB,GAAG;GACpC,WAAW,IAAI;EACjB;EAGA,MAAM,eAAe,WAAW,QAAQ,GAAG,OAAO,YAAY,EAAE,KAAK;EAErE,OAAO,GAAG,QAAQ,GAAG,eAAe,UAAU;CAChD;;;;CAKA,AAAQ,gBAAgB,cAA8B;EACpD,IAAI,kBAAkB,KAAK,YAAY,GACrC,OAAO,aAAa,QAAQ,mBAAmB,gBAAgB;EAEjE,IAAI,SAAS,KAAK,YAAY,GAC5B,OAAO,aAAa,QAAQ,UAAU,gBAAgB;EAExD,OAAO,GAAG,aAAa;CACzB;AACF"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["exceptionSchema","undiciFetch","feed","createAdtContractClient"],"sources":["../src/utils/session.ts","../src/errors.ts","../src/cancellation.ts","../src/adapter.ts","../src/services/transports.ts","../src/services/users.ts","../src/services/source-history.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 * Fetch function injected into SessionManager so the adapter can route\n * CSRF handshake requests through the same Undici dispatcher (and\n * headersTimeout) used for the final adapter request. Defaults to the\n * global fetch when no dispatcher is configured.\n */\nexport type SessionFetchFn = (\n input: string | URL,\n init?: RequestInit,\n) => Promise<Response>;\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 static readonly SESSION_PATH_PREFIX =\n '/sap/bc/adt/core/http/sessions/';\n\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(\n private readonly logger?: Logger,\n private readonly fetchFn: SessionFetchFn = globalThis.fetch,\n ) {}\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 * Reject session paths that could send cleanup requests off-target.\n * Only relative paths under the ADT sessions endpoint (or same-origin\n * absolute URLs with that path) are accepted.\n */\n private isTrustedSessionPath({\n baseUrl,\n sessionPath,\n }: {\n baseUrl: string;\n sessionPath: string;\n }): boolean {\n try {\n const parsed = new URL(sessionPath, baseUrl);\n const base = new URL(baseUrl);\n return (\n parsed.origin === base.origin &&\n parsed.pathname.startsWith(SessionManager.SESSION_PATH_PREFIX)\n );\n } catch {\n return false;\n }\n }\n\n private extractTrustedSessionPath({\n baseUrl,\n body,\n }: {\n baseUrl: string;\n body: string;\n }): string | undefined {\n const match = body.match(/href=\"([^\"]*\\/sessions\\/[^\"]*)\"/);\n const sessionPath = match?.[1];\n if (!sessionPath || !this.isTrustedSessionPath({ baseUrl, sessionPath })) {\n this.logger?.warn(\n 'Session: Created security session returned an untrusted path',\n );\n return undefined;\n }\n return sessionPath;\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 private resolveInitializeCsrfArgs(...args: unknown[]): {\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n } {\n if (args.length === 1) {\n const first = args[0];\n if (first !== null) {\n if (typeof first === 'object') {\n return first as {\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n };\n }\n }\n }\n return {\n authHeader: typeof args[0] === 'string' ? args[0] : undefined,\n client: typeof args[1] === 'string' ? args[1] : undefined,\n language: typeof args[2] === 'string' ? args[2] : undefined,\n signal: args[3] instanceof AbortSignal ? args[3] : undefined,\n };\n }\n\n async initializeCsrf(\n baseUrl: string,\n authHeader?: string,\n client?: string,\n language?: string,\n signal?: AbortSignal,\n ): Promise<boolean>;\n async initializeCsrf(\n baseUrl: string,\n options?: {\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n },\n ): Promise<boolean>;\n async initializeCsrf(baseUrl: string, ...args: unknown[]): Promise<boolean> {\n const { authHeader, client, language, signal } =\n this.resolveInitializeCsrfArgs(...args);\n\n signal?.throwIfAborted();\n const sessionsUrl = this.buildSessionsUrl(baseUrl, client, language);\n let sessionPath: string | undefined;\n\n try {\n sessionPath = await this.createSecuritySession(\n baseUrl,\n sessionsUrl,\n authHeader,\n signal,\n );\n if (!sessionPath) return false;\n if (!(await this.acquireCsrfToken(sessionsUrl, authHeader, signal))) {\n this.deleteSecuritySessionWithFallback({\n sessionPath,\n baseUrl,\n authHeader,\n client,\n language,\n }).catch(() => undefined);\n return false;\n }\n\n const acquiredCsrfToken = this.csrfManager.getCached();\n if (acquiredCsrfToken) {\n await this.deleteSecuritySession({\n sessionPath,\n csrfToken: acquiredCsrfToken,\n baseUrl,\n authHeader,\n client,\n signal,\n });\n }\n signal?.throwIfAborted();\n\n return true;\n } catch (error) {\n return this.handleCsrfInitializationError(error, {\n baseUrl,\n authHeader,\n client,\n language,\n sessionPath,\n signal,\n });\n }\n }\n\n private buildSessionsUrl(\n baseUrl: string,\n client?: string,\n language?: string,\n ): URL {\n const url = new URL('/sap/bc/adt/core/http/sessions', baseUrl);\n if (client) url.searchParams.append('sap-client', client);\n if (language) url.searchParams.append('sap-language', language);\n return url;\n }\n\n private buildSessionHeaders(authHeader?: string): Record<string, string> {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.sap.adt.core.http.session.v3+xml',\n 'X-sap-adt-sessiontype': 'stateful',\n };\n if (authHeader) headers.Authorization = authHeader;\n const cookie = this.cookieStore.getCookieHeader();\n if (cookie) headers.Cookie = cookie;\n return headers;\n }\n\n private async createSecuritySession(\n baseUrl: string,\n sessionsUrl: URL,\n authHeader?: string,\n signal?: AbortSignal,\n ): Promise<string | undefined> {\n this.logger?.debug('Session: Creating security session');\n // nosemgrep\n const response = await this.fetchFn(sessionsUrl, {\n method: 'GET',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'create',\n },\n signal,\n });\n signal?.throwIfAborted();\n\n if (!response.ok) {\n this.logger?.warn(\n `Session: Security session creation failed with status ${response.status}`,\n );\n return undefined;\n }\n this.processResponse(response);\n\n const body = await response.text();\n signal?.throwIfAborted();\n return this.extractTrustedSessionPath({ baseUrl, body });\n }\n\n private async acquireCsrfToken(\n sessionsUrl: URL,\n authHeader?: string,\n signal?: AbortSignal,\n ): Promise<boolean> {\n this.logger?.debug('Session: Fetching CSRF token');\n // nosemgrep\n const response = await this.fetchFn(sessionsUrl, {\n method: 'GET',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': 'Fetch',\n },\n signal,\n });\n signal?.throwIfAborted();\n\n if (!response.ok) {\n this.logger?.warn(\n `Session: CSRF fetch failed with status ${response.status}`,\n );\n return false;\n }\n this.processResponse(response);\n\n if (!this.csrfManager.hasCached()) {\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 return true;\n }\n\n private async deleteSecuritySession({\n sessionPath,\n csrfToken,\n baseUrl,\n authHeader,\n client,\n signal,\n }: {\n sessionPath: string;\n csrfToken: string;\n baseUrl: string;\n authHeader?: string;\n client?: string;\n signal?: AbortSignal;\n }): Promise<void> {\n if (!this.isTrustedSessionPath({ baseUrl, sessionPath })) {\n this.logger?.warn(\n 'Session: Refusing to delete security session with untrusted path',\n );\n return;\n }\n const deleteUrl = new URL(sessionPath, baseUrl);\n if (client) deleteUrl.searchParams.append('sap-client', client);\n try {\n this.logger?.debug(`Session: Deleting security session ${sessionPath}`);\n // nosemgrep\n await this.fetchFn(deleteUrl, {\n method: 'DELETE',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': csrfToken,\n },\n signal: signal ?? AbortSignal.timeout(5_000),\n });\n this.logger?.debug('Session: Security session deleted');\n } catch {\n this.logger?.debug(\n 'Session: Failed to delete security session (will expire)',\n );\n }\n }\n\n private async fetchSecuritySessionCsrfToken({\n baseUrl,\n authHeader,\n client,\n language,\n signal,\n }: {\n baseUrl: string;\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n }): Promise<string | undefined> {\n const sessionsUrl = this.buildSessionsUrl(baseUrl, client, language);\n try {\n this.logger?.debug(\n 'Session: Fetching CSRF token for security session cleanup',\n );\n // nosemgrep\n const response = await this.fetchFn(sessionsUrl, {\n method: 'GET',\n headers: {\n ...this.buildSessionHeaders(authHeader),\n 'x-sap-security-session': 'use',\n 'x-csrf-token': 'Fetch',\n },\n signal: signal ?? AbortSignal.timeout(5_000),\n });\n if (!response.ok) {\n this.logger?.debug(\n `Session: Cleanup CSRF fetch failed with status ${response.status}`,\n );\n return undefined;\n }\n // Parse Set-Cookie for the cleanup session, but do not cache the\n // CSRF token or mark the session active — cleanup must not pollute\n // shared state that later requests rely on.\n const setCookieHeader = response.headers.get('set-cookie');\n if (setCookieHeader) this.cookieStore.parseCookies(setCookieHeader);\n return this.csrfManager.extractFromHeader(\n response.headers.get('x-csrf-token'),\n );\n } catch {\n this.logger?.debug('Session: Failed to fetch cleanup CSRF token');\n return undefined;\n }\n }\n\n private async deleteSecuritySessionWithFallback({\n sessionPath,\n baseUrl,\n authHeader,\n client,\n language,\n signal,\n }: {\n sessionPath: string;\n baseUrl: string;\n authHeader?: string;\n client?: string;\n language?: string;\n signal?: AbortSignal;\n }): Promise<void> {\n // Always fetch a fresh cleanup token; the shared cache may contain a\n // stale value and cleanup must never pollute shared CSRF/session state.\n const token = await this.fetchSecuritySessionCsrfToken({\n baseUrl,\n authHeader,\n client,\n language,\n signal,\n });\n if (token) {\n await this.deleteSecuritySession({\n sessionPath,\n csrfToken: token,\n baseUrl,\n authHeader,\n client,\n signal,\n });\n }\n }\n\n private handleCsrfInitializationError(\n error: unknown,\n {\n baseUrl,\n authHeader,\n client,\n language,\n sessionPath,\n signal,\n }: {\n baseUrl: string;\n authHeader?: string;\n client?: string;\n language?: string;\n sessionPath?: string;\n signal?: AbortSignal;\n },\n ): boolean {\n if (sessionPath) {\n // Use a detached timeout for cleanup; the caller signal may already\n // be aborted and we still want to free the SAP session slot.\n this.deleteSecuritySessionWithFallback({\n sessionPath,\n baseUrl,\n authHeader,\n client,\n language,\n }).catch(() => undefined);\n }\n if (signal?.aborted) {\n throw error;\n }\n this.logger?.error(\n `Session: CSRF initialization error: ${error instanceof Error ? error.message : String(error)}`,\n );\n return false;\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","import { AsyncLocalStorage } from 'node:async_hooks';\n\nconst adtAbortSignal = new AsyncLocalStorage<AbortSignal>();\n\n/**\n * Runs an ADT operation with an execution-scoped abort signal. Async local\n * storage keeps concurrent clients and invocations isolated without mutating\n * shared client state.\n */\nexport async function runWithAdtAbortSignal<T>(\n signal: AbortSignal,\n operation: () => Promise<T>,\n): Promise<T> {\n signal.throwIfAborted();\n return await adtAbortSignal.run(signal, operation);\n}\n\nexport function activeAdtAbortSignal(): AbortSignal | undefined {\n return adtAbortSignal.getStore();\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';\nimport { activeAdtAbortSignal } from './cancellation';\nimport { Agent, fetch as undiciFetch, type Dispatcher } from 'undici';\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/** Options for an authenticated GET that returns plain text. */\nexport interface BoundedTextRequestOptions {\n url: string;\n headers?: Record<string, string>;\n}\n\n/**\n * A response exceeded an explicit byte cap before its body was retained.\n * Deliberately carries size metadata only, never response text.\n */\nexport class AdtResponseTooLargeError extends Error {\n readonly code = 'ADT_RESPONSE_TOO_LARGE' as const;\n\n constructor(\n readonly maxBytes: number,\n readonly receivedBytes?: number,\n ) {\n super(`ADT response exceeds the ${maxBytes}-byte limit.`);\n this.name = 'AdtResponseTooLargeError';\n }\n}\n\nfunction contentLengthExceedsLimit(\n contentLength: string | null,\n maxBytes: number,\n): number | undefined {\n if (!contentLength || !/^\\d+$/.test(contentLength)) return undefined;\n\n const parsed = Number(contentLength);\n return Number.isSafeInteger(parsed) && parsed > maxBytes ? parsed : undefined;\n}\n\nasync function abortResponse(\n response: Response,\n abortController: AbortController,\n): Promise<void> {\n abortController.abort();\n try {\n await response.body?.cancel();\n } catch {\n // A reader may already own the stream; its cancellation is handled there.\n }\n}\n\nasync function readResponseTextBounded(\n response: Response,\n maxBytes: number,\n abortController: AbortController,\n): Promise<string> {\n const declaredBytes = contentLengthExceedsLimit(\n response.headers.get('content-length'),\n maxBytes,\n );\n if (declaredBytes !== undefined) {\n await abortResponse(response, abortController);\n throw new AdtResponseTooLargeError(maxBytes, declaredBytes);\n }\n\n if (!response.body) return '';\n\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let receivedBytes = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n if (!value) continue;\n\n const nextReceivedBytes = receivedBytes + value.byteLength;\n if (nextReceivedBytes > maxBytes) {\n abortController.abort();\n try {\n await reader.cancel();\n } catch {\n // Abort is already enough to stop the transport if stream cancel fails.\n }\n throw new AdtResponseTooLargeError(maxBytes, nextReceivedBytes);\n }\n\n chunks.push(value);\n receivedBytes = nextReceivedBytes;\n }\n } finally {\n reader.releaseLock();\n }\n\n const combined = new Uint8Array(receivedBytes);\n let offset = 0;\n for (const chunk of chunks) {\n combined.set(chunk, offset);\n offset += chunk.byteLength;\n }\n\n return new TextDecoder().decode(combined);\n}\n\nfunction executionAbortController(): {\n abortController: AbortController;\n dispose: () => void;\n} {\n const abortController = new AbortController();\n const executionSignal = activeAdtAbortSignal();\n if (!executionSignal) {\n return { abortController, dispose: () => undefined };\n }\n\n const abortFromExecution = () =>\n abortController.abort(executionSignal.reason);\n if (executionSignal.aborted) {\n abortFromExecution();\n } else {\n executionSignal.addEventListener('abort', abortFromExecution, {\n once: true,\n });\n }\n return {\n abortController,\n dispose: () =>\n executionSignal.removeEventListener('abort', abortFromExecution),\n };\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 /** Read an authenticated plain-text response without exceeding maxBytes. */\n readTextBounded(\n options: BoundedTextRequestOptions,\n maxBytes: number,\n ): Promise<string>;\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 headersTimeoutMs,\n } = config;\n\n const dispatcher =\n headersTimeoutMs === undefined\n ? undefined\n : new Agent({ headersTimeout: headersTimeoutMs });\n\n /** Merge the optional Undici dispatcher into a fetch RequestInit. */\n function withDispatcher<T extends RequestInit>(\n init: T,\n ): T & { dispatcher?: Dispatcher } {\n return dispatcher ? { ...init, dispatcher } : init;\n }\n\n /** Keep a configured Agent paired with the Undici fetch ABI that created it. */\n async function requestFetch(\n input: string,\n init: RequestInit & { dispatcher?: Dispatcher },\n ): Promise<Response> {\n if (!dispatcher) return globalThis.fetch(input, init);\n return (await undiciFetch(\n input,\n init as Parameters<typeof undiciFetch>[1],\n )) as unknown as Response;\n }\n\n /**\n * Route a session-manager request through the same paired Undici\n * fetch/dispatcher used for the final adapter request, so the\n * configured headersTimeout applies to the entire write flow\n * (CSRF handshake included), not just the last hop.\n */\n async function sessionFetch(\n input: string | URL,\n init?: RequestInit,\n ): Promise<Response> {\n return requestFetch(input.toString(), withDispatcher(init ?? {}));\n }\n\n /**\n * Resolve `options.url` against the configured `baseUrl` and reject\n * absolute URLs that would send credentials to a different origin.\n */\n function resolveAdtUrl(requestUrl: string): URL {\n const resolved = new URL(requestUrl, baseUrl);\n const baseOrigin = new URL(baseUrl).origin;\n if (resolved.origin !== baseOrigin) {\n throw new RangeError(\n `Refusing to fetch ${resolved.origin} — ADT requests must target the configured base origin ${baseOrigin}`,\n );\n }\n return resolved;\n }\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, sessionFetch);\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 const executionSignal = activeAdtAbortSignal();\n executionSignal?.throwIfAborted();\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 (validated against the configured base origin)\n const url = resolveAdtUrl(options.url);\n\n // Add query parameters\n if (options.query) {\n Object.entries(options.query).forEach(([key, value]) => {\n const values = Array.isArray(value) ? value : [value];\n values.forEach((entry) => {\n if (entry !== undefined) {\n url.searchParams.append(key, String(entry));\n }\n });\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(baseUrl, {\n authHeader, // undefined for cookie auth — SessionManager uses stored cookies\n client,\n language,\n signal: executionSignal,\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 } | 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 headerNames = Object.keys(headers).sort();\n logger?.debug(\n 'Request headers (names only): ' + JSON.stringify(headerNames),\n );\n }\n const response = await requestFetch(\n url.toString(),\n withDispatcher({\n method: options.method,\n headers,\n body: requestBody,\n signal: executionSignal,\n }),\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 async readTextBounded(\n options: BoundedTextRequestOptions,\n maxBytes: number,\n ): Promise<string> {\n if (!Number.isSafeInteger(maxBytes) || maxBytes < 0) {\n throw new RangeError('maxBytes must be a non-negative safe integer.');\n }\n\n const url = resolveAdtUrl(options.url);\n if (client) url.searchParams.append('sap-client', client);\n if (language) url.searchParams.append('sap-language', language);\n\n const headers: Record<string, string> = {\n 'X-sap-adt-sessiontype': sessionManager.getSessionTypeHeader(),\n ...sessionManager.getRequestHeaders('GET', url.pathname),\n ...options.headers,\n };\n if (authHeader) headers.Authorization = authHeader;\n\n const { abortController, dispose } = executionAbortController();\n try {\n const response = await requestFetch(\n url.toString(), // nosemgrep — origin validated by resolveAdtUrl above\n withDispatcher({\n method: 'GET',\n headers,\n signal: abortController.signal,\n }),\n );\n sessionManager.processResponse(response, url.pathname);\n\n const text = await readResponseTextBounded(\n response,\n maxBytes,\n abortController,\n );\n\n if (!response.ok) {\n throw createAdtError(\n response.status,\n response.statusText,\n url.toString(),\n 'GET',\n text,\n );\n }\n\n return text;\n } finally {\n dispose();\n }\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 parent?: string;\n lastChangedTimestamp?: string;\n tasks?: TransportTask[];\n}\n\nexport interface TransportTask {\n number: string;\n desc?: string;\n owner?: string;\n status?: string;\n type?: string;\n parent?: string;\n lastChangedTimestamp?: 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 // SAP returns the parent request and the requested task as siblings\n // (e.g. GET .../DEVK900002 yields root.request=DEVK900001 and\n // root.task=DEVK900002). Prefer the unit whose number matches the\n // requested trkorr so a task fetch does not map its parent request.\n const wanted = trkorr.trim().toUpperCase();\n const request =\n this.findUnit(unwrapped?.task, wanted) ??\n this.findUnit(unwrapped?.request, wanted) ??\n (unwrapped?.request as Record<string, unknown> | undefined) ??\n (unwrapped?.task as Record<string, unknown> | undefined) ??\n 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 * Find a request/task unit whose number matches the wanted transport id.\n * Handles both single-object and array shapes from ts-xsd.\n */\n private findUnit(\n node: unknown,\n wanted: string,\n ): Record<string, unknown> | undefined {\n if (!node) return undefined;\n const arr = Array.isArray(node) ? node : [node];\n return arr.find((item) => {\n const u = item as Record<string, unknown>;\n return String(u?.trkorr ?? u?.number ?? '').toUpperCase() === wanted;\n }) as Record<string, unknown> | undefined;\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 parent: data.parent as string | undefined,\n lastChangedTimestamp:\n (data.lastchanged_timestamp as string | undefined) ??\n (data.lastChangedTimestamp 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 desc:\n (t.as4text as string | undefined) ?? (t.desc as string | undefined),\n type:\n (t.trfunction as string | undefined) ??\n (t.type as string | undefined),\n parent: t.parent as string | undefined,\n lastChangedTimestamp:\n (t.lastchanged_timestamp as string | undefined) ??\n (t.lastChangedTimestamp 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","import type { AdtClientType } from '@abapify/adt-contracts';\nimport { AdtResponseTooLargeError } from '../adapter';\n\nexport type SourceHistoryProtocolErrorCode =\n | 'SOURCE_HISTORY_URI_UNSAFE'\n | 'SOURCE_VERSION_FEED_INVALID'\n | 'SOURCE_VERSION_ID_MISSING'\n | 'SOURCE_VERSION_CONTENT_LINK_MISSING';\n\nexport class SourceHistoryProtocolError extends Error {\n constructor(\n readonly code: SourceHistoryProtocolErrorCode,\n message: string,\n readonly ordinal?: number,\n ) {\n super(message);\n this.name = 'SourceHistoryProtocolError';\n }\n}\n\nexport interface SourceVersionRef {\n id: string;\n ordinal: number;\n title?: string;\n sourceUri: string;\n contentType?: string;\n etag?: string;\n updatedAt?: string;\n author?: string;\n transports: string[];\n}\n\nexport type SourceVersionReference = SourceVersionRef;\n\nexport type BoundedSourceReader = (\n sourceUri: string,\n maxBytes: number,\n) => Promise<string>;\n\n/**\n * An immutable source body crossed its configured byte limit.\n * This error intentionally does not retain the source URI or any source text.\n */\nexport class SourceVersionTooLargeError extends Error {\n readonly code = 'SOURCE_VERSION_TOO_LARGE' as const;\n\n constructor(\n readonly maxBytes: number,\n readonly receivedBytes?: number,\n ) {\n super(`Source version exceeds the ${maxBytes}-byte limit.`);\n this.name = 'SourceVersionTooLargeError';\n }\n}\n\ninterface AtomLink {\n href?: unknown;\n rel?: unknown;\n type?: unknown;\n title?: unknown;\n etag?: unknown;\n}\n\ninterface AtomEntry {\n id?: unknown;\n title?: unknown;\n updated?: unknown;\n author?: unknown;\n link?: unknown;\n content?: unknown;\n}\n\nconst ADT_PATH_PREFIX = '/sap/bc/adt/';\nconst TRAVERSAL_SEGMENT = /(?:^|\\/)(?:\\.{1,2}|%2e(?:%2e)?)(?:\\/|$)/i;\nconst TRANSPORT_NUMBER = /^[A-Z0-9]{10}$/;\n\nfunction asOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined;\n}\n\nfunction asArray<T>(value: T | T[] | undefined): T[] {\n if (value === undefined) return [];\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * Validate a server-relative ADT URI before it reaches the authenticated\n * adapter. This deliberately rejects absolute and protocol-relative URLs so a\n * source-history link cannot redirect credentials to another origin.\n */\nfunction isUnsafeAdtUri(uri: string): boolean {\n if (uri !== uri.trim()) return true;\n if (!uri.startsWith(ADT_PATH_PREFIX)) return true;\n if (uri.startsWith('//')) return true;\n if (uri.includes('#')) return true;\n if (uri.includes('\\\\')) return true;\n return TRAVERSAL_SEGMENT.test(uri);\n}\n\nexport function assertAdtUri(uri: string): string {\n if (isUnsafeAdtUri(uri)) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_HISTORY_URI_UNSAFE',\n 'Source-history URI must be a server-relative SAP ADT path.',\n );\n }\n return uri;\n}\n\nfunction isUnsafeRelativeHref(href: string): boolean {\n if (href.startsWith('//')) return true;\n if (href.includes('\\\\')) return true;\n if (href.includes(':')) return true;\n return TRAVERSAL_SEGMENT.test(href);\n}\n\nfunction resolveRelativeAdtUri(href: string, versionsUri: string): string {\n const validatedBase = assertAdtUri(versionsUri);\n const collectionBase = validatedBase.endsWith('/')\n ? validatedBase\n : `${validatedBase}/`;\n const resolved = new URL(href, `https://adt.invalid${collectionBase}`);\n return assertAdtUri(`${resolved.pathname}${resolved.search}${resolved.hash}`);\n}\n\nfunction resolveAdtUri(href: string, versionsUri: string): string {\n if (href.startsWith('/')) return assertAdtUri(href);\n if (isUnsafeRelativeHref(href)) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_HISTORY_URI_UNSAFE',\n 'Source-history link must stay within the SAP ADT origin.',\n );\n }\n return resolveRelativeAdtUri(href, versionsUri);\n}\n\nfunction normalizeLinks(value: unknown): AtomLink[] {\n return asArray(value as AtomLink | AtomLink[] | undefined).filter(\n (link): link is AtomLink => Boolean(link) && typeof link === 'object',\n );\n}\n\nfunction isContentLink(link: AtomLink): boolean {\n const rel = asOptionalString(link.rel)?.toLowerCase();\n return Boolean(rel?.endsWith('/content') || rel === 'content');\n}\n\nfunction isPlainTextContentLink(link: AtomLink): boolean {\n const contentType = asOptionalString(link.type)?.toLowerCase();\n return Boolean(contentType?.startsWith('text/plain'));\n}\n\n/**\n * Some SAP source-version feeds represent the immutable source body with the\n * standard Atom `content` element rather than an Atom link. Treat it exactly\n * like a content link, but only when it is explicitly plain text.\n */\nfunction contentElementAsLink(entry: AtomEntry): AtomLink | undefined {\n const content = entry.content;\n if (!content) return undefined;\n if (typeof content !== 'object') return undefined;\n if (Array.isArray(content)) return undefined;\n const src = asOptionalString((content as Record<string, unknown>).src);\n if (!src) return undefined;\n const type = asOptionalString((content as Record<string, unknown>).type);\n if (!type) return undefined;\n if (!type.toLowerCase().startsWith('text/plain')) return undefined;\n return { href: src, type };\n}\n\nfunction isTransportRel(rel: string): boolean {\n return rel.endsWith('/transport') || rel.endsWith('/transport/request');\n}\n\nfunction transportFromTitle(link: AtomLink): string | undefined {\n const title = asOptionalString(link.title)?.toUpperCase();\n if (!title) return undefined;\n if (TRANSPORT_NUMBER.test(title)) return title;\n return undefined;\n}\n\nfunction transportFromHref(link: AtomLink): string | undefined {\n const href = asOptionalString(link.href);\n if (!href) return undefined;\n const path = href.split(/[?#]/, 1)[0];\n const segments = path?.split('/').filter((segment) => segment !== '');\n const lastSegment = segments?.pop();\n if (!lastSegment) return undefined;\n try {\n const candidate = decodeURIComponent(lastSegment).toUpperCase();\n return TRANSPORT_NUMBER.test(candidate) ? candidate : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction transportFromLink(link: AtomLink): string | undefined {\n const rel = asOptionalString(link.rel)?.toLowerCase();\n if (!rel || !isTransportRel(rel)) return undefined;\n return transportFromTitle(link) ?? transportFromHref(link);\n}\n\nfunction authorName(value: unknown): string | undefined {\n const author = asArray(\n value as Record<string, unknown> | Record<string, unknown>[] | undefined,\n ).find((candidate) => Boolean(candidate) && typeof candidate === 'object');\n\n return asOptionalString(author?.name);\n}\n\n/**\n * Normalize the parsed Atom versions feed while preserving SAP's feed order.\n * The result contains references and provenance only; source bodies are fetched\n * separately and are never retained by this function.\n */\nfunction assertSourceVersionPayload(payload: unknown): { feed?: unknown } {\n if (!payload || typeof payload !== 'object') {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history response is not an Atom feed.',\n );\n }\n if (!('feed' in payload)) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history response is not an Atom feed.',\n );\n }\n return payload as { feed?: unknown };\n}\n\nfunction assertSourceVersionFeed(feed: unknown): { entry?: unknown } {\n if (!feed) throw invalidFeedError();\n if (typeof feed !== 'object') throw invalidFeedError();\n if (Array.isArray(feed)) throw invalidFeedError();\n return feed as { entry?: unknown };\n}\n\nfunction invalidFeedError(): SourceHistoryProtocolError {\n return new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history feed has an invalid root.',\n );\n}\n\nfunction readSourceVersionEntries(feed: { entry?: unknown }): AtomEntry[] {\n if (feed.entry === undefined) return [];\n const entries = asArray(feed.entry as AtomEntry | AtomEntry[]);\n if (entries.some((entry) => !entry || typeof entry !== 'object')) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_FEED_INVALID',\n 'SAP ADT source-history feed contains an invalid entry.',\n );\n }\n return entries;\n}\n\nfunction extractContentLink(\n entry: AtomEntry,\n ordinal: number,\n): AtomLink & { href: string } {\n const links = normalizeLinks(entry.link);\n const contentLink =\n links.find(isContentLink) ??\n links.find(isPlainTextContentLink) ??\n contentElementAsLink(entry);\n const href = contentLink && asOptionalString(contentLink.href);\n if (!contentLink || !href) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_CONTENT_LINK_MISSING',\n `Source-history entry ${ordinal} has no immutable content link.`,\n ordinal,\n );\n }\n return { ...contentLink, href };\n}\n\nfunction extractEntryTransports(links: AtomLink[]): string[] {\n return [\n ...new Set(\n links\n .map(transportFromLink)\n .filter((transport): transport is string => Boolean(transport)),\n ),\n ];\n}\n\nfunction sourceVersionRefFromEntry(\n entry: AtomEntry,\n ordinal: number,\n versionsUri: string,\n): SourceVersionRef {\n const id = asOptionalString(entry.id);\n if (!id) {\n throw new SourceHistoryProtocolError(\n 'SOURCE_VERSION_ID_MISSING',\n `Source-history entry ${ordinal} has no stable version id.`,\n ordinal,\n );\n }\n const contentLink = extractContentLink(entry, ordinal);\n return {\n id,\n ordinal,\n title: asOptionalString(entry.title),\n sourceUri: resolveAdtUri(contentLink.href, versionsUri),\n contentType: asOptionalString(contentLink.type),\n etag: asOptionalString(contentLink.etag),\n updatedAt: asOptionalString(entry.updated),\n author: authorName(entry.author),\n transports: extractEntryTransports(normalizeLinks(entry.link)),\n };\n}\n\nexport function normalizeSourceVersionFeed(\n payload: unknown,\n versionsUri: string,\n): SourceVersionRef[] {\n assertAdtUri(versionsUri);\n const wrapper = assertSourceVersionPayload(payload);\n const feed = assertSourceVersionFeed(wrapper.feed);\n const entries = readSourceVersionEntries(feed);\n return entries.map((entry, ordinal) =>\n sourceVersionRefFromEntry(entry, ordinal, versionsUri),\n );\n}\n\nexport class SourceHistoryService {\n constructor(\n private readonly adt: AdtClientType,\n private readonly boundedSourceReader?: BoundedSourceReader,\n ) {}\n\n async listVersions(versionsUri: string): Promise<SourceVersionRef[]> {\n assertAdtUri(versionsUri);\n const feed = await this.adt.repository.sourceversions.list({ versionsUri });\n return normalizeSourceVersionFeed(feed, versionsUri);\n }\n\n async readVersionSource(sourceUri: string): Promise<string> {\n assertAdtUri(sourceUri);\n return this.adt.repository.sourceversions.get({ sourceUri });\n }\n\n async readVersionSourceBounded(\n sourceUri: string,\n maxBytes: number,\n ): Promise<string> {\n assertAdtUri(sourceUri);\n if (!this.boundedSourceReader) {\n throw new Error('Bounded source reads are not configured.');\n }\n try {\n return await this.boundedSourceReader(sourceUri, maxBytes);\n } catch (error) {\n if (error instanceof AdtResponseTooLargeError) {\n throw new SourceVersionTooLargeError(\n error.maxBytes,\n error.receivedBytes,\n );\n }\n throw error;\n }\n }\n}\n\nexport function createSourceHistoryService(\n adt: AdtClientType,\n boundedSourceReader?: BoundedSourceReader,\n): SourceHistoryService {\n return new SourceHistoryService(adt, boundedSourceReader);\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 {\n createSourceHistoryService,\n createTransportService,\n type SourceHistoryService,\n type TransportService,\n} 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/** Options for a bounded authenticated GET that returns plain text. */\nexport interface BoundedTextFetchOptions {\n headers?: Record<string, 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('TRLK900001');\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('TRLK900001');\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 sourceHistory: SourceHistoryService;\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 readTextBounded: (\n url: string,\n maxBytes: number,\n options?: BoundedTextFetchOptions,\n ) => Promise<string>;\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 const readTextBounded = async (\n url: string,\n maxBytes: number,\n options?: BoundedTextFetchOptions,\n ): Promise<string> =>\n adapter.readTextBounded(\n {\n url,\n headers: options?.headers,\n },\n maxBytes,\n );\n\n // Create services layer\n const services: AdtServices = {\n transports: createTransportService(adtClient),\n sourceHistory: createSourceHistoryService(\n adtClient,\n (sourceUri, maxBytes) =>\n readTextBounded(sourceUri, maxBytes, {\n headers: { Accept: 'text/plain' },\n }),\n ),\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 * Read an authenticated plain-text endpoint while enforcing a byte limit.\n */\n readTextBounded,\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 'node:fs';\nimport { dirname, join } from 'node: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.4.
|
|
3
|
+
"version": "0.4.19",
|
|
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.4.
|
|
21
|
-
"@abapify/adt-contracts": "0.4.
|
|
22
|
-
"@abapify/adt-schemas": "0.4.
|
|
20
|
+
"@abapify/logger": "0.4.19",
|
|
21
|
+
"@abapify/adt-contracts": "0.4.19",
|
|
22
|
+
"@abapify/adt-schemas": "0.4.19",
|
|
23
23
|
"undici": "^8.10.0"
|
|
24
24
|
},
|
|
25
25
|
"repository": {
|