@xrmforge/typegen 0.8.2 → 0.8.4

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/logger.ts","../src/auth/credential.ts","../src/http/client.ts","../src/metadata/xml-parser.ts","../src/metadata/form-parser.ts","../src/metadata/client.ts","../src/metadata/cache.ts","../src/metadata/change-detector.ts","../src/metadata/labels.ts","../src/generators/type-mapping.ts","../src/generators/activity-party.ts","../src/generators/label-utils.ts","../src/generators/entity-generator.ts","../src/generators/optionset-generator.ts","../src/generators/form-generator.ts","../src/generators/entity-fields-generator.ts","../src/generators/entity-names-generator.ts","../src/metadata/custom-api-types.ts","../src/generators/action-generator.ts","../src/orchestrator/file-writer.ts","../src/orchestrator/orchestrator.ts"],"sourcesContent":["/**\r\n * @xrmforge/typegen - Error Types\r\n *\r\n * Centralized error hierarchy for consistent error handling across the framework.\r\n * Every error carries a machine-readable code, a human-readable message,\r\n * and optional context for debugging.\r\n */\r\n\r\nexport enum ErrorCode {\r\n // Authentication errors (1xxx)\r\n AUTH_MISSING_CONFIG = 'AUTH_1001',\r\n AUTH_INVALID_CREDENTIALS = 'AUTH_1002',\r\n AUTH_TOKEN_FAILED = 'AUTH_1003',\r\n AUTH_TOKEN_EXPIRED = 'AUTH_1004',\r\n\r\n // API errors (2xxx)\r\n API_REQUEST_FAILED = 'API_2001',\r\n API_RATE_LIMITED = 'API_2002',\r\n API_NOT_FOUND = 'API_2003',\r\n API_UNAUTHORIZED = 'API_2004',\r\n API_TIMEOUT = 'API_2005',\r\n\r\n // Metadata errors (3xxx)\r\n META_ENTITY_NOT_FOUND = 'META_3001',\r\n META_SOLUTION_NOT_FOUND = 'META_3002',\r\n META_FORM_PARSE_FAILED = 'META_3003',\r\n META_ATTRIBUTE_UNKNOWN_TYPE = 'META_3004',\r\n META_VERSION_STAMP_EXPIRED = 'META_3005',\r\n\r\n // Generation errors (4xxx)\r\n GEN_OUTPUT_WRITE_FAILED = 'GEN_4001',\r\n GEN_TEMPLATE_FAILED = 'GEN_4002',\r\n GEN_INVALID_IDENTIFIER = 'GEN_4003',\r\n\r\n // Config errors (5xxx)\r\n CONFIG_INVALID = 'CONFIG_5001',\r\n CONFIG_FILE_NOT_FOUND = 'CONFIG_5002',\r\n CONFIG_ENV_VAR_MISSING = 'CONFIG_5003',\r\n}\r\n\r\n/**\r\n * Base error class for all XrmForge errors.\r\n * Carries a structured error code and optional context object.\r\n */\r\nexport class XrmForgeError extends Error {\r\n public readonly code: ErrorCode;\r\n public readonly context: Record<string, unknown>;\r\n\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(`[${code}] ${message}`);\r\n this.name = 'XrmForgeError';\r\n this.code = code;\r\n this.context = context;\r\n\r\n // Maintains proper stack trace in V8 environments\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, XrmForgeError);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Authentication-specific error.\r\n */\r\nexport class AuthenticationError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'AuthenticationError';\r\n }\r\n}\r\n\r\n/**\r\n * Dataverse API request error.\r\n */\r\nexport class ApiRequestError extends XrmForgeError {\r\n public readonly statusCode: number | undefined;\r\n public readonly responseBody: string | undefined;\r\n\r\n constructor(\r\n code: ErrorCode,\r\n message: string,\r\n context: Record<string, unknown> & {\r\n statusCode?: number;\r\n responseBody?: string;\r\n url?: string;\r\n } = {},\r\n ) {\r\n super(code, message, context);\r\n this.name = 'ApiRequestError';\r\n this.statusCode = context.statusCode;\r\n this.responseBody = context.responseBody;\r\n }\r\n}\r\n\r\n/**\r\n * Metadata retrieval or parsing error.\r\n */\r\nexport class MetadataError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'MetadataError';\r\n }\r\n}\r\n\r\n/**\r\n * Type generation or file output error.\r\n */\r\nexport class GenerationError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'GenerationError';\r\n }\r\n}\r\n\r\n/**\r\n * Configuration validation error.\r\n */\r\nexport class ConfigError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'ConfigError';\r\n }\r\n}\r\n\r\n/**\r\n * Type guard to check if an unknown error is an XrmForgeError.\r\n */\r\nexport function isXrmForgeError(error: unknown): error is XrmForgeError {\r\n return error instanceof XrmForgeError;\r\n}\r\n\r\n/**\r\n * Type guard for API rate limit errors (HTTP 429).\r\n */\r\nexport function isRateLimitError(error: unknown): error is ApiRequestError {\r\n return error instanceof ApiRequestError && error.code === ErrorCode.API_RATE_LIMITED;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Logger\r\n *\r\n * Abstracted logging interface that decouples log output from the modules.\r\n * Supports: CLI (human-readable), CI/CD (structured JSON), silent (library use).\r\n *\r\n * Every logger instance carries a `scope` (e.g. \"auth\", \"metadata\", \"http\")\r\n * so log consumers can filter by origin module.\r\n *\r\n * Consumers can provide their own LogSink to integrate with any logging framework.\r\n */\r\n\r\nexport enum LogLevel {\r\n DEBUG = 0,\r\n INFO = 1,\r\n WARN = 2,\r\n ERROR = 3,\r\n SILENT = 4,\r\n}\r\n\r\nexport interface LogEntry {\r\n level: LogLevel;\r\n scope: string;\r\n message: string;\r\n context?: Record<string, unknown>;\r\n timestamp: Date;\r\n}\r\n\r\n/**\r\n * Interface for log output destinations.\r\n * Implement this to route XrmForge logs into your own logging system.\r\n */\r\nexport interface LogSink {\r\n write(entry: LogEntry): void;\r\n\r\n /**\r\n * Write an inline progress update (no trailing newline).\r\n * Used for long-running operations where each entity gets a status indicator.\r\n *\r\n * Sinks that don't support inline progress (e.g. JSON) should fall back\r\n * to writing a regular INFO entry.\r\n */\r\n writeProgress(message: string): void;\r\n\r\n /**\r\n * Complete an inline progress line with a trailing message and newline.\r\n */\r\n writeProgressEnd(message: string): void;\r\n}\r\n\r\n/**\r\n * Default CLI log sink with human-readable output and ANSI color indicators.\r\n */\r\nexport class ConsoleLogSink implements LogSink {\r\n private static readonly LEVEL_PREFIX: Readonly<Record<LogLevel, string>> = {\r\n [LogLevel.DEBUG]: '\\x1b[90m[DBG]\\x1b[0m',\r\n [LogLevel.INFO]: '\\x1b[36m[INF]\\x1b[0m',\r\n [LogLevel.WARN]: '\\x1b[33m[WRN]\\x1b[0m',\r\n [LogLevel.ERROR]: '\\x1b[31m[ERR]\\x1b[0m',\r\n [LogLevel.SILENT]: '',\r\n };\r\n\r\n write(entry: LogEntry): void {\r\n if (entry.level === LogLevel.SILENT) return;\r\n\r\n const prefix = ConsoleLogSink.LEVEL_PREFIX[entry.level];\r\n const scope = `\\x1b[90m[${entry.scope}]\\x1b[0m`;\r\n const message = `${prefix} ${scope} ${entry.message}`;\r\n\r\n switch (entry.level) {\r\n case LogLevel.ERROR:\r\n console.error(message);\r\n break;\r\n case LogLevel.WARN:\r\n console.warn(message);\r\n break;\r\n default:\r\n console.log(message);\r\n }\r\n\r\n if (entry.context && entry.level === LogLevel.DEBUG) {\r\n console.log(' Context:', JSON.stringify(entry.context, null, 2));\r\n }\r\n }\r\n\r\n writeProgress(message: string): void {\r\n process.stdout.write(message);\r\n }\r\n\r\n writeProgressEnd(message: string): void {\r\n console.log(message);\r\n }\r\n}\r\n\r\n/**\r\n * Structured JSON log sink for CI/CD pipelines and machine-readable output.\r\n */\r\nexport class JsonLogSink implements LogSink {\r\n write(entry: LogEntry): void {\r\n if (entry.level === LogLevel.SILENT) return;\r\n\r\n const output = {\r\n timestamp: entry.timestamp.toISOString(),\r\n level: LogLevel[entry.level],\r\n scope: entry.scope,\r\n message: entry.message,\r\n ...(entry.context ? { context: entry.context } : {}),\r\n };\r\n\r\n console.log(JSON.stringify(output));\r\n }\r\n\r\n writeProgress(message: string): void {\r\n console.log(JSON.stringify({\r\n timestamp: new Date().toISOString(),\r\n level: 'INFO',\r\n scope: 'progress',\r\n message: message.trim(),\r\n }));\r\n }\r\n\r\n writeProgressEnd(message: string): void {\r\n console.log(JSON.stringify({\r\n timestamp: new Date().toISOString(),\r\n level: 'INFO',\r\n scope: 'progress',\r\n message: message.trim(),\r\n }));\r\n }\r\n}\r\n\r\n/**\r\n * Silent log sink that discards all output. Used when running as a library.\r\n */\r\nexport class SilentLogSink implements LogSink {\r\n write(_entry: LogEntry): void {}\r\n writeProgress(_message: string): void {}\r\n writeProgressEnd(_message: string): void {}\r\n}\r\n\r\n// ─── Logger Class ────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Logger with scope prefix and configurable sink/level.\r\n *\r\n * Usage:\r\n * ```ts\r\n * const log = createLogger('auth');\r\n * log.info('Token acquired', { expiresIn: '3600s' });\r\n * // Output: [INF] [auth] Token acquired\r\n * ```\r\n */\r\nexport class Logger {\r\n private readonly scope: string;\r\n private readonly getSink: () => LogSink;\r\n private readonly getMinLevel: () => LogLevel;\r\n\r\n constructor(scope: string, getSink: () => LogSink, getMinLevel: () => LogLevel) {\r\n this.scope = scope;\r\n this.getSink = getSink;\r\n this.getMinLevel = getMinLevel;\r\n }\r\n\r\n debug(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.DEBUG, message, context);\r\n }\r\n\r\n info(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.INFO, message, context);\r\n }\r\n\r\n warn(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.WARN, message, context);\r\n }\r\n\r\n error(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.ERROR, message, context);\r\n }\r\n\r\n /**\r\n * Write an inline progress update (no newline).\r\n */\r\n progress(message: string): void {\r\n if (this.getMinLevel() > LogLevel.INFO) return;\r\n this.getSink().writeProgress(message);\r\n }\r\n\r\n /**\r\n * Complete an inline progress line.\r\n */\r\n progressEnd(message: string): void {\r\n if (this.getMinLevel() > LogLevel.INFO) return;\r\n this.getSink().writeProgressEnd(message);\r\n }\r\n\r\n private log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\r\n if (level < this.getMinLevel()) return;\r\n\r\n this.getSink().write({\r\n level,\r\n scope: this.scope,\r\n message,\r\n context,\r\n timestamp: new Date(),\r\n });\r\n }\r\n}\r\n\r\n// ─── Global Configuration ────────────────────────────────────────────────────\r\n\r\nlet _sharedSink: LogSink = new ConsoleLogSink();\r\nlet _sharedMinLevel: LogLevel = LogLevel.INFO;\r\n\r\n/**\r\n * Configure logging globally for all @xrmforge modules.\r\n * Can be called at any time; existing loggers will pick up the new configuration\r\n * automatically because they reference the shared state via closures.\r\n *\r\n * @example\r\n * ```ts\r\n * configureLogging({ sink: new JsonLogSink(), minLevel: LogLevel.WARN });\r\n * ```\r\n */\r\nexport function configureLogging(options: { sink?: LogSink; minLevel?: LogLevel }): void {\r\n if (options.sink !== undefined) _sharedSink = options.sink;\r\n if (options.minLevel !== undefined) _sharedMinLevel = options.minLevel;\r\n}\r\n\r\n/**\r\n * Create a scoped logger instance. All modules should use this instead of console.log.\r\n *\r\n * The logger reads the global sink and minLevel at each log call (not at creation time),\r\n * so `configureLogging()` takes effect even on previously created loggers.\r\n *\r\n * @param scope - Module identifier shown in log output, e.g. \"auth\", \"metadata\", \"http\"\r\n */\r\nexport function createLogger(scope: string): Logger {\r\n return new Logger(\r\n scope,\r\n () => _sharedSink,\r\n () => _sharedMinLevel,\r\n );\r\n}\r\n","/**\r\n * @xrmforge/typegen - Authentication Module\r\n *\r\n * Handles authentication to Dataverse Web API using MSAL (@azure/identity).\r\n * Supports: Client Credentials (Service Principal), Interactive Browser, Device Code.\r\n *\r\n * Token acquisition and caching is handled by the DataverseHttpClient.\r\n * This module is responsible only for creating the correct TokenCredential.\r\n */\r\n\r\nimport {\r\n ClientSecretCredential,\r\n InteractiveBrowserCredential,\r\n DeviceCodeCredential,\r\n type TokenCredential,\r\n} from '@azure/identity';\r\nimport { AuthenticationError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('auth');\r\n\r\n// ─── Configuration Types ─────────────────────────────────────────────────────\r\n\r\nexport type AuthMethod = 'client-credentials' | 'interactive' | 'device-code' | 'token';\r\n\r\nexport interface ClientCredentialsAuth {\r\n method: 'client-credentials';\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface InteractiveAuth {\r\n method: 'interactive';\r\n tenantId?: string;\r\n clientId?: string;\r\n}\r\n\r\nexport interface DeviceCodeAuth {\r\n method: 'device-code';\r\n tenantId?: string;\r\n clientId?: string;\r\n}\r\n\r\nexport interface TokenAuth {\r\n method: 'token';\r\n /** Pre-acquired Bearer token (e.g. from TokenVault, Key Vault, CI/CD secret) */\r\n token: string;\r\n}\r\n\r\nexport type AuthConfig = ClientCredentialsAuth | InteractiveAuth | DeviceCodeAuth | TokenAuth;\r\n\r\n/**\r\n * Default App ID provided by Microsoft for dev/prototyping scenarios.\r\n * For production, create a dedicated App Registration in Azure AD.\r\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/xrm-tooling/use-connection-strings-xrm-tooling-connect\r\n */\r\nconst DEFAULT_CLIENT_ID = '51f81489-12ee-4a9e-aaae-a2591f45987d';\r\n\r\n// ─── Credential Factory ──────────────────────────────────────────────────────\r\n\r\n/**\r\n * Creates an Azure Identity TokenCredential from the provided auth configuration.\r\n * Validates required fields before attempting credential creation.\r\n *\r\n * @throws {AuthenticationError} if required configuration values are missing\r\n */\r\nexport function createCredential(config: AuthConfig): TokenCredential {\r\n switch (config.method) {\r\n case 'client-credentials':\r\n return createClientCredential(config);\r\n\r\n case 'interactive':\r\n return createInteractiveCredential(config);\r\n\r\n case 'device-code':\r\n return createDeviceCodeCredential(config);\r\n\r\n case 'token':\r\n return createStaticTokenCredential(config);\r\n\r\n default: {\r\n // Exhaustiveness check: this should never happen with proper TypeScript usage\r\n const exhaustiveCheck: never = config;\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_MISSING_CONFIG,\r\n `Unknown authentication method: \"${(exhaustiveCheck as AuthConfig).method}\". ` +\r\n `Supported methods: client-credentials, interactive, device-code.`,\r\n );\r\n }\r\n }\r\n}\r\n\r\n// ─── Internal Credential Builders ────────────────────────────────────────────\r\n\r\nfunction createClientCredential(config: ClientCredentialsAuth): TokenCredential {\r\n const missing: string[] = [];\r\n if (!config.tenantId?.trim()) missing.push('tenantId');\r\n if (!config.clientId?.trim()) missing.push('clientId');\r\n if (!config.clientSecret?.trim()) missing.push('clientSecret');\r\n\r\n if (missing.length > 0) {\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_MISSING_CONFIG,\r\n `Client credentials authentication requires: ${missing.join(', ')}. ` +\r\n `These can be set in xrmforge.config.json or via environment variables ` +\r\n `(XRMFORGE_TENANT_ID, XRMFORGE_CLIENT_ID, XRMFORGE_CLIENT_SECRET).`,\r\n { missingFields: missing },\r\n );\r\n }\r\n\r\n log.debug('Creating client credentials (Service Principal)', {\r\n tenantId: config.tenantId,\r\n clientId: config.clientId,\r\n });\r\n\r\n return new ClientSecretCredential(config.tenantId, config.clientId, config.clientSecret);\r\n}\r\n\r\nfunction createInteractiveCredential(config: InteractiveAuth): TokenCredential {\r\n const clientId = config.clientId?.trim() || DEFAULT_CLIENT_ID;\r\n\r\n log.debug('Creating interactive browser credential', {\r\n clientId,\r\n tenantId: config.tenantId ?? 'common',\r\n usingDefaultClientId: !config.clientId,\r\n });\r\n\r\n return new InteractiveBrowserCredential({\r\n tenantId: config.tenantId,\r\n clientId,\r\n });\r\n}\r\n\r\nfunction createDeviceCodeCredential(config: DeviceCodeAuth): TokenCredential {\r\n const clientId = config.clientId?.trim() || DEFAULT_CLIENT_ID;\r\n\r\n log.debug('Creating device code credential', {\r\n clientId,\r\n tenantId: config.tenantId ?? 'common',\r\n usingDefaultClientId: !config.clientId,\r\n });\r\n\r\n return new DeviceCodeCredential({\r\n tenantId: config.tenantId,\r\n clientId,\r\n userPromptCallback: (info) => {\r\n log.info('Device Code Authentication required:');\r\n log.info(info.message);\r\n },\r\n });\r\n}\r\n\r\nfunction createStaticTokenCredential(config: TokenAuth): TokenCredential {\r\n if (!config.token?.trim()) {\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_MISSING_CONFIG,\r\n 'Token authentication requires a non-empty token. ' +\r\n 'Set XRMFORGE_TOKEN environment variable or use --token flag.',\r\n );\r\n }\r\n\r\n log.debug('Using pre-acquired token (static credential)');\r\n\r\n return new StaticTokenCredential(config.token);\r\n}\r\n\r\n// ─── Static Token Credential ────────────────────────────────────────────────\r\n\r\n/**\r\n * A TokenCredential that wraps a pre-acquired Bearer token.\r\n * Useful for integration with external token sources like:\r\n * - Markant TokenVault (Get-VaultToken -System 'dataverse_dev')\r\n * - Azure Key Vault\r\n * - CI/CD pipeline secrets\r\n *\r\n * Note: This credential does NOT handle token refresh. If the token expires,\r\n * the next API call will fail with HTTP 401.\r\n */\r\nexport class StaticTokenCredential implements TokenCredential {\r\n private readonly token: string;\r\n\r\n constructor(token: string) {\r\n this.token = token;\r\n }\r\n\r\n async getToken(): Promise<{ token: string; expiresOnTimestamp: number }> {\r\n // Return the static token with a far-future expiry.\r\n // The HttpClient's 401-retry logic will clear the cache and call getToken again,\r\n // but since we have no refresh mechanism, it will return the same (possibly expired) token.\r\n return {\r\n token: this.token,\r\n expiresOnTimestamp: Date.now() + 60 * 60 * 1000, // Pretend 1 hour validity\r\n };\r\n }\r\n}\r\n\r\n","/**\r\n * @xrmforge/typegen - Dataverse HTTP Client\r\n *\r\n * Resilient HTTP client for the Dataverse Web API.\r\n *\r\n * Features:\r\n * - Token caching with 5-minute buffer before expiry\r\n * - Automatic retry with exponential backoff and jitter\r\n * - Rate limit awareness (HTTP 429 with Retry-After)\r\n * - Request timeout via AbortController\r\n * - Concurrency control (semaphore pattern, NOT recursive)\r\n * - Automatic OData paging via @odata.nextLink with safety limit\r\n * - Input sanitization helpers against OData injection\r\n */\r\n\r\nimport type { TokenCredential, AccessToken } from '@azure/identity';\r\nimport { ApiRequestError, AuthenticationError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('http');\r\n\r\n// ─── Internal Constants ──────────────────────────────────────────────────────\r\n\r\n/** Buffer before token expiry to trigger re-acquisition (5 minutes) */\r\nconst TOKEN_BUFFER_MS = 5 * 60 * 1000;\r\n\r\n/** Maximum backoff delay cap for exponential retry (60 seconds) */\r\nconst MAX_BACKOFF_MS = 60_000;\r\n\r\n/** Maximum characters of response body stored in error context */\r\nconst MAX_RESPONSE_BODY_LENGTH = 2000;\r\n\r\n/** Maximum length of user-provided values in error messages (prevents log injection) */\r\nconst MAX_ERROR_VALUE_LENGTH = 100;\r\n\r\n/** Maximum consecutive HTTP 429 retries before giving up (prevents infinite loops) */\r\nconst DEFAULT_MAX_RATE_LIMIT_RETRIES = 10;\r\n\r\n// ─── Configuration ───────────────────────────────────────────────────────────\r\n\r\nexport interface HttpClientOptions {\r\n /** Dataverse environment URL, e.g. \"https://myorg.crm4.dynamics.com\" */\r\n environmentUrl: string;\r\n\r\n /** Azure Identity credential */\r\n credential: TokenCredential;\r\n\r\n /** API version (default: \"v9.2\") */\r\n apiVersion?: string;\r\n\r\n /** Maximum retry attempts for transient errors (default: 3) */\r\n maxRetries?: number;\r\n\r\n /** Base delay in ms for exponential backoff (default: 1000) */\r\n retryBaseDelayMs?: number;\r\n\r\n /** Request timeout in ms (default: 30000) */\r\n timeoutMs?: number;\r\n\r\n /** Maximum concurrent requests to Dataverse (default: 5) */\r\n maxConcurrency?: number;\r\n\r\n /** Maximum pages to follow via @odata.nextLink (default: 100, safety limit) */\r\n maxPages?: number;\r\n\r\n /** Maximum consecutive HTTP 429 retries before giving up (default: 10) */\r\n maxRateLimitRetries?: number;\r\n\r\n /**\r\n * Read-only mode (default: true).\r\n * When true, the client will ONLY allow GET requests and throw an error\r\n * for any POST, PATCH, PUT, or DELETE attempt.\r\n *\r\n * SAFETY: XrmForge typegen is a read-only tool. It must NEVER modify\r\n * data in Dataverse environments. This flag defaults to true and should\r\n * only be set to false for the @xrmforge/webapi package (future).\r\n */\r\n readOnly?: boolean;\r\n}\r\n\r\n// ─── Token Cache ─────────────────────────────────────────────────────────────\r\n\r\ninterface CachedToken {\r\n token: string;\r\n expiresAt: number; // Unix timestamp in ms\r\n}\r\n\r\n// ─── Client ──────────────────────────────────────────────────────────────────\r\n\r\nexport class DataverseHttpClient {\r\n private readonly baseUrl: string;\r\n private readonly apiVersion: string;\r\n private readonly credential: TokenCredential;\r\n private readonly maxRetries: number;\r\n private readonly retryBaseDelayMs: number;\r\n private readonly timeoutMs: number;\r\n private readonly maxConcurrency: number;\r\n private readonly maxPages: number;\r\n private readonly maxRateLimitRetries: number;\r\n private readonly readOnly: boolean;\r\n\r\n private cachedToken: CachedToken | null = null;\r\n /** Pending token refresh promise (prevents concurrent token requests) */\r\n private pendingTokenRefresh: Promise<string> | null = null;\r\n\r\n // Semaphore for concurrency control (non-recursive)\r\n private activeConcurrentRequests = 0;\r\n private readonly waitQueue: Array<() => void> = [];\r\n\r\n constructor(options: HttpClientOptions) {\r\n this.baseUrl = options.environmentUrl.replace(/\\/$/, '');\r\n this.apiVersion = options.apiVersion ?? 'v9.2';\r\n this.credential = options.credential;\r\n this.maxRetries = options.maxRetries ?? 3;\r\n this.retryBaseDelayMs = options.retryBaseDelayMs ?? 1000;\r\n this.timeoutMs = options.timeoutMs ?? 30_000;\r\n this.maxConcurrency = options.maxConcurrency ?? 5;\r\n this.maxPages = options.maxPages ?? 100;\r\n this.maxRateLimitRetries = options.maxRateLimitRetries ?? DEFAULT_MAX_RATE_LIMIT_RETRIES;\r\n this.readOnly = options.readOnly ?? true; // SAFETY: default to read-only\r\n }\r\n\r\n /**\r\n * Full API base URL, e.g. \"https://myorg.crm4.dynamics.com/api/data/v9.2\"\r\n */\r\n get apiUrl(): string {\r\n return `${this.baseUrl}/api/data/${this.apiVersion}`;\r\n }\r\n\r\n // ─── Public API ──────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute a GET request against the Dataverse Web API.\r\n * Handles token caching, retries, rate limits, and timeout.\r\n *\r\n * @param path - API path (relative or absolute URL)\r\n * @param signal - Optional AbortSignal to cancel the request\r\n */\r\n async get<T>(path: string, signal?: AbortSignal): Promise<T> {\r\n const url = this.resolveUrl(path);\r\n return this.executeWithConcurrency<T>(url, signal);\r\n }\r\n\r\n /**\r\n * Execute a GET request and automatically follow @odata.nextLink for paging.\r\n * Returns all pages combined into a single array.\r\n *\r\n * Safety: Stops after `maxPages` iterations to prevent infinite loops.\r\n *\r\n * @param path - API path (relative or absolute URL)\r\n * @param signal - Optional AbortSignal to cancel the request\r\n */\r\n async getAll<T>(path: string, signal?: AbortSignal): Promise<T[]> {\r\n interface ODataPage {\r\n value: T[];\r\n '@odata.nextLink'?: string;\r\n }\r\n\r\n const allResults: T[] = [];\r\n let currentUrl: string | null = this.resolveUrl(path);\r\n let page = 0;\r\n\r\n while (currentUrl) {\r\n // Check abort before each page\r\n if (signal?.aborted) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Request aborted after ${page} pages (${allResults.length} records retrieved)`,\r\n { url: currentUrl, pagesCompleted: page },\r\n );\r\n }\r\n\r\n page++;\r\n if (page > this.maxPages) {\r\n log.warn(\r\n `Stopped paging after ${this.maxPages} pages (safety limit). ` +\r\n `${allResults.length} records retrieved. Increase maxPages if this is expected.`,\r\n );\r\n break;\r\n }\r\n\r\n const response: ODataPage = await this.executeWithConcurrency<ODataPage>(currentUrl, signal);\r\n\r\n allResults.push(...response.value);\r\n currentUrl = response['@odata.nextLink'] ?? null;\r\n\r\n if (currentUrl) {\r\n log.debug(`Following @odata.nextLink (page ${page}), ${allResults.length} records so far`);\r\n }\r\n }\r\n\r\n return allResults;\r\n }\r\n\r\n // ─── Read-Only Enforcement ─────────────────────────────────────────────\r\n\r\n /**\r\n * Returns true if this client is in read-only mode (the safe default).\r\n */\r\n get isReadOnly(): boolean {\r\n return this.readOnly;\r\n }\r\n\r\n /**\r\n * Assert that a non-GET operation is allowed.\r\n * Throws immediately if the client is in read-only mode.\r\n *\r\n * @throws {ApiRequestError} always in read-only mode\r\n * @internal This method exists so that future packages (e.g. @xrmforge/webapi)\r\n * can reuse the HTTP client for write operations when readOnly is explicitly false.\r\n */\r\n assertWriteAllowed(operation: string): void {\r\n if (this.readOnly) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `BLOCKED: Write operation \"${operation}\" rejected. ` +\r\n `This client is in read-only mode (readOnly: true). ` +\r\n `XrmForge typegen must NEVER modify data in Dataverse. ` +\r\n `Set readOnly: false only for @xrmforge/webapi (not for typegen).`,\r\n { operation, readOnly: true },\r\n );\r\n }\r\n }\r\n\r\n // ─── Input Sanitization ──────────────────────────────────────────────────\r\n\r\n /**\r\n * Validate that a value is a safe OData identifier (entity name, attribute name).\r\n * Prevents OData injection by allowing only: starts with letter/underscore,\r\n * followed by alphanumeric/underscore.\r\n *\r\n * @throws {ApiRequestError} if the value contains invalid characters\r\n */\r\n static sanitizeIdentifier(value: string): string {\r\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Invalid OData identifier: \"${value.substring(0, MAX_ERROR_VALUE_LENGTH).replace(/[\\r\\n]/g, '')}\". ` +\r\n `Only letters, digits, and underscores allowed; must start with a letter or underscore.`,\r\n { value: value.substring(0, MAX_ERROR_VALUE_LENGTH) },\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n /**\r\n * Validate that a value is a properly formatted GUID.\r\n *\r\n * @throws {ApiRequestError} if the format is invalid\r\n */\r\n static sanitizeGuid(value: string): string {\r\n const guidPattern =\r\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\r\n if (!guidPattern.test(value)) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Invalid GUID format: \"${value}\".`,\r\n { value },\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n /**\r\n * Escape a string for use inside OData single-quoted string literals.\r\n * Doubles single quotes to prevent injection.\r\n */\r\n static escapeODataString(value: string): string {\r\n return value.replace(/'/g, \"''\");\r\n }\r\n\r\n // ─── Token Management ────────────────────────────────────────────────────\r\n\r\n private async getToken(): Promise<string> {\r\n // Return cached token if it has more than 5 minutes of validity remaining\r\n if (this.cachedToken && this.cachedToken.expiresAt - Date.now() > TOKEN_BUFFER_MS) {\r\n return this.cachedToken.token;\r\n }\r\n\r\n // If a refresh is already in progress, wait for it instead of starting a second one\r\n if (this.pendingTokenRefresh) {\r\n return this.pendingTokenRefresh;\r\n }\r\n\r\n this.pendingTokenRefresh = this.refreshToken();\r\n try {\r\n return await this.pendingTokenRefresh;\r\n } finally {\r\n this.pendingTokenRefresh = null;\r\n }\r\n }\r\n\r\n /** Internal: actually acquire a new token from the credential provider. */\r\n private async refreshToken(): Promise<string> {\r\n log.debug('Requesting new access token');\r\n\r\n const scope = `${this.baseUrl}/.default`;\r\n let tokenResponse: AccessToken | null;\r\n\r\n try {\r\n tokenResponse = await this.credential.getToken(scope);\r\n } catch (error: unknown) {\r\n const cause = error instanceof Error ? error.message : String(error);\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_TOKEN_FAILED,\r\n `Failed to acquire access token for ${this.baseUrl}. ` +\r\n `Verify your authentication configuration.\\n` +\r\n `Cause: ${cause}`,\r\n {\r\n environmentUrl: this.baseUrl,\r\n originalError: cause,\r\n },\r\n );\r\n }\r\n\r\n if (!tokenResponse) {\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_TOKEN_FAILED,\r\n `No access token returned for ${this.baseUrl}. ` +\r\n `This may indicate invalid credentials or insufficient permissions.`,\r\n { environmentUrl: this.baseUrl },\r\n );\r\n }\r\n\r\n this.cachedToken = {\r\n token: tokenResponse.token,\r\n expiresAt: tokenResponse.expiresOnTimestamp,\r\n };\r\n\r\n log.debug('Access token acquired', {\r\n expiresIn: `${Math.round((tokenResponse.expiresOnTimestamp - Date.now()) / 1000)}s`,\r\n });\r\n\r\n return tokenResponse.token;\r\n }\r\n\r\n // ─── Concurrency Control ─────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute a request within the concurrency semaphore.\r\n * The semaphore is acquired ONCE per logical request. Retries happen\r\n * INSIDE the semaphore to avoid the recursive slot exhaustion bug.\r\n */\r\n private async executeWithConcurrency<T>(url: string, signal?: AbortSignal, method: 'GET' | 'POST' = 'GET', requestBody?: unknown): Promise<T> {\r\n await this.acquireSlot();\r\n try {\r\n return await this.executeWithRetry<T>(url, 1, 0, signal, method, requestBody);\r\n } finally {\r\n this.releaseSlot();\r\n }\r\n }\r\n\r\n private acquireSlot(): Promise<void> {\r\n if (this.activeConcurrentRequests < this.maxConcurrency) {\r\n this.activeConcurrentRequests++;\r\n return Promise.resolve();\r\n }\r\n\r\n return new Promise<void>((resolve) => {\r\n this.waitQueue.push(() => {\r\n this.activeConcurrentRequests++;\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n private releaseSlot(): void {\r\n this.activeConcurrentRequests--;\r\n const next = this.waitQueue.shift();\r\n if (next) next();\r\n }\r\n\r\n // ─── Retry Logic (runs INSIDE a single concurrency slot) ─────────────────\r\n\r\n private async executeWithRetry<T>(url: string, attempt: number, rateLimitRetries: number = 0, signal?: AbortSignal, method: 'GET' | 'POST' = 'GET', requestBody?: unknown): Promise<T> {\r\n // Check if already aborted before starting\r\n if (signal?.aborted) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n 'Request aborted before execution',\r\n { url },\r\n );\r\n }\r\n\r\n const token = await this.getToken();\r\n\r\n // Combine user's AbortSignal with per-request timeout\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\r\n\r\n // If the user's signal fires, abort our controller too\r\n const onUserAbort = () => controller.abort();\r\n signal?.addEventListener('abort', onUserAbort, { once: true });\r\n\r\n let response: Response;\r\n try {\r\n log.debug(`${method} ${url}`, { attempt });\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${token}`,\r\n 'OData-MaxVersion': '4.0',\r\n 'OData-Version': '4.0',\r\n Accept: 'application/json',\r\n Prefer: 'odata.include-annotations=\"*\"',\r\n };\r\n if (method === 'POST') {\r\n headers['Content-Type'] = 'application/json';\r\n }\r\n\r\n response = await fetch(url, {\r\n method,\r\n headers,\r\n body: requestBody !== undefined ? JSON.stringify(requestBody) : undefined,\r\n signal: controller.signal,\r\n });\r\n } catch (fetchError: unknown) {\r\n clearTimeout(timeoutId);\r\n signal?.removeEventListener('abort', onUserAbort);\r\n\r\n // Distinguish user abort from timeout\r\n if ((fetchError instanceof Error) && fetchError.name === 'AbortError') {\r\n // If the USER aborted (not our timeout), don't retry\r\n if (signal?.aborted) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n 'Request aborted by caller',\r\n { url, attempt },\r\n );\r\n }\r\n\r\n // Timeout: retry if attempts remain\r\n if (attempt <= this.maxRetries) {\r\n const delay = this.calculateBackoff(attempt);\r\n log.warn(`Request timed out, retrying in ${delay}ms (${attempt}/${this.maxRetries})`, {\r\n url,\r\n });\r\n await this.sleep(delay);\r\n return this.executeWithRetry<T>(url, attempt + 1, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n throw new ApiRequestError(\r\n ErrorCode.API_TIMEOUT,\r\n `Request timed out after ${this.timeoutMs}ms (${this.maxRetries} retries exhausted)`,\r\n { url, timeoutMs: this.timeoutMs },\r\n );\r\n }\r\n\r\n // Network error\r\n if (attempt <= this.maxRetries) {\r\n const delay = this.calculateBackoff(attempt);\r\n log.warn(`Network error, retrying in ${delay}ms (${attempt}/${this.maxRetries})`, {\r\n url,\r\n error: fetchError instanceof Error ? fetchError.message : String(fetchError),\r\n });\r\n await this.sleep(delay);\r\n return this.executeWithRetry<T>(url, attempt + 1, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Network error after ${this.maxRetries} retries`,\r\n {\r\n url,\r\n originalError: fetchError instanceof Error ? fetchError.message : String(fetchError),\r\n },\r\n );\r\n } finally {\r\n clearTimeout(timeoutId);\r\n signal?.removeEventListener('abort', onUserAbort);\r\n }\r\n\r\n // ── Handle HTTP Errors ──\r\n\r\n if (!response.ok) {\r\n return this.handleHttpError<T>(response, url, attempt, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n log.debug(`${method} ${url} -> ${response.status}`, { attempt });\r\n return response.json() as Promise<T>;\r\n }\r\n\r\n private async handleHttpError<T>(\r\n response: Response,\r\n url: string,\r\n attempt: number,\r\n rateLimitRetries: number,\r\n signal?: AbortSignal,\r\n method: 'GET' | 'POST' = 'GET',\r\n requestBody?: unknown,\r\n ): Promise<T> {\r\n const body = await response.text();\r\n\r\n // 429 Rate Limited: retry with separate counter to prevent infinite loops\r\n if (response.status === 429) {\r\n if (rateLimitRetries >= this.maxRateLimitRetries) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_RATE_LIMITED,\r\n `Rate limit retries exhausted (${this.maxRateLimitRetries} consecutive 429 responses)`,\r\n { url, rateLimitRetries },\r\n );\r\n }\r\n\r\n const retryAfterHeader = response.headers.get('Retry-After');\r\n const retryAfterMs = retryAfterHeader\r\n ? parseInt(retryAfterHeader, 10) * 1000\r\n : this.calculateBackoff(rateLimitRetries + 1);\r\n\r\n log.warn(`Rate limited (HTTP 429). Waiting ${retryAfterMs}ms (${rateLimitRetries + 1}/${this.maxRateLimitRetries}).`, {\r\n url,\r\n retryAfterHeader,\r\n });\r\n\r\n await this.sleep(retryAfterMs);\r\n // Do NOT increment attempt for 429: these are not \"failures\", they are throttling\r\n return this.executeWithRetry<T>(url, attempt, rateLimitRetries + 1, signal, method, requestBody);\r\n }\r\n\r\n // 401 Unauthorized: clear token cache and retry once\r\n if (response.status === 401 && attempt === 1) {\r\n log.warn('HTTP 401 received, clearing token cache and retrying');\r\n this.cachedToken = null;\r\n return this.executeWithRetry<T>(url, attempt + 1, 0, signal, method, requestBody);\r\n }\r\n\r\n // 5xx Server Errors: retryable\r\n if (response.status >= 500 && attempt <= this.maxRetries) {\r\n const delay = this.calculateBackoff(attempt);\r\n log.warn(\r\n `Server error ${response.status}, retrying in ${delay}ms (${attempt}/${this.maxRetries})`,\r\n );\r\n await this.sleep(delay);\r\n return this.executeWithRetry<T>(url, attempt + 1, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n // Non-retryable error: throw\r\n const errorCode =\r\n response.status === 401\r\n ? ErrorCode.API_UNAUTHORIZED\r\n : response.status === 404\r\n ? ErrorCode.API_NOT_FOUND\r\n : ErrorCode.API_REQUEST_FAILED;\r\n\r\n throw new ApiRequestError(\r\n errorCode,\r\n `Dataverse API error: HTTP ${response.status} ${response.statusText}`,\r\n {\r\n url,\r\n statusCode: response.status,\r\n responseBody: body.substring(0, MAX_RESPONSE_BODY_LENGTH),\r\n },\r\n );\r\n }\r\n\r\n // ─── Helpers ─────────────────────────────────────────────────────────────\r\n\r\n private resolveUrl(path: string): string {\r\n return path.startsWith('http') ? path : `${this.apiUrl}${path}`;\r\n }\r\n\r\n private calculateBackoff(attempt: number): number {\r\n const exponential = this.retryBaseDelayMs * Math.pow(2, attempt - 1);\r\n const jitter = Math.random() * this.retryBaseDelayMs;\r\n return Math.min(exponential + jitter, MAX_BACKOFF_MS);\r\n }\r\n\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n }\r\n}\r\n","/**\r\n * @xrmforge/typegen - XML Parser Abstraction\r\n *\r\n * Interface for XML parsing, decoupled from any specific parser library.\r\n * Allows swapping the underlying parser (currently fast-xml-parser)\r\n * by implementing a single interface. (Goldene Regel 14)\r\n */\r\n\r\n// ─── Parser Interface ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Parsed XML element with attributes and children.\r\n */\r\nexport interface XmlElement {\r\n /** Element tag name */\r\n tag: string;\r\n /** Element attributes as key-value pairs */\r\n attributes: Record<string, string>;\r\n /** Child elements */\r\n children: XmlElement[];\r\n /** Text content (if any) */\r\n text?: string;\r\n}\r\n\r\n/**\r\n * Abstraction for XML parsing. Implementations must convert an XML string\r\n * into a tree of XmlElement objects.\r\n *\r\n * To swap the underlying parser library, implement this interface\r\n * and pass it to the FormXml parser. Only this file needs to change.\r\n */\r\nexport interface XmlParser {\r\n parse(xml: string): XmlElement;\r\n}\r\n\r\n// ─── fast-xml-parser Implementation ──────────────────────────────────────────\r\n\r\nimport { XMLParser } from 'fast-xml-parser';\r\n\r\n/**\r\n * XML parser implementation using fast-xml-parser.\r\n * Zero dependencies (fast-xml-parser itself has none), 26 KB minified.\r\n */\r\nexport class FastXmlParser implements XmlParser {\r\n private readonly parser: XMLParser;\r\n\r\n constructor() {\r\n this.parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: '@_',\r\n allowBooleanAttributes: true,\r\n preserveOrder: true,\r\n trimValues: true,\r\n });\r\n }\r\n\r\n parse(xml: string): XmlElement {\r\n const result = this.parser.parse(xml);\r\n return this.convertToXmlElement(result);\r\n }\r\n\r\n private convertToXmlElement(parsed: unknown): XmlElement {\r\n if (!Array.isArray(parsed) || parsed.length === 0) {\r\n return { tag: 'root', attributes: {}, children: [] };\r\n }\r\n\r\n // fast-xml-parser with preserveOrder returns an array of objects\r\n // Each object has one key (the tag name) and optionally :@_attrs\r\n const children = parsed.map((item: Record<string, unknown>) => this.convertNode(item));\r\n\r\n if (children.length === 1) {\r\n return children[0]!;\r\n }\r\n\r\n return { tag: 'root', attributes: {}, children };\r\n }\r\n\r\n private convertNode(node: Record<string, unknown>): XmlElement {\r\n const attrs: Record<string, string> = {};\r\n let tag = '';\r\n let children: XmlElement[] = [];\r\n let text: string | undefined;\r\n\r\n for (const key of Object.keys(node)) {\r\n if (key === ':@') {\r\n // Attributes object\r\n const attrObj = node[key] as Record<string, string>;\r\n for (const [attrKey, attrVal] of Object.entries(attrObj)) {\r\n // Remove the @_ prefix added by fast-xml-parser\r\n const cleanKey = attrKey.startsWith('@_') ? attrKey.slice(2) : attrKey;\r\n attrs[cleanKey] = String(attrVal);\r\n }\r\n } else if (key === '#text') {\r\n text = String(node[key]);\r\n } else {\r\n tag = key;\r\n const childContent = node[key];\r\n if (Array.isArray(childContent)) {\r\n children = childContent.map((child: Record<string, unknown>) => this.convertNode(child));\r\n }\r\n }\r\n }\r\n\r\n return { tag, attributes: attrs, children, text };\r\n }\r\n}\r\n\r\n// ─── Default Instance ────────────────────────────────────────────────────────\r\n\r\n/** Default parser instance. Use this unless you need a custom parser. */\r\nexport const defaultXmlParser: XmlParser = new FastXmlParser();\r\n","/**\r\n * @xrmforge/typegen - FormXml Parser\r\n *\r\n * Parses Dataverse FormXml into structured TypeScript objects.\r\n * Extracts tabs, sections, controls (data-bound + special) for generating\r\n * typed FormContext interfaces with compile-time field validation.\r\n *\r\n * Uses the XmlParser abstraction (Goldene Regel 14) instead of regex.\r\n */\r\n\r\nimport type { FormControl, FormSpecialControl, FormTab, FormSection, ParsedForm, SystemFormMetadata, SpecialControlType } from './types.js';\r\nimport type { XmlParser, XmlElement } from './xml-parser.js';\r\nimport { defaultXmlParser } from './xml-parser.js';\r\nimport { MetadataError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('form-parser');\r\n\r\n// ─── Known Control ClassIds ─────────────────────────────────────────────────\r\n// Extracted from real FormXml across 25 Markant entities (2026-03-30)\r\n\r\n/** Well-known classid GUIDs for special (non-data-bound) controls */\r\nconst SPECIAL_CONTROL_CLASSIDS: Record<string, SpecialControlType> = {\r\n // Subgrid (read-only list of related records)\r\n 'e7a81278-8635-4d9e-8d4d-59480b391c5b': 'subgrid',\r\n // Editable Grid (inline-editable list)\r\n '02d4264b-47e2-4b4c-aa95-f439f3f4d458': 'editablegrid',\r\n // Quick View Form (embedded read-only form of related record)\r\n '5c5600e0-1d6e-4205-a272-be80da87fd42': 'quickview',\r\n // Web Resource (custom HTML/JS content)\r\n '9fdf5f91-88b1-47f4-ad53-c11efc01a01d': 'webresource',\r\n // Bing Map\r\n '62b0df79-0464-470f-8af7-4483cfea0c7d': 'map',\r\n // Notes/Timeline\r\n '06375649-c143-495e-a496-c962e5b4488e': 'notes',\r\n};\r\n\r\n/**\r\n * Parse a SystemFormMetadata response into a structured ParsedForm.\r\n *\r\n * @param form - The system form metadata from the API\r\n * @param parser - XML parser to use (defaults to fast-xml-parser)\r\n * @throws {MetadataError} if the formxml cannot be parsed\r\n */\r\nexport function parseForm(form: SystemFormMetadata, parser: XmlParser = defaultXmlParser): ParsedForm {\r\n const tabs = parseTabs(form.formxml, form.name, parser);\r\n const allControls = tabs.flatMap((tab) =>\r\n tab.sections.flatMap((section) => section.controls),\r\n );\r\n const allSpecialControls = tabs.flatMap((tab) =>\r\n tab.sections.flatMap((section) => section.specialControls),\r\n );\r\n\r\n return {\r\n name: form.name,\r\n formId: form.formid,\r\n isDefault: form.isdefault,\r\n tabs,\r\n allControls,\r\n allSpecialControls,\r\n };\r\n}\r\n\r\n/**\r\n * Extract all data-bound control field names from FormXml (flattened).\r\n * Simpler alternative to full parsing when only the field list is needed.\r\n */\r\nexport function extractControlFields(formxml: string, parser: XmlParser = defaultXmlParser): string[] {\r\n if (!formxml || formxml.trim().length === 0) {\r\n return [];\r\n }\r\n\r\n try {\r\n const root = parser.parse(formxml);\r\n const fields: string[] = [];\r\n collectDatafieldNames(root, fields);\r\n return fields;\r\n } catch {\r\n log.warn('Failed to parse formxml for field extraction, returning empty list');\r\n return [];\r\n }\r\n}\r\n\r\n// ─── Internal Parsing ────────────────────────────────────────────────────────\r\n\r\nfunction parseTabs(formxml: string, formName: string, parser: XmlParser): FormTab[] {\r\n if (!formxml || formxml.trim().length === 0) {\r\n log.warn(`Empty formxml for form \"${formName}\"`);\r\n return [];\r\n }\r\n\r\n let root: XmlElement;\r\n try {\r\n root = parser.parse(formxml);\r\n } catch (error: unknown) {\r\n throw new MetadataError(\r\n ErrorCode.META_FORM_PARSE_FAILED,\r\n `Failed to parse FormXml for form \"${formName}\"`,\r\n {\r\n formName,\r\n originalError: error instanceof Error ? error.message : String(error),\r\n },\r\n );\r\n }\r\n\r\n const tabs: FormTab[] = [];\r\n const tabElements = findElements(root, 'tab');\r\n\r\n for (const tabEl of tabElements) {\r\n const tabName = tabEl.attributes['name'] ?? '';\r\n const tabVisible = tabEl.attributes['visible'] !== 'false';\r\n\r\n // Extract tab label from <labels><label description=\"...\" /></labels>\r\n const tabLabel = extractLabel(tabEl);\r\n\r\n const sections = parseSections(tabEl);\r\n tabs.push({ name: tabName, label: tabLabel, visible: tabVisible, sections });\r\n }\r\n\r\n return tabs;\r\n}\r\n\r\nfunction parseSections(tabElement: XmlElement): FormSection[] {\r\n const sections: FormSection[] = [];\r\n const sectionElements = findElements(tabElement, 'section');\r\n\r\n for (const sectionEl of sectionElements) {\r\n const sectionName = sectionEl.attributes['name'] ?? '';\r\n const sectionVisible = sectionEl.attributes['visible'] !== 'false';\r\n const sectionLabel = extractLabel(sectionEl);\r\n\r\n const controls = parseDataControls(sectionEl);\r\n const specialControls = parseSpecialControls(sectionEl);\r\n\r\n sections.push({\r\n name: sectionName,\r\n label: sectionLabel,\r\n visible: sectionVisible,\r\n controls,\r\n specialControls,\r\n });\r\n }\r\n\r\n return sections;\r\n}\r\n\r\n/** Parse data-bound controls (controls with a datafieldname attribute) */\r\nfunction parseDataControls(sectionElement: XmlElement): FormControl[] {\r\n const controls: FormControl[] = [];\r\n const controlElements = findElements(sectionElement, 'control');\r\n\r\n for (const controlEl of controlElements) {\r\n const datafieldname = controlEl.attributes['datafieldname'];\r\n if (!datafieldname) continue; // Special controls are handled separately\r\n\r\n controls.push({\r\n id: controlEl.attributes['id'] ?? '',\r\n datafieldname,\r\n classid: controlEl.attributes['classid'] ?? '',\r\n });\r\n }\r\n\r\n return controls;\r\n}\r\n\r\n/** Parse special controls (subgrids, quick views, web resources, etc.) */\r\nfunction parseSpecialControls(sectionElement: XmlElement): FormSpecialControl[] {\r\n const controls: FormSpecialControl[] = [];\r\n const controlElements = findElements(sectionElement, 'control');\r\n\r\n for (const controlEl of controlElements) {\r\n // Only process controls WITHOUT datafieldname (these are special controls)\r\n if (controlEl.attributes['datafieldname']) continue;\r\n\r\n const classid = (controlEl.attributes['classid'] ?? '').replace(/[{}]/g, '').toLowerCase();\r\n const controlType = SPECIAL_CONTROL_CLASSIDS[classid];\r\n\r\n // Skip controls we don't recognize (standard field controls without datafieldname are noise)\r\n if (!controlType) continue;\r\n\r\n const special: FormSpecialControl = {\r\n id: controlEl.attributes['id'] ?? '',\r\n classid,\r\n controlType,\r\n };\r\n\r\n // Extract parameters for subgrids and quick views\r\n if (controlType === 'subgrid' || controlType === 'editablegrid') {\r\n special.targetEntityType = extractParameter(controlEl, 'TargetEntityType');\r\n special.relationshipName = extractParameter(controlEl, 'RelationshipName');\r\n }\r\n\r\n if (controlType === 'webresource') {\r\n special.webResourceName = extractParameter(controlEl, 'Url');\r\n }\r\n\r\n controls.push(special);\r\n }\r\n\r\n return controls;\r\n}\r\n\r\n// ─── XML Tree Navigation ─────────────────────────────────────────────────────\r\n\r\n/** Recursively find all elements with a given tag name */\r\nfunction findElements(element: XmlElement, tagName: string): XmlElement[] {\r\n const results: XmlElement[] = [];\r\n\r\n if (element.tag === tagName) {\r\n results.push(element);\r\n }\r\n\r\n for (const child of element.children) {\r\n results.push(...findElements(child, tagName));\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/** Recursively collect all datafieldname attributes from control elements */\r\nfunction collectDatafieldNames(element: XmlElement, fields: string[]): void {\r\n if (element.tag === 'control') {\r\n const name = element.attributes['datafieldname'];\r\n if (name && !fields.includes(name)) {\r\n fields.push(name);\r\n }\r\n }\r\n\r\n for (const child of element.children) {\r\n collectDatafieldNames(child, fields);\r\n }\r\n}\r\n\r\n/** Extract label text from a <labels><label description=\"...\" /></labels> child */\r\nfunction extractLabel(element: XmlElement): string | undefined {\r\n const labelsEl = element.children.find((c) => c.tag === 'labels');\r\n if (!labelsEl) return undefined;\r\n\r\n const labelEl = labelsEl.children.find((c) => c.tag === 'label');\r\n if (!labelEl) return undefined;\r\n\r\n return labelEl.attributes['description'] || undefined;\r\n}\r\n\r\n/** Extract a parameter value from <parameters><ParameterName>Value</ParameterName></parameters> */\r\nfunction extractParameter(controlElement: XmlElement, paramName: string): string | undefined {\r\n const paramsEl = controlElement.children.find((c) => c.tag === 'parameters');\r\n if (!paramsEl) return undefined;\r\n\r\n const paramEl = paramsEl.children.find((c) => c.tag === paramName);\r\n if (!paramEl) return undefined;\r\n\r\n return paramEl.text || undefined;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Metadata Client\r\n *\r\n * High-level client for querying Dataverse Metadata API endpoints.\r\n * Built on top of DataverseHttpClient for resilient HTTP communication.\r\n *\r\n * Provides methods for:\r\n * - Entity metadata with attributes\r\n * - Typed attribute queries (Picklist, Lookup, Status/State)\r\n * - Form metadata (SystemForms + FormXml parsing)\r\n * - Global OptionSet definitions\r\n * - Solution-based entity filtering\r\n * - Relationship metadata (1:N, N:N)\r\n */\r\n\r\nimport { DataverseHttpClient } from '../http/client.js';\r\nimport { MetadataError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\nimport { parseForm } from './form-parser.js';\r\nimport type {\r\n EntityMetadata,\r\n PicklistAttributeMetadata,\r\n LookupAttributeMetadata,\r\n StatusAttributeMetadata,\r\n StateAttributeMetadata,\r\n OptionSetMetadata,\r\n SystemFormMetadata,\r\n ParsedForm,\r\n OneToManyRelationshipMetadata,\r\n ManyToManyRelationshipMetadata,\r\n SolutionComponent,\r\n EntityTypeInfo,\r\n} from './types.js';\r\nimport type {\r\n CustomApiTypeInfo,\r\n CustomApiMetadata,\r\n CustomApiRequestParameter,\r\n CustomApiResponseProperty,\r\n} from './custom-api-types.js';\r\n\r\nconst log = createLogger('metadata');\r\n\r\n// ─── OData Response Wrappers ─────────────────────────────────────────────────\r\n\r\ninterface ODataCollection<T> {\r\n value: T[];\r\n}\r\n\r\ninterface SolutionRecord {\r\n solutionid: string;\r\n uniquename: string;\r\n friendlyname: string;\r\n}\r\n\r\n// ─── Dataverse Constants ─────────────────────────────────────────────────────\r\n\r\n/** Dataverse SystemForm type code for Main forms */\r\nconst FORM_TYPE_MAIN = 2;\r\n\r\n/** Dataverse SolutionComponent type code for Entity */\r\nconst COMPONENT_TYPE_ENTITY = 1;\r\n\r\n// ─── Select Constants ────────────────────────────────────────────────────────\r\n\r\nconst ENTITY_SELECT = 'LogicalName,SchemaName,EntitySetName,DisplayName,PrimaryIdAttribute,PrimaryNameAttribute,OwnershipType,IsCustomEntity,LogicalCollectionName,MetadataId';\r\nconst ATTRIBUTE_SELECT = 'LogicalName,SchemaName,AttributeType,AttributeTypeName,DisplayName,IsPrimaryId,IsPrimaryName,RequiredLevel,IsValidForRead,IsValidForCreate,IsValidForUpdate,MetadataId';\r\nconst FORM_SELECT = 'name,formid,formxml,description,isdefault';\r\n\r\n// ─── Client ──────────────────────────────────────────────────────────────────\r\n\r\nexport class MetadataClient {\r\n private readonly http: DataverseHttpClient;\r\n\r\n constructor(httpClient: DataverseHttpClient) {\r\n this.http = httpClient;\r\n }\r\n\r\n // ─── Entity Metadata ───────────────────────────────────────────────────\r\n\r\n /**\r\n * Get metadata for a single entity by LogicalName, including all attributes.\r\n *\r\n * @throws {MetadataError} if the entity is not found\r\n */\r\n async getEntityWithAttributes(logicalName: string): Promise<EntityMetadata> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.info(`Fetching entity metadata: ${safeName}`);\r\n\r\n const entity = await this.http.get<EntityMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')?$select=${ENTITY_SELECT}&$expand=Attributes($select=${ATTRIBUTE_SELECT})`,\r\n );\r\n\r\n log.info(`Entity \"${safeName}\": ${entity.Attributes?.length ?? 0} attributes`);\r\n return entity;\r\n }\r\n\r\n /**\r\n * List all entities (without attributes) for discovery.\r\n * Use `$filter` parameter to narrow results.\r\n */\r\n async listEntities(filter?: string): Promise<EntityMetadata[]> {\r\n let path = `/EntityDefinitions?$select=${ENTITY_SELECT}`;\r\n if (filter) {\r\n path += `&$filter=${filter}`;\r\n }\r\n\r\n log.info('Listing entities');\r\n return this.http.getAll<EntityMetadata>(path);\r\n }\r\n\r\n // ─── Typed Attribute Queries ───────────────────────────────────────────\r\n\r\n /**\r\n * Get all Picklist attributes with their OptionSets for an entity.\r\n * Includes both local and global OptionSets.\r\n */\r\n async getPicklistAttributes(logicalName: string): Promise<PicklistAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching Picklist attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<PicklistAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata?$select=LogicalName,SchemaName,MetadataId&$expand=OptionSet($select=Options,Name,IsGlobal,MetadataId),GlobalOptionSet($select=Options,Name,MetadataId)`,\r\n );\r\n }\r\n\r\n /**\r\n * Get all Lookup attributes with their target entity names.\r\n */\r\n async getLookupAttributes(logicalName: string): Promise<LookupAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching Lookup attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<LookupAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.LookupAttributeMetadata?$select=LogicalName,SchemaName,Targets,MetadataId`,\r\n );\r\n }\r\n\r\n /**\r\n * Get Status attributes (statuscode) with their OptionSets.\r\n */\r\n async getStatusAttributes(logicalName: string): Promise<StatusAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching Status attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<StatusAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.StatusAttributeMetadata?$select=LogicalName,SchemaName,MetadataId&$expand=OptionSet($select=Options)`,\r\n );\r\n }\r\n\r\n /**\r\n * Get State attributes (statecode) with their OptionSets.\r\n */\r\n async getStateAttributes(logicalName: string): Promise<StateAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching State attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<StateAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.StateAttributeMetadata?$select=LogicalName,SchemaName,MetadataId&$expand=OptionSet($select=Options)`,\r\n );\r\n }\r\n\r\n // ─── Form Metadata ────────────────────────────────────────────────────\r\n\r\n /**\r\n * Get and parse Main forms (type=2) for an entity.\r\n * Returns parsed form structures with tabs, sections, and controls.\r\n */\r\n async getMainForms(logicalName: string): Promise<ParsedForm[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.info(`Fetching Main forms for: ${safeName}`);\r\n\r\n const forms = await this.http.getAll<SystemFormMetadata>(\r\n `/systemforms?$filter=objecttypecode eq '${safeName}' and type eq ${FORM_TYPE_MAIN}&$select=${FORM_SELECT}`,\r\n );\r\n\r\n log.info(`Found ${forms.length} Main form(s) for \"${safeName}\"`);\r\n\r\n return forms.map((form) => {\r\n try {\r\n return parseForm(form);\r\n } catch (error: unknown) {\r\n log.warn(`Failed to parse form \"${form.name}\" (${form.formid}), skipping`, {\r\n formName: form.name,\r\n formId: form.formid,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n // Return a minimal parsed form with no controls rather than failing entirely\r\n return {\r\n name: form.name,\r\n formId: form.formid,\r\n isDefault: form.isdefault,\r\n tabs: [],\r\n allControls: [],\r\n allSpecialControls: [],\r\n };\r\n }\r\n });\r\n }\r\n\r\n // ─── Global OptionSets ─────────────────────────────────────────────────\r\n\r\n /**\r\n * Get a global OptionSet by its exact name.\r\n */\r\n async getGlobalOptionSet(name: string): Promise<OptionSetMetadata> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(name);\r\n\r\n log.debug(`Fetching GlobalOptionSet: ${safeName}`);\r\n\r\n return this.http.get<OptionSetMetadata>(\r\n `/GlobalOptionSetDefinitions(Name='${safeName}')`,\r\n );\r\n }\r\n\r\n /**\r\n * List all global OptionSets (names and types only).\r\n */\r\n async listGlobalOptionSets(): Promise<OptionSetMetadata[]> {\r\n log.debug('Listing all GlobalOptionSets');\r\n\r\n return this.http.getAll<OptionSetMetadata>(\r\n `/GlobalOptionSetDefinitions?$select=Name,DisplayName,OptionSetType,IsGlobal,MetadataId`,\r\n );\r\n }\r\n\r\n // ─── Relationships ─────────────────────────────────────────────────────\r\n\r\n /**\r\n * Get all 1:N relationships where this entity is the referenced (parent) entity.\r\n */\r\n async getOneToManyRelationships(logicalName: string): Promise<OneToManyRelationshipMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching 1:N relationships for: ${safeName}`);\r\n\r\n return this.http.getAll<OneToManyRelationshipMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/OneToManyRelationships?$select=SchemaName,ReferencingEntity,ReferencingAttribute,ReferencedEntity,ReferencedAttribute,MetadataId`,\r\n );\r\n }\r\n\r\n /**\r\n * Get all N:N relationships for an entity.\r\n */\r\n async getManyToManyRelationships(logicalName: string): Promise<ManyToManyRelationshipMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching N:N relationships for: ${safeName}`);\r\n\r\n return this.http.getAll<ManyToManyRelationshipMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/ManyToManyRelationships?$select=SchemaName,Entity1LogicalName,Entity2LogicalName,IntersectEntityName,MetadataId`,\r\n );\r\n }\r\n\r\n // ─── Solution Filter ───────────────────────────────────────────────────\r\n\r\n /**\r\n * Get all entity LogicalNames that belong to a specific solution.\r\n * Resolves SolutionComponent MetadataIds to EntityDefinition LogicalNames.\r\n *\r\n * @param solutionUniqueName - The unique name of the solution\r\n * @returns Array of entity LogicalNames (e.g. [\"account\", \"contact\"])\r\n */\r\n async getEntityNamesForSolution(solutionUniqueName: string): Promise<string[]> {\r\n const safeName = DataverseHttpClient.escapeODataString(solutionUniqueName);\r\n\r\n log.info(`Fetching solution: ${solutionUniqueName}`);\r\n\r\n // Step 1: Get solution ID\r\n const solutions = await this.http.get<ODataCollection<SolutionRecord>>(\r\n `/solutions?$filter=uniquename eq '${safeName}'&$select=solutionid,uniquename,friendlyname`,\r\n );\r\n\r\n if (solutions.value.length === 0) {\r\n throw new MetadataError(\r\n ErrorCode.META_SOLUTION_NOT_FOUND,\r\n `Solution \"${solutionUniqueName}\" not found`,\r\n { solutionUniqueName },\r\n );\r\n }\r\n\r\n const solutionId = solutions.value[0]!.solutionid;\r\n const solutionName = solutions.value[0]!.friendlyname;\r\n\r\n log.info(`Solution \"${solutionName}\" (${solutionId})`);\r\n\r\n // Step 2: Get entity components (componenttype=1)\r\n const components = await this.http.getAll<SolutionComponent>(\r\n `/solutioncomponents?$filter=_solutionid_value eq ${DataverseHttpClient.sanitizeGuid(solutionId)} and componenttype eq ${COMPONENT_TYPE_ENTITY}&$select=objectid,componenttype`,\r\n );\r\n\r\n log.info(`Solution \"${solutionName}\" contains ${components.length} entity components`);\r\n\r\n if (components.length === 0) return [];\r\n\r\n // Step 3: Resolve MetadataIds to LogicalNames via EntityDefinitions\r\n // The objectid in solutioncomponents is the MetadataId of the EntityDefinition,\r\n // NOT the LogicalName. We need an additional query to resolve.\r\n const metadataIds = components.map((c) => c.objectid);\r\n const filterClauses = metadataIds.map((id) => `MetadataId eq ${DataverseHttpClient.sanitizeGuid(id)}`);\r\n\r\n // Batch in groups of 15 to avoid excessively long filter strings\r\n const BATCH_SIZE = 15;\r\n const logicalNames: string[] = [];\r\n\r\n for (let i = 0; i < filterClauses.length; i += BATCH_SIZE) {\r\n const batch = filterClauses.slice(i, i + BATCH_SIZE);\r\n const filter = batch.join(' or ');\r\n const entities = await this.http.getAll<{ LogicalName: string }>(\r\n `/EntityDefinitions?$filter=${filter}&$select=LogicalName`,\r\n );\r\n for (const e of entities) {\r\n logicalNames.push(e.LogicalName);\r\n }\r\n }\r\n\r\n log.info(`Resolved ${logicalNames.length} entity logical names from solution \"${solutionName}\"`);\r\n\r\n return logicalNames;\r\n }\r\n\r\n /**\r\n * Get all entity LogicalNames from multiple solutions, merged and deduplicated.\r\n *\r\n * @param solutionUniqueNames - Array of solution unique names\r\n * @returns Deduplicated array of entity LogicalNames\r\n */\r\n async getEntityNamesForSolutions(solutionUniqueNames: string[]): Promise<string[]> {\r\n const allNames = new Set<string>();\r\n\r\n for (const name of solutionUniqueNames) {\r\n const names = await this.getEntityNamesForSolution(name);\r\n for (const n of names) {\r\n allNames.add(n);\r\n }\r\n }\r\n\r\n const result = [...allNames].sort();\r\n log.info(`${result.length} unique entities from ${solutionUniqueNames.length} solutions`);\r\n return result;\r\n }\r\n\r\n // ─── Aggregated Metadata ───────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch complete metadata for a single entity: all attributes (typed),\r\n * forms, and relationships. This is the primary method for type generation.\r\n *\r\n * Makes 7 parallel API calls per entity for optimal performance.\r\n */\r\n async getEntityTypeInfo(logicalName: string): Promise<EntityTypeInfo> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.info(`Fetching complete type info for: ${safeName}`);\r\n\r\n const [entity, picklistAttributes, lookupAttributes, statusAttributes, stateAttributes, forms, relationships] =\r\n await Promise.all([\r\n this.getEntityWithAttributes(safeName),\r\n this.getPicklistAttributes(safeName),\r\n this.getLookupAttributes(safeName),\r\n this.getStatusAttributes(safeName),\r\n this.getStateAttributes(safeName),\r\n this.getMainForms(safeName),\r\n this.getRelationships(safeName),\r\n ]);\r\n\r\n const result: EntityTypeInfo = {\r\n entity,\r\n attributes: entity.Attributes ?? [],\r\n picklistAttributes,\r\n lookupAttributes,\r\n statusAttributes,\r\n stateAttributes,\r\n forms,\r\n oneToManyRelationships: relationships.oneToMany,\r\n manyToManyRelationships: relationships.manyToMany,\r\n };\r\n\r\n log.info(\r\n `Type info for \"${safeName}\": ${result.attributes.length} attrs, ` +\r\n `${picklistAttributes.length} picklists, ${lookupAttributes.length} lookups, ` +\r\n `${stateAttributes.length} state, ${forms.length} forms, ` +\r\n `${relationships.oneToMany.length} 1:N, ${relationships.manyToMany.length} N:N`,\r\n );\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Fetch complete metadata for multiple entities in parallel.\r\n * Respects the HTTP client's concurrency limit automatically.\r\n */\r\n async getMultipleEntityTypeInfos(logicalNames: string[]): Promise<EntityTypeInfo[]> {\r\n log.info(`Fetching type info for ${logicalNames.length} entities`);\r\n return Promise.all(logicalNames.map((name) => this.getEntityTypeInfo(name)));\r\n }\r\n\r\n // ─── Custom API Metadata ───────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch all Custom APIs with their request parameters and response properties.\r\n *\r\n * Queries the customapi, customapirequestparameter, and customapiresponseproperty\r\n * tables and joins them into CustomApiTypeInfo objects.\r\n *\r\n * @param solutionFilter - Optional: filter by solution unique name\r\n * @returns Array of complete Custom API definitions\r\n */\r\n async getCustomApis(solutionFilter?: string): Promise<CustomApiTypeInfo[]> {\r\n log.info('Fetching Custom APIs...');\r\n\r\n // 1. Load all Custom APIs\r\n let apiUrl = '/customapis?$select=uniquename,bindingtype,isfunction,boundentitylogicalname,displayname,description';\r\n if (solutionFilter) {\r\n const safeSolution = DataverseHttpClient.sanitizeIdentifier(solutionFilter);\r\n apiUrl += `&$filter=solutionid/uniquename eq '${safeSolution}'`;\r\n }\r\n\r\n const apis = await this.http.get<ODataCollection<CustomApiMetadata & { customapiid: string }>>(apiUrl);\r\n log.info(`Found ${apis.value.length} Custom APIs`);\r\n\r\n if (apis.value.length === 0) return [];\r\n\r\n // 2. Load all request parameters\r\n const params = await this.http.get<ODataCollection<CustomApiRequestParameter & { _customapiid_value: string }>>(\r\n '/customapirequestparameters?$select=uniquename,type,isoptional,logicalentityname,description,_customapiid_value',\r\n );\r\n\r\n // 3. Load all response properties\r\n const props = await this.http.get<ODataCollection<CustomApiResponseProperty & { _customapiid_value: string }>>(\r\n '/customapiresponseproperties?$select=uniquename,type,logicalentityname,description,_customapiid_value',\r\n );\r\n\r\n // 4. Group parameters and properties by Custom API ID\r\n const paramsByApi = new Map<string, CustomApiRequestParameter[]>();\r\n for (const p of params.value) {\r\n const apiId = p._customapiid_value;\r\n if (!paramsByApi.has(apiId)) paramsByApi.set(apiId, []);\r\n paramsByApi.get(apiId)!.push({\r\n uniquename: p.uniquename,\r\n type: p.type,\r\n isoptional: p.isoptional,\r\n logicalentityname: p.logicalentityname,\r\n description: p.description,\r\n });\r\n }\r\n\r\n const propsByApi = new Map<string, CustomApiResponseProperty[]>();\r\n for (const p of props.value) {\r\n const apiId = p._customapiid_value;\r\n if (!propsByApi.has(apiId)) propsByApi.set(apiId, []);\r\n propsByApi.get(apiId)!.push({\r\n uniquename: p.uniquename,\r\n type: p.type,\r\n logicalentityname: p.logicalentityname,\r\n description: p.description,\r\n });\r\n }\r\n\r\n // 5. Build CustomApiTypeInfo objects\r\n const result: CustomApiTypeInfo[] = [];\r\n for (const api of apis.value) {\r\n result.push({\r\n api: {\r\n uniquename: api.uniquename,\r\n bindingtype: api.bindingtype,\r\n isfunction: api.isfunction,\r\n boundentitylogicalname: api.boundentitylogicalname,\r\n displayname: api.displayname,\r\n description: api.description,\r\n },\r\n requestParameters: paramsByApi.get(api.customapiid) ?? [],\r\n responseProperties: propsByApi.get(api.customapiid) ?? [],\r\n });\r\n }\r\n\r\n log.info(`Loaded ${result.length} Custom APIs with parameters and response properties`);\r\n return result;\r\n }\r\n\r\n // ─── Internal Helpers ──────────────────────────────────────────────────\r\n\r\n private async getRelationships(logicalName: string): Promise<{\r\n oneToMany: OneToManyRelationshipMetadata[];\r\n manyToMany: ManyToManyRelationshipMetadata[];\r\n }> {\r\n const [oneToMany, manyToMany] = await Promise.all([\r\n this.getOneToManyRelationships(logicalName),\r\n this.getManyToManyRelationships(logicalName),\r\n ]);\r\n return { oneToMany, manyToMany };\r\n }\r\n}\r\n","/**\r\n * @xrmforge/typegen - Metadata Cache\r\n *\r\n * File-system based metadata cache using Dataverse's RetrieveMetadataChanges\r\n * ServerVersionStamp for efficient delta detection.\r\n *\r\n * On first run: full metadata retrieval, saved to .xrmforge/cache/metadata.json\r\n * On subsequent runs: delta query with stored VersionStamp, only changed entities refreshed\r\n * On expired stamp (90-day window or system maintenance): automatic full reload\r\n *\r\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/cache-schema-data\r\n */\r\n\r\nimport { promises as fs } from 'node:fs';\r\nimport * as path from 'node:path';\r\nimport { createLogger } from '../logger.js';\r\nimport type { EntityTypeInfo } from './types.js';\r\n\r\nconst log = createLogger('cache');\r\n\r\n// ─── Cache Structure ─────────────────────────────────────────────────────────\r\n\r\ninterface CacheManifest {\r\n /** XrmForge version that created this cache */\r\n version: string;\r\n /** Dataverse environment URL */\r\n environmentUrl: string;\r\n /** ServerVersionStamp from last RetrieveMetadataChanges call */\r\n serverVersionStamp: string | null;\r\n /** ISO timestamp of last full or delta refresh */\r\n lastRefreshed: string;\r\n /** Entity logical names in this cache */\r\n entities: string[];\r\n}\r\n\r\ninterface CacheData {\r\n manifest: CacheManifest;\r\n entityTypeInfos: Record<string, EntityTypeInfo>;\r\n}\r\n\r\n// ─── Constants ───────────────────────────────────────────────────────────────\r\n\r\nconst CACHE_DIR = '.xrmforge/cache';\r\nconst CACHE_FILE = 'metadata.json';\r\nconst CACHE_VERSION = '1';\r\n\r\n// ─── Cache Manager ───────────────────────────────────────────────────────────\r\n\r\nexport class MetadataCache {\r\n private readonly cacheDir: string;\r\n private readonly cacheFilePath: string;\r\n\r\n /**\r\n * @param cacheDir - Directory where cache files are stored.\r\n * Can be an absolute path or relative to cwd.\r\n * Defaults to \".xrmforge/cache\" when constructed without argument.\r\n */\r\n constructor(cacheDir: string = CACHE_DIR) {\r\n this.cacheDir = path.resolve(cacheDir);\r\n this.cacheFilePath = path.join(this.cacheDir, CACHE_FILE);\r\n }\r\n\r\n /**\r\n * Load cached metadata from disk.\r\n * Returns null if no cache exists, cache is for a different environment,\r\n * or cache format is incompatible.\r\n */\r\n async load(environmentUrl: string): Promise<CacheData | null> {\r\n try {\r\n const raw = await fs.readFile(this.cacheFilePath, 'utf-8');\r\n const data = JSON.parse(raw) as CacheData;\r\n\r\n // Validate cache compatibility\r\n if (data.manifest.version !== CACHE_VERSION) {\r\n log.info('Cache version mismatch, will do full refresh');\r\n return null;\r\n }\r\n\r\n if (data.manifest.environmentUrl !== environmentUrl) {\r\n log.info('Cache is for a different environment, will do full refresh', {\r\n cached: data.manifest.environmentUrl,\r\n current: environmentUrl,\r\n });\r\n return null;\r\n }\r\n\r\n log.info(`Loaded metadata cache: ${data.manifest.entities.length} entities, ` +\r\n `last refreshed ${data.manifest.lastRefreshed}`);\r\n\r\n return data;\r\n } catch (error: unknown) {\r\n if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n log.info('No metadata cache found, will do full refresh');\r\n } else {\r\n log.warn('Failed to read metadata cache, will do full refresh', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Save metadata to the file-system cache.\r\n */\r\n async save(\r\n environmentUrl: string,\r\n entityTypeInfos: Record<string, EntityTypeInfo>,\r\n serverVersionStamp: string | null,\r\n ): Promise<void> {\r\n const data: CacheData = {\r\n manifest: {\r\n version: CACHE_VERSION,\r\n environmentUrl,\r\n serverVersionStamp,\r\n lastRefreshed: new Date().toISOString(),\r\n entities: Object.keys(entityTypeInfos).sort(),\r\n },\r\n entityTypeInfos,\r\n };\r\n\r\n await fs.mkdir(this.cacheDir, { recursive: true });\r\n\r\n // Atomic write: write to temp file, then rename (prevents corrupt cache on crash)\r\n const tmpPath = this.cacheFilePath + '.tmp';\r\n await fs.writeFile(tmpPath, JSON.stringify(data, null, 2), 'utf-8');\r\n await fs.rename(tmpPath, this.cacheFilePath);\r\n\r\n log.info(`Saved metadata cache: ${data.manifest.entities.length} entities`);\r\n }\r\n\r\n /**\r\n * Get the stored ServerVersionStamp for delta queries.\r\n * Returns null if no cache exists.\r\n */\r\n async getVersionStamp(environmentUrl: string): Promise<string | null> {\r\n const cache = await this.load(environmentUrl);\r\n return cache?.manifest.serverVersionStamp ?? null;\r\n }\r\n\r\n /**\r\n * Update specific entities in the cache (delta update).\r\n * Merges new/changed entities into the existing cache.\r\n */\r\n async updateEntities(\r\n environmentUrl: string,\r\n updatedEntities: Record<string, EntityTypeInfo>,\r\n newVersionStamp: string | null,\r\n ): Promise<void> {\r\n const existing = await this.load(environmentUrl);\r\n const merged = existing?.entityTypeInfos ?? {};\r\n\r\n for (const [name, info] of Object.entries(updatedEntities)) {\r\n merged[name] = info;\r\n }\r\n\r\n await this.save(environmentUrl, merged, newVersionStamp);\r\n\r\n log.info(`Delta cache update: ${Object.keys(updatedEntities).length} entities updated`);\r\n }\r\n\r\n /**\r\n * Remove specific entities from the cache (for deleted entities).\r\n */\r\n async removeEntities(\r\n environmentUrl: string,\r\n deletedEntityNames: string[],\r\n newVersionStamp: string | null,\r\n ): Promise<void> {\r\n const existing = await this.load(environmentUrl);\r\n if (!existing) return;\r\n\r\n const merged = existing.entityTypeInfos;\r\n for (const name of deletedEntityNames) {\r\n delete merged[name];\r\n }\r\n\r\n await this.save(environmentUrl, merged, newVersionStamp);\r\n\r\n log.info(`Removed ${deletedEntityNames.length} entities from cache`);\r\n }\r\n\r\n /**\r\n * Delete the entire cache.\r\n */\r\n async clear(): Promise<void> {\r\n try {\r\n await fs.unlink(this.cacheFilePath);\r\n log.info('Metadata cache cleared');\r\n } catch (error: unknown) {\r\n if (!(error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT')) {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Check if a cache file exists.\r\n */\r\n async exists(): Promise<boolean> {\r\n try {\r\n await fs.access(this.cacheFilePath);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n}\r\n","/**\n * @xrmforge/typegen - Metadata Change Detector\n *\n * Uses the Dataverse RetrieveMetadataChanges action to determine\n * which entities have changed since the last generation run.\n * This enables incremental type generation (only re-fetch changed entities).\n *\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/samples/retrievemetadatachanges\n */\n\nimport type { DataverseHttpClient } from '../http/client.js';\nimport { MetadataError, ErrorCode } from '../errors.js';\nimport { createLogger } from '../logger.js';\n\nconst log = createLogger('change-detector');\n\n/** Result of a change detection query */\nexport interface ChangeDetectionResult {\n /** Entity logical names that have changed (new or modified) */\n changedEntityNames: string[];\n /** Entity logical names that have been deleted */\n deletedEntityNames: string[];\n /** New server version stamp (store this for the next run) */\n newVersionStamp: string;\n}\n\n/** Dataverse error code for expired version stamps (90-day limit) */\nconst EXPIRED_VERSION_STAMP_ERROR = '0x80044352';\n\n/**\n * Detects metadata changes in Dataverse using RetrieveMetadataChanges.\n *\n * Usage:\n * ```typescript\n * const detector = new ChangeDetector(httpClient);\n * const result = await detector.detectChanges(cachedVersionStamp);\n * // result.changedEntityNames = ['account', 'contact']\n * // result.newVersionStamp = 'new-stamp-to-cache'\n * ```\n */\nexport class ChangeDetector {\n constructor(private readonly http: DataverseHttpClient) {}\n\n /**\n * Detect which entities have changed since the given version stamp.\n *\n * @param clientVersionStamp - The ServerVersionStamp from the last run (from cache)\n * @returns Changed entity names, deleted entity names, and new version stamp\n * @throws {MetadataError} with META_VERSION_STAMP_EXPIRED if stamp is too old (>90 days)\n */\n async detectChanges(clientVersionStamp: string): Promise<ChangeDetectionResult> {\n log.info('Detecting metadata changes since last run');\n\n const query = {\n Criteria: {\n FilterOperator: 'And',\n Conditions: [],\n },\n Properties: {\n AllProperties: false,\n PropertyNames: ['LogicalName'],\n },\n };\n\n // RetrieveMetadataChanges is a Function (GET), not an Action (POST).\n // Parameters are passed as URL parameters using parameter aliases.\n const queryJson = encodeURIComponent(JSON.stringify(query));\n const deletedFilter = `Microsoft.Dynamics.CRM.DeletedMetadataFilters'Default'`;\n\n const path = `/RetrieveMetadataChanges(Query=@q,ClientVersionStamp=@s,DeletedMetadataFilters=@d)` +\n `?@q=${queryJson}&@s='${clientVersionStamp}'&@d=${deletedFilter}`;\n\n interface RetrieveMetadataChangesResponse {\n EntityMetadata: Array<{\n LogicalName: string;\n HasChanged: boolean | null;\n MetadataId: string;\n }>;\n ServerVersionStamp: string;\n DeletedMetadata?: {\n Keys?: string[];\n [key: string]: unknown;\n };\n }\n\n let response: RetrieveMetadataChangesResponse;\n try {\n response = await this.http.get<RetrieveMetadataChangesResponse>(path);\n } catch (error: unknown) {\n // Check for expired version stamp\n if (this.isExpiredVersionStampError(error)) {\n throw new MetadataError(\n ErrorCode.META_VERSION_STAMP_EXPIRED,\n 'Cached version stamp has expired (>90 days). A full metadata refresh is required.',\n { clientVersionStamp },\n );\n }\n throw error;\n }\n\n // Extract changed entity names (HasChanged = true or null means changed)\n const changedEntityNames = (response.EntityMetadata ?? [])\n .filter((e) => e.HasChanged !== false)\n .map((e) => e.LogicalName)\n .filter(Boolean);\n\n // Extract deleted entity names from DeletedMetadata\n const deletedEntityNames: string[] = [];\n if (response.DeletedMetadata?.Keys) {\n // DeletedMetadata.Keys contains MetadataIds, not LogicalNames\n // We log them but cannot resolve to names without the cache\n log.info(`${response.DeletedMetadata.Keys.length} entity metadata IDs were deleted`);\n }\n\n const newVersionStamp = response.ServerVersionStamp;\n\n log.info(`Change detection complete: ${changedEntityNames.length} changed, ${deletedEntityNames.length} deleted`, {\n changedEntityNames: changedEntityNames.length <= 10 ? changedEntityNames : `${changedEntityNames.length} entities`,\n newVersionStamp: newVersionStamp.substring(0, 20) + '...',\n });\n\n return {\n changedEntityNames,\n deletedEntityNames,\n newVersionStamp,\n };\n }\n\n /**\n * Perform an initial metadata query to get the first ServerVersionStamp.\n * This is used on the very first run (no cache exists).\n *\n * @returns The initial server version stamp\n */\n async getInitialVersionStamp(): Promise<string> {\n log.info('Fetching initial server version stamp');\n\n const query = {\n Criteria: {\n FilterOperator: 'And',\n Conditions: [],\n },\n Properties: {\n AllProperties: false,\n PropertyNames: ['LogicalName'],\n },\n };\n\n // RetrieveMetadataChanges is a Function (GET) - no ClientVersionStamp for initial call\n const queryParam = encodeURIComponent(JSON.stringify(query));\n const path = `/RetrieveMetadataChanges(Query=@q)?@q=${queryParam}`;\n\n interface InitialResponse {\n ServerVersionStamp: string;\n }\n\n const response = await this.http.get<InitialResponse>(path);\n\n log.info('Initial version stamp acquired');\n return response.ServerVersionStamp;\n }\n\n /** Check if an error is the expired version stamp error (0x80044352) */\n private isExpiredVersionStampError(error: unknown): boolean {\n const msg = error instanceof Error ? error.message : String(error);\n // Check message and context.responseBody (HttpClient puts body text in context)\n const errorRecord = error as Record<string, Record<string, unknown>> | undefined;\n const contextBody = errorRecord?.context\n ? String(errorRecord.context['responseBody'] ?? '')\n : '';\n const combined = msg + contextBody;\n return combined.includes(EXPIRED_VERSION_STAMP_ERROR) || combined.includes('ExpiredVersionStamp');\n }\n}\n","/**\r\n * @xrmforge/typegen - Label Utilities\r\n *\r\n * Extracts and formats localized labels from Dataverse metadata.\r\n * Supports dual-language output (Goldene Regel 15):\r\n * - Primary language for identifiers and first JSDoc line\r\n * - Secondary language as optional addition in JSDoc\r\n *\r\n * Format: \"Primary Label | Sekundäres Label\"\r\n * If secondary language is not available: \"Primary Label\" only.\r\n */\r\n\r\nimport type { Label, LocalizedLabel } from './types.js';\r\n\r\n// ─── Configuration ───────────────────────────────────────────────────────────\r\n\r\nexport interface LabelConfig {\r\n /** Primary language LCID (used for identifiers and first JSDoc line). Default: 1033 (English) */\r\n primaryLanguage: number;\r\n /** Optional secondary language LCID (added as comment). Example: 1031 (German) */\r\n secondaryLanguage?: number;\r\n}\r\n\r\n/** Default: English only */\r\nexport const DEFAULT_LABEL_CONFIG: LabelConfig = {\r\n primaryLanguage: 1033,\r\n};\r\n\r\n// ─── Label Extraction ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Extract a label string for the primary language.\r\n * Falls back to UserLocalizedLabel if the specific LCID is not found.\r\n * Returns empty string if no label is available.\r\n */\r\nexport function getPrimaryLabel(label: Label | null | undefined, config: LabelConfig): string {\r\n if (!label) return '';\r\n return getLabelForLanguage(label, config.primaryLanguage);\r\n}\r\n\r\n/**\r\n * Extract a dual-language JSDoc string.\r\n * Returns \"Primary | Secondary\" if both languages available,\r\n * or just \"Primary\" if secondary is missing or not configured.\r\n */\r\nexport function getJSDocLabel(label: Label | null | undefined, config: LabelConfig): string {\r\n if (!label) return '';\r\n\r\n const primary = getLabelForLanguage(label, config.primaryLanguage);\r\n if (!primary) return '';\r\n\r\n if (!config.secondaryLanguage) return primary;\r\n\r\n const secondary = getLabelForLanguage(label, config.secondaryLanguage);\r\n if (!secondary || secondary === primary) return primary;\r\n\r\n return `${primary} | ${secondary}`;\r\n}\r\n\r\n/**\r\n * Extract a label for a specific language code.\r\n * Searches LocalizedLabels first, falls back to UserLocalizedLabel.\r\n */\r\nfunction getLabelForLanguage(label: Label, languageCode: number): string {\r\n // Search in LocalizedLabels array first (most precise)\r\n const localized = label.LocalizedLabels?.find(\r\n (l: LocalizedLabel) => l.LanguageCode === languageCode,\r\n );\r\n if (localized?.Label) return localized.Label;\r\n\r\n // Fall back to UserLocalizedLabel\r\n if (label.UserLocalizedLabel?.Label) return label.UserLocalizedLabel.Label;\r\n\r\n return '';\r\n}\r\n\r\n// ─── Transliteration ─────────────────────────────────────────────────────────\r\n\r\n/** German umlaut transliteration map (R6-03) */\r\nconst TRANSLITERATION_MAP: Record<string, string> = {\r\n 'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 'ß': 'ss',\r\n 'Ä': 'Ae', 'Ö': 'Oe', 'Ü': 'Ue',\r\n};\r\n\r\n/**\r\n * Transliterate German umlauts to ASCII equivalents for TypeScript identifiers.\r\n * Other non-ASCII characters are removed in the subsequent cleaning step.\r\n */\r\nexport function transliterateUmlauts(text: string): string {\r\n return text.replace(/[äöüßÄÖÜ]/g, (char) => TRANSLITERATION_MAP[char] ?? char);\r\n}\r\n\r\n// ─── Identifier Generation ───────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert a label string to a valid TypeScript identifier (PascalCase).\r\n * Transliterates German umlauts (ä to ae, ö to oe, ü to ue, ß to ss),\r\n * then removes remaining invalid characters.\r\n *\r\n * @returns A valid TypeScript identifier, or null if the label cannot be converted\r\n */\r\nexport function labelToIdentifier(label: string): string | null {\r\n if (!label || label.trim().length === 0) return null;\r\n\r\n // Transliterate umlauts first (ä -> ae, ö -> oe, etc.)\r\n const transliterated = transliterateUmlauts(label);\r\n\r\n // Remove characters that are not ASCII letters, digits, spaces, or underscores\r\n let cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n cleaned = cleaned.trim();\r\n\r\n if (cleaned.length === 0) return null;\r\n\r\n // Convert to PascalCase: split by spaces/underscores, capitalize first letter of each word\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n const pascal = parts\r\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\r\n .join('');\r\n\r\n if (pascal.length === 0) return null;\r\n\r\n // Ensure it starts with a letter (not a digit)\r\n if (/^\\d/.test(pascal)) {\r\n return `_${pascal}`;\r\n }\r\n\r\n return pascal;\r\n}\r\n\r\n/**\r\n * Generate unique enum member names from OptionSet labels.\r\n * Handles duplicates by appending _{Value} to colliding names.\r\n * Falls back to Value_{numericValue} for unconvertible labels.\r\n *\r\n * @param options - Array of { Value, Label } from OptionSetMetadata\r\n * @param config - Label configuration for language selection\r\n * @returns Array of { name, value, jsDocLabel } for enum generation\r\n */\r\nexport function generateEnumMembers(\r\n options: Array<{ Value: number; Label: Label }>,\r\n config: LabelConfig,\r\n): Array<{ name: string; value: number; jsDocLabel: string }> {\r\n // Step 1: Generate initial names from labels\r\n const members = options.map((option) => {\r\n const primaryLabel = getPrimaryLabel(option.Label, config);\r\n const identifier = labelToIdentifier(primaryLabel);\r\n const jsDocLabel = getJSDocLabel(option.Label, config);\r\n\r\n return {\r\n rawName: identifier ?? `Value_${option.Value}`,\r\n value: option.Value,\r\n jsDocLabel: jsDocLabel || `Value ${option.Value}`,\r\n fromLabel: identifier !== null,\r\n };\r\n });\r\n\r\n // Step 2: Detect duplicates and disambiguate\r\n const nameCount = new Map<string, number>();\r\n for (const m of members) {\r\n nameCount.set(m.rawName, (nameCount.get(m.rawName) ?? 0) + 1);\r\n }\r\n\r\n const nameUsed = new Map<string, boolean>();\r\n const result: Array<{ name: string; value: number; jsDocLabel: string }> = [];\r\n\r\n for (const m of members) {\r\n const count = nameCount.get(m.rawName) ?? 1;\r\n\r\n if (count === 1) {\r\n // Unique name, use as-is\r\n result.push({ name: m.rawName, value: m.value, jsDocLabel: m.jsDocLabel });\r\n } else {\r\n // Duplicate: first occurrence keeps the name, subsequent get _{Value}\r\n if (!nameUsed.get(m.rawName)) {\r\n nameUsed.set(m.rawName, true);\r\n result.push({ name: m.rawName, value: m.value, jsDocLabel: m.jsDocLabel });\r\n } else {\r\n result.push({ name: `${m.rawName}_${m.value}`, value: m.value, jsDocLabel: m.jsDocLabel });\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ─── Query Parameter ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Build the LabelLanguages query parameter for Dataverse Metadata API.\r\n * Returns the parameter string to append to metadata queries.\r\n *\r\n * @example\r\n * getLabelLanguagesParam({ primaryLanguage: 1033, secondaryLanguage: 1031 })\r\n * // Returns \"&LabelLanguages=1033,1031\"\r\n */\r\nexport function getLabelLanguagesParam(config: LabelConfig): string {\r\n const languages = [config.primaryLanguage];\r\n if (config.secondaryLanguage && config.secondaryLanguage !== config.primaryLanguage) {\r\n languages.push(config.secondaryLanguage);\r\n }\r\n return `&LabelLanguages=${languages.join(',')}`;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Type Mapping\r\n *\r\n * Maps Dataverse AttributeType values to TypeScript types for:\r\n * 1. Entity interfaces (Web API data types)\r\n * 2. Form interfaces (Xrm.Attributes.* types from @types/xrm)\r\n * 3. Control interfaces (Xrm.Controls.* types from @types/xrm)\r\n *\r\n * This is the bridge between Dataverse metadata and generated TypeScript.\r\n * Goldene Regel 1: All types extend @types/xrm, never replace.\r\n */\r\n\r\nimport type { AttributeMetadata } from '../metadata/types.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('type-mapping');\r\n\r\n// ─── Entity Data Types (Web API responses) ───────────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to TypeScript type for entity interfaces.\r\n * These represent the raw data types returned by the Web API.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @param isLookup - Whether this is a lookup field (uses _fieldname_value pattern)\r\n * @returns TypeScript type string (e.g. \"string\", \"number\", \"boolean\")\r\n */\r\nexport function getEntityPropertyType(attributeType: string, isLookup: boolean = false): string {\r\n if (isLookup) return 'string'; // GUIDs are strings in Web API\r\n\r\n const mapping = ENTITY_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n log.warn(`Unmapped AttributeType \"${attributeType}\" falling back to \"unknown\"`);\r\n return 'unknown'; // Unmapped types get 'unknown' (safer than 'any')\r\n}\r\n\r\n/** Dataverse AttributeType to TypeScript data type */\r\nconst ENTITY_TYPE_MAP: Record<string, string> = {\r\n // String types\r\n String: 'string',\r\n Memo: 'string',\r\n EntityName: 'string',\r\n\r\n // Numeric types\r\n Integer: 'number',\r\n BigInt: 'number',\r\n Decimal: 'number',\r\n Double: 'number',\r\n Money: 'number',\r\n\r\n // Boolean\r\n Boolean: 'boolean',\r\n\r\n // OptionSet types (numeric values in Web API)\r\n Picklist: 'number',\r\n State: 'number',\r\n Status: 'number',\r\n // MultiSelectPicklist: Web API returns comma-separated string (e.g. \"595300000,595300001\")\r\n // Verified live on markant-dev.crm4.dynamics.com 2026-03-29\r\n MultiSelectPicklist: 'string',\r\n\r\n // Date/Time (ISO 8601 strings in Web API)\r\n DateTime: 'string',\r\n\r\n // Identifiers\r\n Uniqueidentifier: 'string',\r\n\r\n // Lookup (handled separately via _value pattern, but base type is string)\r\n Lookup: 'string',\r\n Customer: 'string',\r\n Owner: 'string',\r\n PartyList: 'string',\r\n\r\n // Binary/Image (not typically in entity interfaces, filtered by shouldIncludeInEntityInterface)\r\n Virtual: 'unknown',\r\n CalendarRules: 'unknown',\r\n};\r\n\r\n// ─── Form Attribute Types (@types/xrm) ──────────────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to Xrm.Attributes.* type for form interfaces.\r\n * These represent the getAttribute() return types on FormContext.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @returns Fully qualified Xrm attribute type string\r\n */\r\nexport function getFormAttributeType(attributeType: string): string {\r\n const mapping = FORM_ATTRIBUTE_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n log.warn(`Unmapped form AttributeType \"${attributeType}\" falling back to generic Attribute`);\r\n return 'Xrm.Attributes.Attribute'; // Generic fallback\r\n}\r\n\r\n/** Dataverse AttributeType to Xrm.Attributes.* type */\r\nconst FORM_ATTRIBUTE_TYPE_MAP: Record<string, string> = {\r\n // String types\r\n String: 'Xrm.Attributes.StringAttribute',\r\n Memo: 'Xrm.Attributes.StringAttribute',\r\n\r\n // Numeric types\r\n Integer: 'Xrm.Attributes.NumberAttribute',\r\n BigInt: 'Xrm.Attributes.NumberAttribute',\r\n Decimal: 'Xrm.Attributes.NumberAttribute',\r\n Double: 'Xrm.Attributes.NumberAttribute',\r\n Money: 'Xrm.Attributes.NumberAttribute',\r\n\r\n // Boolean\r\n Boolean: 'Xrm.Attributes.BooleanAttribute',\r\n\r\n // OptionSet types\r\n Picklist: 'Xrm.Attributes.OptionSetAttribute',\r\n State: 'Xrm.Attributes.OptionSetAttribute',\r\n Status: 'Xrm.Attributes.OptionSetAttribute',\r\n MultiSelectPicklist: 'Xrm.Attributes.MultiSelectOptionSetAttribute',\r\n\r\n // Date/Time\r\n DateTime: 'Xrm.Attributes.DateAttribute',\r\n\r\n // Lookup types\r\n Lookup: 'Xrm.Attributes.LookupAttribute',\r\n Customer: 'Xrm.Attributes.LookupAttribute',\r\n Owner: 'Xrm.Attributes.LookupAttribute',\r\n PartyList: 'Xrm.Attributes.LookupAttribute',\r\n\r\n // Entity Name (string attribute in forms)\r\n EntityName: 'Xrm.Attributes.StringAttribute',\r\n};\r\n\r\n// ─── Form Control Types (@types/xrm) ────────────────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to Xrm.Controls.* type for form interfaces.\r\n * These represent the getControl() return types on FormContext.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @returns Fully qualified Xrm control type string\r\n */\r\nexport function getFormControlType(attributeType: string): string {\r\n const mapping = FORM_CONTROL_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n return 'Xrm.Controls.StandardControl'; // Generic fallback\r\n}\r\n\r\n/** Dataverse AttributeType to Xrm.Controls.* type */\r\nconst FORM_CONTROL_TYPE_MAP: Record<string, string> = {\r\n // Standard controls\r\n String: 'Xrm.Controls.StringControl',\r\n Memo: 'Xrm.Controls.StringControl',\r\n Integer: 'Xrm.Controls.NumberControl',\r\n BigInt: 'Xrm.Controls.NumberControl',\r\n Decimal: 'Xrm.Controls.NumberControl',\r\n Double: 'Xrm.Controls.NumberControl',\r\n Money: 'Xrm.Controls.NumberControl',\r\n Boolean: 'Xrm.Controls.StandardControl',\r\n DateTime: 'Xrm.Controls.DateControl',\r\n EntityName: 'Xrm.Controls.StandardControl',\r\n\r\n // OptionSet controls\r\n Picklist: 'Xrm.Controls.OptionSetControl',\r\n State: 'Xrm.Controls.OptionSetControl',\r\n Status: 'Xrm.Controls.OptionSetControl',\r\n MultiSelectPicklist: 'Xrm.Controls.MultiSelectOptionSetControl',\r\n\r\n // Lookup controls\r\n Lookup: 'Xrm.Controls.LookupControl',\r\n Customer: 'Xrm.Controls.LookupControl',\r\n Owner: 'Xrm.Controls.LookupControl',\r\n PartyList: 'Xrm.Controls.LookupControl',\r\n};\r\n\r\n// ─── Mock Value Types (for @xrmforge/testing) ──────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to JavaScript value type for mock objects.\r\n * Used by the form generator to create MockValues types for @xrmforge/testing.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @returns TypeScript value type string (e.g. \"string | null\", \"number | null\")\r\n */\r\nexport function getFormMockValueType(attributeType: string): string {\r\n const mapping = FORM_MOCK_VALUE_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n return 'unknown';\r\n}\r\n\r\n/** Dataverse AttributeType to JavaScript value type */\r\nconst FORM_MOCK_VALUE_TYPE_MAP: Record<string, string> = {\r\n // String types\r\n String: 'string | null',\r\n Memo: 'string | null',\r\n EntityName: 'string | null',\r\n\r\n // Numeric types\r\n Integer: 'number | null',\r\n BigInt: 'number | null',\r\n Decimal: 'number | null',\r\n Double: 'number | null',\r\n Money: 'number | null',\r\n\r\n // Boolean\r\n Boolean: 'boolean | null',\r\n\r\n // OptionSet types (numeric values at runtime)\r\n Picklist: 'number | null',\r\n State: 'number | null',\r\n Status: 'number | null',\r\n MultiSelectPicklist: 'number[] | null',\r\n\r\n // Date/Time\r\n DateTime: 'Date | null',\r\n\r\n // Lookup types\r\n Lookup: 'Xrm.LookupValue[] | null',\r\n Customer: 'Xrm.LookupValue[] | null',\r\n Owner: 'Xrm.LookupValue[] | null',\r\n PartyList: 'Xrm.LookupValue[] | null',\r\n};\r\n\r\n// ─── Identifier Utilities ────────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert a Dataverse LogicalName to a safe TypeScript identifier.\r\n * Validates that the result is a valid identifier.\r\n *\r\n * @param logicalName - Dataverse field or entity logical name\r\n * @returns A valid TypeScript identifier\r\n * @throws Never throws; returns the input if already valid, prefixes with _ if starts with digit\r\n */\r\nexport function toSafeIdentifier(logicalName: string): string {\r\n // Most Dataverse logical names are already valid identifiers\r\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(logicalName)) {\r\n return logicalName;\r\n }\r\n\r\n // Remove invalid characters\r\n let safe = logicalName.replace(/[^a-zA-Z0-9_$]/g, '_');\r\n\r\n // Ensure it doesn't start with a digit\r\n if (/^\\d/.test(safe)) {\r\n safe = `_${safe}`;\r\n }\r\n\r\n // Ensure it's not empty\r\n if (safe.length === 0) {\r\n return '_unnamed';\r\n }\r\n\r\n return safe;\r\n}\r\n\r\n/**\r\n * Convert a Dataverse LogicalName to PascalCase for use as interface/type name.\r\n *\r\n * @example\r\n * toPascalCase('account') // 'Account'\r\n * toPascalCase('markant_cdhcontactsource') // 'MarkantCdhcontactsource'\r\n */\r\nexport function toPascalCase(logicalName: string): string {\r\n return logicalName\r\n .split('_')\r\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\r\n .join('');\r\n}\r\n\r\n// ─── Lookup Field Name Utilities ─────────────────────────────────────────────\r\n\r\n/**\r\n * Convert a lookup attribute LogicalName to its Web API value property name.\r\n * In the Web API, lookup fields are represented as `_fieldname_value`.\r\n *\r\n * @example\r\n * toLookupValueProperty('primarycontactid') // '_primarycontactid_value'\r\n * toLookupValueProperty('ownerid') // '_ownerid_value'\r\n */\r\nexport function toLookupValueProperty(logicalName: string): string {\r\n return `_${logicalName}_value`;\r\n}\r\n\r\n/**\r\n * Determine if an attribute is a single-value lookup type.\r\n * PartyList is NOT included: it's a collection navigation property (ActivityParty[]),\r\n * not a single _fieldname_value property in the Web API.\r\n */\r\nexport function isLookupType(attributeType: string): boolean {\r\n return attributeType === 'Lookup' || attributeType === 'Customer' || attributeType === 'Owner';\r\n}\r\n\r\n/**\r\n * Determine if an attribute is a PartyList (ActivityParty collection).\r\n * PartyList fields (to, from, cc, bcc, requiredattendees, optionalattendees)\r\n * are navigation properties in the Web API, not flat lookup properties.\r\n */\r\nexport function isPartyListType(attributeType: string): boolean {\r\n return attributeType === 'PartyList';\r\n}\r\n\r\n/**\r\n * Determine if an attribute should be included in entity interfaces.\r\n * Excludes virtual/calculated fields that are not readable via Web API.\r\n *\r\n * Filtered types:\r\n * - Virtual, CalendarRules: not readable via Web API\r\n * - ManagedProperty: solution metadata (iscustomizable etc.), not business data\r\n * - EntityName: internal companion fields for lookups; entity type info is only\r\n * available via @Microsoft.Dynamics.CRM.lookuplogicalname OData annotation,\r\n * not as a standalone property in Web API responses\r\n */\r\nexport function shouldIncludeInEntityInterface(attr: AttributeMetadata): boolean {\r\n // Exclude virtual attributes (images, file, calculated), but keep MultiSelectPicklist\r\n // (MultiSelectPicklist has AttributeType \"Virtual\" but @odata.type distinguishes it)\r\n if (attr.AttributeType === 'Virtual' || attr.AttributeType === 'CalendarRules') {\r\n const odataType = (attr as unknown as Record<string, unknown>)['@odata.type'] as string | undefined;\r\n if (odataType !== '#Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata') {\r\n return false;\r\n }\r\n }\r\n\r\n // Exclude solution metadata (not business data)\r\n if (attr.AttributeType === 'ManagedProperty') {\r\n return false;\r\n }\r\n\r\n // Exclude EntityName companion fields (e.g. owneridtype, regardingobjectidtype)\r\n // Entity type info comes from OData annotations, not from these fields\r\n if (attr.AttributeType === 'EntityName') {\r\n return false;\r\n }\r\n\r\n // Exclude attributes that cannot be read\r\n if (attr.IsValidForRead === false) {\r\n return false;\r\n }\r\n\r\n return true;\r\n}\r\n","/**\r\n * @xrmforge/typegen - ActivityParty Base Interface\r\n *\r\n * ActivityParty is a Dataverse system entity that represents participants\r\n * in activities (email, appointment, phonecall, fax, etc.).\r\n *\r\n * PartyList fields (to, from, cc, bcc, requiredattendees, optionalattendees)\r\n * are collection-valued navigation properties that return ActivityParty arrays.\r\n *\r\n * This interface is generated once and referenced by all Activity entities\r\n * that have PartyList fields.\r\n *\r\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/activityparty\r\n */\r\n\r\n/**\r\n * Generate the ActivityParty base interface declaration.\r\n * This is a fixed structure (system entity, never customized).\r\n *\r\n * @returns TypeScript declaration string\r\n */\r\nexport function generateActivityPartyInterface(): string {\r\n const lines: string[] = [];\r\n\r\n lines.push('/**');\r\n lines.push(' * Activity Party - Teilnehmer einer Aktivität (E-Mail, Termin, etc.)');\r\n lines.push(' * Wird von PartyList-Feldern (to, from, cc, bcc, requiredattendees) referenziert.');\r\n lines.push(' * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/activityparty');\r\n lines.push(' */');\r\n lines.push('export interface ActivityParty {');\r\n lines.push(' /** Primary key */');\r\n lines.push(' activitypartyid?: string;');\r\n lines.push(' /** Referenz auf die zugehörige Aktivität */');\r\n lines.push(' _activityid_value?: string;');\r\n lines.push(' /** Referenz auf den Teilnehmer (account | contact | systemuser | queue | knowledgearticle) */');\r\n lines.push(' _partyid_value?: string;');\r\n lines.push(' /**');\r\n lines.push(' * Rolle des Teilnehmers:');\r\n lines.push(' * 1=Sender, 2=To, 3=CC, 4=BCC, 5=Required Attendee,');\r\n lines.push(' * 6=Optional Attendee, 7=Organizer, 8=Regarding, 9=Owner,');\r\n lines.push(' * 10=Resource, 11=Customer, 12=Chat Participant, 13=Related');\r\n lines.push(' */');\r\n lines.push(' participationtypemask?: number;');\r\n lines.push(' /** E-Mail-Adresse für die Zustellung */');\r\n lines.push(' addressused?: string;');\r\n lines.push(' /** Aufwand des Teilnehmers (bei Serviceterminen) */');\r\n lines.push(' effort?: number;');\r\n lines.push(' /** Name des Teilnehmers (wenn nicht aufgelöst) */');\r\n lines.push(' unresolvedpartyname?: string;');\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\r\n * @xrmforge/typegen - Generator-specific Label Utilities\r\n *\r\n * This module contains ONLY generator-specific label functions.\r\n * Core label extraction (getPrimaryLabel, getJSDocLabel, LabelConfig etc.)\r\n * is provided by the canonical implementation in metadata/labels.ts (R6-02).\r\n *\r\n * Re-exports from metadata/labels.ts are provided for convenience.\r\n */\r\n\r\n// Re-export canonical label utilities from metadata/labels.ts\r\nexport {\r\n getPrimaryLabel,\r\n getJSDocLabel as formatDualLabel,\r\n labelToIdentifier,\r\n transliterateUmlauts,\r\n DEFAULT_LABEL_CONFIG,\r\n} from '../metadata/labels.js';\r\nexport type { LabelConfig } from '../metadata/labels.js';\r\n\r\n// Re-export getSecondaryLabel functionality via getJSDocLabel\r\n// (getSecondaryLabel was a generator-only concept, not needed separately)\r\n\r\nimport type { Label } from '../metadata/types.js';\r\nimport type { LabelConfig } from '../metadata/labels.js';\r\n\r\n/**\r\n * Extract the secondary language label, if configured and available.\r\n * Returns undefined if no secondary language is configured or label not found.\r\n */\r\nexport function getSecondaryLabel(label: Label, config: LabelConfig): string | undefined {\r\n if (!config.secondaryLanguage) return undefined;\r\n\r\n const secondary = label.LocalizedLabels.find((l) => l.LanguageCode === config.secondaryLanguage);\r\n return (secondary && secondary.Label) || undefined;\r\n}\r\n\r\n// ─── Generator-specific: Enum Member Names ──────────────────────────────────\r\n\r\nimport { transliterateUmlauts } from '../metadata/labels.js';\r\n\r\n/**\r\n * Convert a label to a valid PascalCase TypeScript identifier for enum members.\r\n * Transliterates German umlauts (R6-03), then removes remaining invalid characters.\r\n *\r\n * @example\r\n * labelToEnumMember(\"Preferred Customer\") // \"PreferredCustomer\"\r\n * labelToEnumMember(\"Bevorzügter Kunde\") // \"BevorzuegterKunde\"\r\n * labelToEnumMember(\"100% Complete\") // \"_100Complete\"\r\n * labelToEnumMember(\"\") // \"\" (caller handles empty)\r\n */\r\nexport function labelToEnumMember(labelText: string): string {\r\n if (!labelText) return '';\r\n\r\n // Transliterate umlauts first (ä -> ae, ö -> oe, etc.)\r\n const transliterated = transliterateUmlauts(labelText);\r\n\r\n // Remove characters that are not ASCII letters, digits, spaces or underscores\r\n const cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n\r\n // Split on whitespace/underscore and PascalCase each word\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n if (parts.length === 0) return '';\r\n\r\n const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');\r\n\r\n // Ensure it doesn't start with a digit\r\n if (/^\\d/.test(pascal)) {\r\n return `_${pascal}`;\r\n }\r\n\r\n return pascal;\r\n}\r\n\r\n// ─── Generator-specific: Disambiguate Duplicate Enum Members ─────────────────\r\n\r\n/**\r\n * Disambiguate duplicate enum member names by appending the numeric value.\r\n * Only the second and subsequent duplicates get the suffix.\r\n * Re-checks that the suffixed name doesn't collide with an existing name.\r\n *\r\n * @example\r\n * disambiguateEnumMembers([\r\n * { name: \"Active\", value: 1 },\r\n * { name: \"Active\", value: 2 },\r\n * ])\r\n * // [{ name: \"Active\", value: 1 }, { name: \"Active_2\", value: 2 }]\r\n *\r\n * // Edge case: \"Active_2\" already exists as a label-derived name\r\n * disambiguateEnumMembers([\r\n * { name: \"Active\", value: 1 },\r\n * { name: \"Active\", value: 2 },\r\n * { name: \"Active_2\", value: 3 },\r\n * ])\r\n * // [{ name: \"Active\", value: 1 }, { name: \"Active_2_v2\", value: 2 }, { name: \"Active_2\", value: 3 }]\r\n */\r\nexport function disambiguateEnumMembers(\r\n members: Array<{ name: string; value: number }>,\r\n): Array<{ name: string; value: number }> {\r\n // First pass: collect all original names to detect collisions\r\n const allOriginalNames = new Set(members.map((m) => m.name));\r\n const usedNames = new Set<string>();\r\n const result: Array<{ name: string; value: number }> = [];\r\n\r\n for (const { name, value } of members) {\r\n let finalName = name;\r\n\r\n if (usedNames.has(finalName)) {\r\n // Try _value suffix first\r\n finalName = `${name}_${value}`;\r\n\r\n // If that also collides (with an original name or already used), keep suffixing\r\n let attempt = 2;\r\n while (usedNames.has(finalName) || (allOriginalNames.has(finalName) && finalName !== name)) {\r\n finalName = `${name}_${value}_v${attempt}`;\r\n attempt++;\r\n }\r\n }\r\n\r\n usedNames.add(finalName);\r\n result.push({ name: finalName, value });\r\n }\r\n\r\n return result;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Entity Interface Generator\r\n *\r\n * Generates TypeScript declaration files (.d.ts) for Dataverse entity interfaces.\r\n * These interfaces represent the data types returned by the Web API.\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.Entities {\r\n * interface Account {\r\n * accountid: string | null;\r\n * name: string | null;\r\n * // ...\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { EntityTypeInfo } from '../metadata/types.js';\r\nimport {\r\n getEntityPropertyType,\r\n isLookupType,\r\n isPartyListType,\r\n toLookupValueProperty,\r\n shouldIncludeInEntityInterface,\r\n toPascalCase,\r\n} from './type-mapping.js';\r\nimport { formatDualLabel, type LabelConfig, DEFAULT_LABEL_CONFIG } from './label-utils.js';\r\n\r\n/** Options for entity interface generation */\r\nexport interface EntityGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n}\r\n\r\n/**\r\n * Generate a TypeScript declaration for an entity interface.\r\n *\r\n * @param info - Complete entity metadata (from MetadataClient.getEntityTypeInfo)\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string (.d.ts content)\r\n */\r\nexport function generateEntityInterface(info: EntityTypeInfo, options: EntityGeneratorOptions = {}): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityName = toPascalCase(info.entity.LogicalName);\r\n const entityLabel = formatDualLabel(info.entity.DisplayName, labelConfig);\r\n\r\n const lines: string[] = [];\r\n\r\n // Check if ActivityParty import is needed (entity has PartyList fields)\r\n const partyListAttrs = info.attributes.filter((a) => isPartyListType(a.AttributeType));\r\n if (partyListAttrs.length > 0) {\r\n lines.push(\"import type { ActivityParty } from './_activity-party.js';\");\r\n lines.push('');\r\n }\r\n\r\n // Entity JSDoc\r\n if (entityLabel) {\r\n lines.push(`/** ${entityLabel} */`);\r\n }\r\n lines.push(`export interface ${entityName} {`);\r\n\r\n // Filter and sort attributes\r\n const includedAttrs = info.attributes\r\n .filter(shouldIncludeInEntityInterface)\r\n .sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));\r\n\r\n // Build lookup map for target info in JSDoc\r\n const lookupTargets = new Map<string, string[]>();\r\n for (const la of info.lookupAttributes) {\r\n if (la.Targets && la.Targets.length > 0) {\r\n lookupTargets.set(la.LogicalName, la.Targets);\r\n }\r\n }\r\n\r\n for (const attr of includedAttrs) {\r\n const isLookup = isLookupType(attr.AttributeType);\r\n const propertyName = isLookup ? toLookupValueProperty(attr.LogicalName) : attr.LogicalName;\r\n const tsType = getEntityPropertyType(attr.AttributeType, isLookup);\r\n\r\n // Build JSDoc\r\n const label = formatDualLabel(attr.DisplayName, labelConfig);\r\n const jsdocParts: string[] = [];\r\n if (label) jsdocParts.push(label);\r\n\r\n // Add lookup target info\r\n if (isLookup) {\r\n const targets = lookupTargets.get(attr.LogicalName);\r\n if (targets && targets.length > 0) {\r\n jsdocParts.push(`Lookup (${targets.join(' | ')})`);\r\n }\r\n }\r\n\r\n // Add read-only marker\r\n if (!attr.IsValidForCreate && !attr.IsValidForUpdate && !attr.IsPrimaryId) {\r\n jsdocParts.push('read-only');\r\n }\r\n\r\n if (jsdocParts.length > 0) {\r\n lines.push(` /** ${jsdocParts.join(' - ')} */`);\r\n }\r\n\r\n lines.push(` ${propertyName}: ${tsType} | null;`);\r\n }\r\n\r\n // PartyList: single navigation property for the entity's ActivityParty collection.\r\n // Multiple PartyList fields (to, from, cc, bcc, requiredattendees) share ONE\r\n // navigation property per entity (e.g. email_activity_parties).\r\n if (partyListAttrs.length > 0) {\r\n // Find the activity party relationship (there's typically one per activity entity)\r\n const relationship = info.oneToManyRelationships.find(\r\n (r) => r.ReferencingEntity === 'activityparty',\r\n );\r\n const navPropName = relationship\r\n ? relationship.SchemaName.charAt(0).toLowerCase() + relationship.SchemaName.slice(1)\r\n : `${info.entity.LogicalName}_activity_parties`;\r\n\r\n lines.push('');\r\n lines.push(` /** ActivityParty collection (${partyListAttrs.length} PartyList-Felder: ${partyListAttrs.map((a) => a.LogicalName).join(', ')}) */`);\r\n lines.push(` ${navPropName}: ActivityParty[] | null;`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\r\n * @xrmforge/typegen - OptionSet Enum Generator\r\n *\r\n * Generates TypeScript const enums from Dataverse OptionSet metadata.\r\n * Uses const enum because D365 form scripts have no module system at runtime,\r\n * so enum values must be inlined at compile time.\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.OptionSets {\r\n * const enum AccountCategoryCode {\r\n * PreferredCustomer = 1,\r\n * Standard = 2,\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { OptionSetMetadata } from '../metadata/types.js';\r\nimport {\r\n formatDualLabel,\r\n getPrimaryLabel,\r\n labelToEnumMember,\r\n disambiguateEnumMembers,\r\n type LabelConfig,\r\n DEFAULT_LABEL_CONFIG,\r\n} from './label-utils.js';\r\nimport { toPascalCase } from './type-mapping.js';\r\n\r\n/** Options for OptionSet enum generation */\r\nexport interface OptionSetGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n}\r\n\r\n/**\r\n * Generate a TypeScript const enum declaration from an OptionSet.\r\n *\r\n * @param optionSet - OptionSet metadata from Dataverse\r\n * @param entityLogicalName - Entity this OptionSet belongs to (for naming local OptionSets)\r\n * @param attributeSchemaName - Attribute schema name (for naming local OptionSets)\r\n * @param options - Generator options\r\n * @returns TypeScript const enum declaration string\r\n */\r\nexport function generateOptionSetEnum(\r\n optionSet: OptionSetMetadata,\r\n _entityLogicalName: string,\r\n attributeSchemaName: string,\r\n options: OptionSetGeneratorOptions = {},\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n\r\n // Determine enum name\r\n const enumName = optionSet.IsGlobal\r\n ? toPascalCase(optionSet.Name)\r\n : toPascalCase(attributeSchemaName);\r\n\r\n // Build members from options\r\n const rawMembers = optionSet.Options.map((opt) => {\r\n const label = getPrimaryLabel(opt.Label, labelConfig);\r\n const memberName = labelToEnumMember(label);\r\n return {\r\n name: memberName || `Value_${opt.Value}`, // Fallback for empty/invalid labels\r\n value: opt.Value,\r\n option: opt,\r\n };\r\n });\r\n\r\n // Disambiguate duplicate member names\r\n const disambiguated = disambiguateEnumMembers(\r\n rawMembers.map((m) => ({ name: m.name, value: m.value })),\r\n );\r\n\r\n const lines: string[] = [];\r\n\r\n // Enum JSDoc\r\n const enumLabel = formatDualLabel(optionSet.DisplayName, labelConfig);\r\n if (enumLabel) {\r\n lines.push(`/** ${enumLabel} (${optionSet.Name}) */`);\r\n }\r\n\r\n lines.push(`export const enum ${enumName} {`);\r\n\r\n // Members\r\n for (let i = 0; i < disambiguated.length; i++) {\r\n const member = disambiguated[i]!;\r\n const rawMember = rawMembers[i];\r\n if (!rawMember) continue;\r\n\r\n // JSDoc with original label (always show, even if member name was derived from it)\r\n const memberLabel = formatDualLabel(rawMember.option.Label, labelConfig);\r\n if (memberLabel) {\r\n lines.push(` /** ${memberLabel} */`);\r\n }\r\n\r\n lines.push(` ${member.name} = ${member.value},`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate multiple OptionSet enums for all picklist attributes of an entity.\r\n * Handles both local and global OptionSets.\r\n *\r\n * @param picklistAttributes - Picklist attributes with their OptionSet metadata\r\n * @param entityLogicalName - Entity logical name\r\n * @param options - Generator options\r\n * @returns Array of { enumName, content } for each generated enum\r\n */\r\nexport function generateEntityOptionSets(\r\n picklistAttributes: Array<{ SchemaName: string; OptionSet: OptionSetMetadata | null; GlobalOptionSet: OptionSetMetadata | null }>,\r\n entityLogicalName: string,\r\n options: OptionSetGeneratorOptions = {},\r\n): Array<{ enumName: string; content: string }> {\r\n const results: Array<{ enumName: string; content: string }> = [];\r\n const generatedGlobals = new Set<string>();\r\n\r\n for (const attr of picklistAttributes) {\r\n const optionSet = attr.OptionSet || attr.GlobalOptionSet;\r\n if (!optionSet || !optionSet.Options || optionSet.Options.length === 0) continue;\r\n\r\n // Skip global OptionSets that were already generated\r\n if (optionSet.IsGlobal && generatedGlobals.has(optionSet.Name)) continue;\r\n if (optionSet.IsGlobal) generatedGlobals.add(optionSet.Name);\r\n\r\n const enumName = optionSet.IsGlobal\r\n ? toPascalCase(optionSet.Name)\r\n : toPascalCase(attr.SchemaName);\r\n\r\n const content = generateOptionSetEnum(optionSet, entityLogicalName, attr.SchemaName, options);\r\n results.push({ enumName, content });\r\n }\r\n\r\n return results;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Form Interface Generator\r\n *\r\n * Generates TypeScript form interfaces with compile-time field validation.\r\n *\r\n * Architecture:\r\n * 1. Union Type (LeadFormFields): restricts getAttribute to form-specific fields only\r\n * 2. Mapped Return Type (LeadAttributeMap): maps field name to correct Xrm type\r\n * 3. Generic getAttribute<K>: returns the exact type for each field\r\n * 4. Fields const enum: provides autocomplete with dual-language labels\r\n * 5. NO fallback getAttribute(name: string): unknown fields are compile errors\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.Forms.Account {\r\n * type AccountMainFormFields = \"name\" | \"telephone1\" | \"revenue\";\r\n *\r\n * type AccountMainFormAttributeMap = {\r\n * name: Xrm.Attributes.StringAttribute;\r\n * telephone1: Xrm.Attributes.StringAttribute;\r\n * revenue: Xrm.Attributes.NumberAttribute;\r\n * };\r\n *\r\n * type AccountMainFormControlMap = {\r\n * name: Xrm.Controls.StringControl;\r\n * telephone1: Xrm.Controls.StringControl;\r\n * revenue: Xrm.Controls.NumberControl;\r\n * };\r\n *\r\n * const enum AccountMainFormFields {\r\n * Name = 'name',\r\n * Telephone1 = 'telephone1',\r\n * Revenue = 'revenue',\r\n * }\r\n *\r\n * interface AccountMainForm extends Omit<Xrm.FormContext, 'getAttribute' | 'getControl'> {\r\n * getAttribute<K extends AccountMainFormFields>(name: K): AccountMainFormAttributeMap[K];\r\n * getAttribute(index: number): Xrm.Attributes.Attribute;\r\n * getAttribute(): Xrm.Attributes.Attribute[];\r\n * getControl<K extends AccountMainFormFields>(name: K): AccountMainFormControlMap[K];\r\n * getControl(index: number): Xrm.Controls.Control;\r\n * getControl(): Xrm.Controls.Control[];\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { ParsedForm, AttributeMetadata, SpecialControlType } from '../metadata/types.js';\r\nimport {\r\n getFormAttributeType,\r\n getFormControlType,\r\n getFormMockValueType,\r\n toPascalCase,\r\n} from './type-mapping.js';\r\nimport { transliterateUmlauts, formatDualLabel, getPrimaryLabel, type LabelConfig, DEFAULT_LABEL_CONFIG } from './label-utils.js';\r\n\r\n/** Map special control types to @types/xrm control interfaces */\r\nfunction specialControlToXrmType(controlType: SpecialControlType): string | null {\r\n switch (controlType) {\r\n case 'subgrid': return 'Xrm.Controls.GridControl';\r\n case 'editablegrid': return 'Xrm.Controls.GridControl';\r\n case 'quickview': return 'Xrm.Controls.QuickFormControl';\r\n case 'webresource': return 'Xrm.Controls.IframeControl';\r\n case 'iframe': return 'Xrm.Controls.IframeControl';\r\n case 'notes': return 'Xrm.Controls.Control';\r\n case 'map': return 'Xrm.Controls.Control';\r\n default: return null;\r\n }\r\n}\r\n\r\n/** Options for form interface generation */\r\nexport interface FormGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n /** Form types to include (default: [2] = Main only) */\r\n formTypes?: number[];\r\n}\r\n\r\n/** Build a safe TypeScript identifier from a form/tab/section name */\r\nfunction toSafeFormName(formName: string): string {\r\n const result = transliterateUmlauts(formName)\r\n .replace(/[^a-zA-Z0-9\\s]/g, '')\r\n .split(/\\s+/)\r\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\r\n .join('');\r\n\r\n // Prefix with _ if starts with digit (e.g. GUID-based section names)\r\n if (/^\\d/.test(result)) return `_${result}`;\r\n if (result.length === 0) return '_Unnamed';\r\n return result;\r\n}\r\n\r\n/**\r\n * Build the interface base name, avoiding redundant prefix.\r\n * \"Account\" + \"Account\" -> \"Account\" (not \"AccountAccount\")\r\n * \"Lead\" + \"Markant Lead\" -> \"LeadMarkantLead\"\r\n */\r\nfunction buildFormBaseName(entityPascal: string, safeFormName: string): string {\r\n // If form name starts with entity name, don't prefix\r\n if (safeFormName.startsWith(entityPascal)) {\r\n return safeFormName;\r\n }\r\n return `${entityPascal}${safeFormName}`;\r\n}\r\n\r\n/** Convert a label to a PascalCase enum member name */\r\nfunction labelToPascalMember(label: string): string {\r\n if (!label) return '';\r\n const transliterated = transliterateUmlauts(label);\r\n const cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n if (parts.length === 0) return '';\r\n const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join('');\r\n if (/^\\d/.test(pascal)) return `_${pascal}`;\r\n return pascal;\r\n}\r\n\r\n/**\r\n * Generate a complete form declaration: union type, mapped types, fields enum, and interface.\r\n *\r\n * @param form - Parsed form structure (from FormXml parser)\r\n * @param entityLogicalName - Entity this form belongs to\r\n * @param attributeMap - Map of LogicalName to AttributeMetadata for type resolution\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string\r\n */\r\nexport function generateFormInterface(\r\n form: ParsedForm,\r\n entityLogicalName: string,\r\n attributeMap: Map<string, AttributeMetadata>,\r\n options: FormGeneratorOptions = {},\r\n baseNameOverride?: string,\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityPascal = toPascalCase(entityLogicalName);\r\n const baseName = baseNameOverride || buildFormBaseName(entityPascal, toSafeFormName(form.name));\r\n const interfaceName = `${baseName}Form`;\r\n const fieldsTypeName = `${baseName}FormFields`;\r\n const attrMapName = `${baseName}FormAttributeMap`;\r\n const ctrlMapName = `${baseName}FormControlMap`;\r\n\r\n // Get unique field names from form controls (deduplicate across tabs/sections)\r\n const fieldNames = new Set<string>();\r\n for (const control of form.allControls) {\r\n if (control.datafieldname) {\r\n fieldNames.add(control.datafieldname);\r\n }\r\n }\r\n\r\n // Resolve field types from attribute metadata\r\n const fields: Array<{\r\n logicalName: string;\r\n attributeType: string;\r\n formAttributeType: string;\r\n formControlType: string;\r\n label: string;\r\n enumMemberName: string;\r\n }> = [];\r\n\r\n const usedEnumNames = new Set<string>();\r\n\r\n for (const fieldName of [...fieldNames].sort()) {\r\n const attr = attributeMap.get(fieldName);\r\n if (!attr) continue;\r\n\r\n // Build enum member name from label (English primary)\r\n const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);\r\n let enumMember = labelToPascalMember(primaryLabel);\r\n if (!enumMember) {\r\n enumMember = toPascalCase(fieldName);\r\n }\r\n\r\n // Disambiguate enum member names\r\n const originalEnumMember = enumMember;\r\n let counter = 2;\r\n while (usedEnumNames.has(enumMember)) {\r\n enumMember = `${originalEnumMember}${counter}`;\r\n counter++;\r\n }\r\n usedEnumNames.add(enumMember);\r\n\r\n const dualLabel = formatDualLabel(attr.DisplayName, labelConfig);\r\n\r\n fields.push({\r\n logicalName: fieldName,\r\n attributeType: attr.AttributeType,\r\n formAttributeType: getFormAttributeType(attr.AttributeType),\r\n formControlType: getFormControlType(attr.AttributeType),\r\n label: dualLabel,\r\n enumMemberName: enumMember,\r\n });\r\n }\r\n\r\n const lines: string[] = [];\r\n\r\n // 1. Union Type: restricts which field names are valid\r\n lines.push(`/** Valid field names for the \"${form.name}\" form */`);\r\n if (fields.length === 0) {\r\n lines.push(`export type ${fieldsTypeName} = never;`);\r\n } else {\r\n lines.push(`export type ${fieldsTypeName} =`);\r\n for (let i = 0; i < fields.length; i++) {\r\n const separator = i === fields.length - 1 ? ';' : '';\r\n lines.push(` | \"${fields[i]!.logicalName}\"${separator}`);\r\n }\r\n }\r\n lines.push('');\r\n\r\n // 2. Attribute Map: maps field name to Xrm.Attributes.* type\r\n lines.push(`/** Attribute type map for \"${form.name}\" */`);\r\n if (fields.length === 0) {\r\n lines.push(`export type ${attrMapName} = Record<string, never>;`);\r\n } else {\r\n lines.push(`export type ${attrMapName} = {`);\r\n for (const field of fields) {\r\n lines.push(` ${field.logicalName}: ${field.formAttributeType};`);\r\n }\r\n lines.push('};');\r\n }\r\n lines.push('');\r\n\r\n // 3. Control Map: maps field name to Xrm.Controls.* type\r\n lines.push(`/** Control type map for \"${form.name}\" */`);\r\n if (fields.length === 0) {\r\n lines.push(`export type ${ctrlMapName} = Record<string, never>;`);\r\n } else {\r\n lines.push(`export type ${ctrlMapName} = {`);\r\n for (const field of fields) {\r\n lines.push(` ${field.logicalName}: ${field.formControlType};`);\r\n }\r\n lines.push('};');\r\n }\r\n lines.push('');\r\n\r\n // 4. Fields const enum: autocomplete with dual-language labels\r\n lines.push(`/** Field constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${fieldsTypeName}Enum {`);\r\n for (const field of fields) {\r\n if (field.label) {\r\n lines.push(` /** ${field.label} */`);\r\n }\r\n lines.push(` ${field.enumMemberName} = '${field.logicalName}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n\r\n // 4b. Tabs const enum\r\n const namedTabs = form.tabs.filter((t) => t.name);\r\n if (namedTabs.length > 0) {\r\n const tabsEnumName = `${baseName}FormTabs`;\r\n\r\n // Pre-compute disambiguated tab member names (Bug 2 fix: duplicate tab names)\r\n const usedTabMembers = new Set<string>();\r\n const tabMemberNames: string[] = [];\r\n for (const tab of namedTabs) {\r\n let memberName = toSafeFormName(tab.name) || toPascalCase(tab.name);\r\n const originalName = memberName;\r\n let counter = 2;\r\n while (usedTabMembers.has(memberName)) {\r\n memberName = `${originalName}${counter}`;\r\n counter++;\r\n }\r\n usedTabMembers.add(memberName);\r\n tabMemberNames.push(memberName);\r\n }\r\n\r\n lines.push(`/** Tab constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${tabsEnumName} {`);\r\n for (let i = 0; i < namedTabs.length; i++) {\r\n const tab = namedTabs[i]!;\r\n if (tab.label) {\r\n lines.push(` /** ${tab.label} */`);\r\n }\r\n lines.push(` ${tabMemberNames[i]} = '${tab.name}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n\r\n // 4c. Section const enums (one per tab, using disambiguated tab member names)\r\n for (let i = 0; i < namedTabs.length; i++) {\r\n const tab = namedTabs[i]!;\r\n const namedSections = tab.sections.filter((s) => s.name);\r\n if (namedSections.length === 0) continue;\r\n\r\n const tabMemberName = tabMemberNames[i]!;\r\n const sectionsEnumName = `${baseName}Form${tabMemberName}Sections`;\r\n lines.push(`/** Section constants for tab \"${tab.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${sectionsEnumName} {`);\r\n\r\n // Disambiguate section member names within a tab\r\n const usedSectionMembers = new Set<string>();\r\n for (const section of namedSections) {\r\n if (section.label) {\r\n lines.push(` /** ${section.label} */`);\r\n }\r\n let sectionMember = toSafeFormName(section.name) || toPascalCase(section.name);\r\n const originalSectionMember = sectionMember;\r\n let sCounter = 2;\r\n while (usedSectionMembers.has(sectionMember)) {\r\n sectionMember = `${originalSectionMember}${sCounter}`;\r\n sCounter++;\r\n }\r\n usedSectionMembers.add(sectionMember);\r\n lines.push(` ${sectionMember} = '${section.name}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n }\r\n }\r\n\r\n // 4d. Subgrid/QuickView const enum (all special controls with stable IDs)\r\n const specialControls = form.allSpecialControls || [];\r\n const subgrids = specialControls.filter((sc) => sc.controlType === 'subgrid' || sc.controlType === 'editablegrid');\r\n const quickViews = specialControls.filter((sc) => sc.controlType === 'quickview');\r\n\r\n if (subgrids.length > 0) {\r\n const subgridsEnumName = `${baseName}FormSubgrids`;\r\n lines.push(`/** Subgrid constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${subgridsEnumName} {`);\r\n const usedMembers = new Set<string>();\r\n for (const sg of subgrids) {\r\n let member = toSafeFormName(sg.id) || toPascalCase(sg.id);\r\n const original = member;\r\n let counter = 2;\r\n while (usedMembers.has(member)) {\r\n member = `${original}${counter}`;\r\n counter++;\r\n }\r\n usedMembers.add(member);\r\n const label = sg.targetEntityType ? `Subgrid: ${sg.targetEntityType}` : `Subgrid`;\r\n lines.push(` /** ${label} */`);\r\n lines.push(` ${member} = '${sg.id}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n }\r\n\r\n if (quickViews.length > 0) {\r\n const qvEnumName = `${baseName}FormQuickViews`;\r\n lines.push(`/** Quick View constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${qvEnumName} {`);\r\n const usedMembers = new Set<string>();\r\n for (const qv of quickViews) {\r\n let member = toSafeFormName(qv.id) || toPascalCase(qv.id);\r\n const original = member;\r\n let counter = 2;\r\n while (usedMembers.has(member)) {\r\n member = `${original}${counter}`;\r\n counter++;\r\n }\r\n usedMembers.add(member);\r\n lines.push(` /** Quick View */`);\r\n lines.push(` ${member} = '${qv.id}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n }\r\n\r\n // 5. Form Interface: generic getAttribute/getControl with compile-time validation\r\n lines.push(`/** ${form.name} */`);\r\n lines.push(`export interface ${interfaceName} extends Omit<Xrm.FormContext, 'getAttribute' | 'getControl'> {`);\r\n lines.push(` /** Typisierter Feldzugriff: nur Felder die auf diesem Formular existieren */`);\r\n lines.push(` getAttribute<K extends ${fieldsTypeName}>(name: K): ${attrMapName}[K];`);\r\n lines.push(' getAttribute(index: number): Xrm.Attributes.Attribute;');\r\n lines.push(' getAttribute(): Xrm.Attributes.Attribute[];');\r\n lines.push('');\r\n lines.push(` /** Typisierter Control-Zugriff: nur Controls die auf diesem Formular existieren */`);\r\n lines.push(` getControl<K extends ${fieldsTypeName}>(name: K): ${ctrlMapName}[K];`);\r\n\r\n // Typed getControl overloads for special controls (subgrids, quick views, etc.)\r\n for (const sc of specialControls) {\r\n const xrmType = specialControlToXrmType(sc.controlType);\r\n if (xrmType) {\r\n lines.push(` getControl(name: \"${sc.id}\"): ${xrmType};`);\r\n }\r\n }\r\n\r\n lines.push(' getControl(index: number): Xrm.Controls.Control;');\r\n lines.push(' getControl(): Xrm.Controls.Control[];');\r\n\r\n // 6. Typed ui.tabs for compile-time tab name validation\r\n if (form.tabs.length > 0) {\r\n lines.push('');\r\n lines.push(' /** Typisierter Tab-Zugriff */');\r\n lines.push(' ui: {');\r\n lines.push(' tabs: {');\r\n for (const tab of form.tabs) {\r\n if (tab.name) {\r\n const sectionNames = tab.sections.filter((s) => s.name).map((s) => s.name);\r\n if (sectionNames.length > 0) {\r\n // Tab with typed sections\r\n lines.push(` get(name: \"${tab.name}\"): Xrm.Controls.Tab & {`);\r\n lines.push(' sections: {');\r\n for (const sectionName of sectionNames) {\r\n lines.push(` get(name: \"${sectionName}\"): Xrm.Controls.Section;`);\r\n }\r\n lines.push(' get(name: string): Xrm.Controls.Section;');\r\n lines.push(' };');\r\n lines.push(' };');\r\n } else {\r\n lines.push(` get(name: \"${tab.name}\"): Xrm.Controls.Tab;`);\r\n }\r\n }\r\n }\r\n lines.push(' get(name: string): Xrm.Controls.Tab;');\r\n lines.push(' };');\r\n lines.push(' } & Xrm.Ui;');\r\n }\r\n\r\n lines.push('}');\r\n\r\n // 7. MockValues type for @xrmforge/testing\r\n const mockValuesName = `${interfaceName}MockValues`;\r\n lines.push('');\r\n lines.push(`/** Mock value types for \"${form.name}\" form (used with @xrmforge/testing) */`);\r\n lines.push(`export type ${mockValuesName} = {`);\r\n for (const field of fields) {\r\n const mockType = getFormMockValueType(field.attributeType);\r\n lines.push(` ${field.logicalName}?: ${mockType};`);\r\n }\r\n lines.push('};');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate form interfaces for all forms of an entity.\r\n *\r\n * @param forms - Parsed forms (from FormXml parser)\r\n * @param entityLogicalName - Entity logical name\r\n * @param attributes - All attributes of the entity (for type resolution)\r\n * @param options - Generator options\r\n * @returns Array of { formName, interfaceName, content }\r\n */\r\nexport function generateEntityForms(\r\n forms: ParsedForm[],\r\n entityLogicalName: string,\r\n attributes: AttributeMetadata[],\r\n options: FormGeneratorOptions = {},\r\n): Array<{ formName: string; interfaceName: string; content: string }> {\r\n // Build attribute lookup map\r\n const attributeMap = new Map<string, AttributeMetadata>();\r\n for (const attr of attributes) {\r\n attributeMap.set(attr.LogicalName, attr);\r\n }\r\n\r\n const entityPascal = toPascalCase(entityLogicalName);\r\n\r\n // Pre-compute base names for all valid forms\r\n const validForms = forms.filter((f) => f.allControls.length > 0);\r\n const baseNames = validForms.map((form) => {\r\n const safeFormName = toSafeFormName(form.name);\r\n return buildFormBaseName(entityPascal, safeFormName);\r\n });\r\n\r\n // Count occurrences to detect duplicates\r\n const baseNameCounts = new Map<string, number>();\r\n for (const name of baseNames) {\r\n baseNameCounts.set(name, (baseNameCounts.get(name) || 0) + 1);\r\n }\r\n\r\n // Disambiguate duplicate base names with numeric suffix\r\n const baseNameCounters = new Map<string, number>();\r\n const results: Array<{ formName: string; interfaceName: string; content: string }> = [];\r\n\r\n for (let i = 0; i < validForms.length; i++) {\r\n const form = validForms[i]!;\r\n let baseName = baseNames[i]!;\r\n\r\n if (baseNameCounts.get(baseName)! > 1) {\r\n const counter = (baseNameCounters.get(baseName) || 0) + 1;\r\n baseNameCounters.set(baseName, counter);\r\n if (counter > 1) {\r\n baseName = `${baseName}${counter}`;\r\n }\r\n }\r\n\r\n const interfaceName = `${baseName}Form`;\r\n const content = generateFormInterface(form, entityLogicalName, attributeMap, options, baseName);\r\n results.push({ formName: form.name, interfaceName, content });\r\n }\r\n\r\n return results;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Entity Fields Enum Generator\r\n *\r\n * Generates a const enum with ALL entity fields for use with Xrm.WebApi.\r\n * Unlike form-specific Fields enums, this contains every readable attribute.\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.Entities {\r\n * const enum AccountFields {\r\n * /** Account Name | Firmenname *\\/\r\n * Name = 'name',\r\n * /** Main Phone | Haupttelefon *\\/\r\n * Telephone1 = 'telephone1',\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { EntityTypeInfo } from '../metadata/types.js';\r\nimport {\r\n toPascalCase,\r\n shouldIncludeInEntityInterface,\r\n isLookupType,\r\n toLookupValueProperty,\r\n} from './type-mapping.js';\r\nimport {\r\n formatDualLabel,\r\n getPrimaryLabel,\r\n transliterateUmlauts,\r\n type LabelConfig,\r\n DEFAULT_LABEL_CONFIG,\r\n} from './label-utils.js';\r\n\r\n/** Options for entity fields enum generation */\r\nexport interface EntityFieldsGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n}\r\n\r\n/** Convert a label to a PascalCase enum member name */\r\nfunction labelToPascalMember(label: string): string {\r\n if (!label) return '';\r\n const transliterated = transliterateUmlauts(label);\r\n const cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n if (parts.length === 0) return '';\r\n const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join('');\r\n if (/^\\d/.test(pascal)) return `_${pascal}`;\r\n return pascal;\r\n}\r\n\r\n/**\r\n * Generate a const enum with all entity fields for Web API usage.\r\n * Includes ALL readable fields (not form-specific).\r\n *\r\n * @param info - Complete entity metadata\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string\r\n */\r\nexport function generateEntityFieldsEnum(\r\n info: EntityTypeInfo,\r\n options: EntityFieldsGeneratorOptions = {},\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityName = toPascalCase(info.entity.LogicalName);\r\n const enumName = `${entityName}Fields`;\r\n\r\n // Filter and sort attributes\r\n const includedAttrs = info.attributes\r\n .filter(shouldIncludeInEntityInterface)\r\n .sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));\r\n\r\n const lines: string[] = [];\r\n lines.push(`/** All fields of ${entityName} (for Web API $select queries) */`);\r\n lines.push(`export const enum ${enumName} {`);\r\n\r\n const usedNames = new Set<string>();\r\n\r\n for (const attr of includedAttrs) {\r\n const isLookup = isLookupType(attr.AttributeType);\r\n const propertyName = isLookup ? toLookupValueProperty(attr.LogicalName) : attr.LogicalName;\r\n\r\n // Build enum member name from label\r\n const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);\r\n let memberName = labelToPascalMember(primaryLabel);\r\n if (!memberName) {\r\n memberName = toPascalCase(attr.LogicalName);\r\n }\r\n\r\n // Disambiguate\r\n const originalName = memberName;\r\n let counter = 2;\r\n while (usedNames.has(memberName)) {\r\n memberName = `${originalName}${counter}`;\r\n counter++;\r\n }\r\n usedNames.add(memberName);\r\n\r\n // Dual-language JSDoc\r\n const dualLabel = formatDualLabel(attr.DisplayName, labelConfig);\r\n if (dualLabel) {\r\n lines.push(` /** ${dualLabel} */`);\r\n }\r\n lines.push(` ${memberName} = '${propertyName}',`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate a const enum with navigation property names for all lookup fields.\r\n * Used with parseLookup() and $expand queries.\r\n *\r\n * Unlike EntityFields (which uses _fieldname_value format), this enum\r\n * contains the plain LogicalName (= navigation property name for non-polymorphic lookups).\r\n *\r\n * @param info - Complete entity metadata\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string\r\n *\r\n * @example\r\n * ```typescript\r\n * // Generated:\r\n * const enum AccountNavigationProperties {\r\n * Country = 'markant_address1_countryid',\r\n * PrimaryContact = 'primarycontactid',\r\n * }\r\n *\r\n * // Usage:\r\n * parseLookup(response, AccountNav.Country);\r\n * ```\r\n */\r\nexport function generateEntityNavigationProperties(\r\n info: EntityTypeInfo,\r\n options: EntityFieldsGeneratorOptions = {},\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityName = toPascalCase(info.entity.LogicalName);\r\n const enumName = `${entityName}NavigationProperties`;\r\n\r\n // Filter to lookup attributes only\r\n const lookupAttrs = info.attributes\r\n .filter(shouldIncludeInEntityInterface)\r\n .filter((a) => isLookupType(a.AttributeType))\r\n .sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));\r\n\r\n if (lookupAttrs.length === 0) return '';\r\n\r\n const lines: string[] = [];\r\n lines.push(`/** Navigation properties of ${entityName} (for parseLookup and $expand) */`);\r\n lines.push(`export const enum ${enumName} {`);\r\n\r\n const usedNames = new Set<string>();\r\n\r\n for (const attr of lookupAttrs) {\r\n // Build enum member name from label\r\n const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);\r\n let memberName = labelToPascalMember(primaryLabel);\r\n if (!memberName) {\r\n memberName = toPascalCase(attr.LogicalName);\r\n }\r\n\r\n // Disambiguate\r\n const originalName = memberName;\r\n let counter = 2;\r\n while (usedNames.has(memberName)) {\r\n memberName = `${originalName}${counter}`;\r\n counter++;\r\n }\r\n usedNames.add(memberName);\r\n\r\n // Dual-language JSDoc\r\n const dualLabel = formatDualLabel(attr.DisplayName, labelConfig);\r\n if (dualLabel) {\r\n lines.push(` /** ${dualLabel} */`);\r\n }\r\n // Value = LogicalName (NOT _value format)\r\n lines.push(` ${memberName} = '${attr.LogicalName}',`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\n * @xrmforge/typegen - Entity Names Enum Generator\n *\n * Generates a single const enum with all entity logical names.\n * Eliminates raw strings in Xrm.WebApi calls.\n *\n * Output pattern:\n * ```typescript\n * declare namespace XrmForge {\n * const enum EntityNames {\n * Account = 'account',\n * Contact = 'contact',\n * Lead = 'lead',\n * }\n * }\n * ```\n */\n\nimport { toPascalCase } from './type-mapping.js';\n\nexport interface EntityNamesGeneratorOptions {\n // Options reserved for future use\n}\n\n/**\n * Generate a const enum mapping entity PascalCase names to logical names.\n *\n * @param entityNames - Array of entity logical names\n * @param options - Generator options\n * @returns TypeScript declaration string\n */\nexport function generateEntityNamesEnum(\n entityNames: string[],\n _options: EntityNamesGeneratorOptions = {},\n): string {\n const sorted = [...entityNames].sort();\n\n const lines: string[] = [];\n lines.push('/** Entity logical names for Xrm.WebApi calls (compile-time only, zero runtime) */');\n lines.push('export const enum EntityNames {');\n\n for (const name of sorted) {\n const pascal = toPascalCase(name);\n lines.push(` ${pascal} = '${name}',`);\n }\n\n lines.push('}');\n lines.push('');\n\n return lines.join('\\n');\n}\n","/**\r\n * @xrmforge/typegen - Custom API Metadata Types\r\n *\r\n * TypeScript interfaces for Dataverse Custom API definitions.\r\n * These types model the JSON structures from the customapi,\r\n * customapirequestparameter, and customapiresponseproperty tables.\r\n *\r\n * Used by the action-generator to produce typed Action/Function executors.\r\n */\r\n\r\n// ─── Custom API Parameter Type ──────────────────────────────────────────────\r\n\r\n/**\r\n * Custom API parameter type (picklist values from customapirequestparameter.type).\r\n *\r\n * Maps to TypeScript types and OData metadata for getMetadata().\r\n */\r\nexport const enum CustomApiParameterType {\r\n Boolean = 0,\r\n DateTime = 1,\r\n Decimal = 2,\r\n Entity = 3,\r\n EntityCollection = 4,\r\n EntityReference = 5,\r\n Float = 6,\r\n Integer = 7,\r\n Money = 8,\r\n Picklist = 9,\r\n String = 10,\r\n StringArray = 11,\r\n Guid = 12,\r\n}\r\n\r\n/**\r\n * Custom API binding type (picklist values from customapi.bindingtype).\r\n */\r\nexport const enum CustomApiBindingType {\r\n /** Nicht an eine Entity gebunden (global aufrufbar) */\r\n Global = 0,\r\n /** An einen einzelnen Entity-Datensatz gebunden */\r\n Entity = 1,\r\n /** An eine Entity-Collection gebunden */\r\n EntityCollection = 2,\r\n}\r\n\r\n// ─── Metadata Interfaces ────────────────────────────────────────────────────\r\n\r\n/** Custom API definition (from the customapi table) */\r\nexport interface CustomApiMetadata {\r\n /** Unique message name (e.g. \"markant_NormalizePhone\") */\r\n uniquename: string;\r\n /** 0=Global, 1=Entity, 2=EntityCollection */\r\n bindingtype: CustomApiBindingType;\r\n /** true = Function (GET), false = Action (POST) */\r\n isfunction: boolean;\r\n /** Bound entity logical name (null for unbound) */\r\n boundentitylogicalname: string | null;\r\n /** Display name for documentation */\r\n displayname?: string;\r\n /** Description for JSDoc */\r\n description?: string;\r\n}\r\n\r\n/** Custom API request parameter (from the customapirequestparameter table) */\r\nexport interface CustomApiRequestParameter {\r\n /** Parameter name (e.g. \"Input\", \"TargetId\") */\r\n uniquename: string;\r\n /** Parameter type (0-12, see CustomApiParameterType) */\r\n type: CustomApiParameterType;\r\n /** Whether the parameter is optional */\r\n isoptional: boolean;\r\n /** Entity logical name (for Entity/EntityReference types) */\r\n logicalentityname?: string | null;\r\n /** Description for JSDoc */\r\n description?: string;\r\n}\r\n\r\n/** Custom API response property (from the customapiresponseproperty table) */\r\nexport interface CustomApiResponseProperty {\r\n /** Property name (e.g. \"Normalized\", \"IsValid\") */\r\n uniquename: string;\r\n /** Property type (0-12, see CustomApiParameterType) */\r\n type: CustomApiParameterType;\r\n /** Entity logical name (for Entity/EntityReference types) */\r\n logicalentityname?: string | null;\r\n /** Description for JSDoc */\r\n description?: string;\r\n}\r\n\r\n/** Complete Custom API definition with parameters and response */\r\nexport interface CustomApiTypeInfo {\r\n api: CustomApiMetadata;\r\n requestParameters: CustomApiRequestParameter[];\r\n responseProperties: CustomApiResponseProperty[];\r\n}\r\n\r\n// ─── Type Mapping ───────────────────────────────────────────────────────────\r\n\r\n/** Mapped type information for code generation */\r\nexport interface MappedParameterType {\r\n /** TypeScript type string (e.g. \"string\", \"number\", \"boolean\") */\r\n tsType: string;\r\n /** OData type name for getMetadata() (e.g. \"Edm.String\", \"mscrm.quote\") */\r\n typeName: string;\r\n /** structuralProperty value for getMetadata() */\r\n structuralProperty: number;\r\n}\r\n\r\n/**\r\n * Map a Custom API parameter type (0-12) to TypeScript + OData metadata.\r\n *\r\n * @param type - Custom API parameter type value\r\n * @param entityName - Entity logical name (for Entity/EntityReference types)\r\n * @returns TypeScript type, OData typeName, and structuralProperty\r\n */\r\nexport function mapCustomApiParameterType(\r\n type: CustomApiParameterType,\r\n entityName?: string | null,\r\n): MappedParameterType {\r\n switch (type) {\r\n case CustomApiParameterType.Boolean:\r\n return { tsType: 'boolean', typeName: 'Edm.Boolean', structuralProperty: 1 };\r\n case CustomApiParameterType.DateTime:\r\n return { tsType: 'string', typeName: 'Edm.DateTimeOffset', structuralProperty: 1 };\r\n case CustomApiParameterType.Decimal:\r\n return { tsType: 'number', typeName: 'Edm.Decimal', structuralProperty: 1 };\r\n case CustomApiParameterType.Entity:\r\n return {\r\n tsType: 'Record<string, unknown>',\r\n typeName: `mscrm.${entityName || 'crmbaseentity'}`,\r\n structuralProperty: 5,\r\n };\r\n case CustomApiParameterType.EntityCollection:\r\n return {\r\n tsType: 'Array<Record<string, unknown>>',\r\n typeName: 'Collection(mscrm.crmbaseentity)',\r\n structuralProperty: 4,\r\n };\r\n case CustomApiParameterType.EntityReference:\r\n return {\r\n tsType: '{ id: string; entityType: string; name?: string }',\r\n typeName: `mscrm.${entityName || 'crmbaseentity'}`,\r\n structuralProperty: 5,\r\n };\r\n case CustomApiParameterType.Float:\r\n return { tsType: 'number', typeName: 'Edm.Double', structuralProperty: 1 };\r\n case CustomApiParameterType.Integer:\r\n return { tsType: 'number', typeName: 'Edm.Int32', structuralProperty: 1 };\r\n case CustomApiParameterType.Money:\r\n return { tsType: 'number', typeName: 'Edm.Decimal', structuralProperty: 1 };\r\n case CustomApiParameterType.Picklist:\r\n return { tsType: 'number', typeName: 'Edm.Int32', structuralProperty: 1 };\r\n case CustomApiParameterType.String:\r\n return { tsType: 'string', typeName: 'Edm.String', structuralProperty: 1 };\r\n case CustomApiParameterType.StringArray:\r\n return { tsType: 'string[]', typeName: 'Collection(Edm.String)', structuralProperty: 4 };\r\n case CustomApiParameterType.Guid:\r\n return { tsType: 'string', typeName: 'Edm.Guid', structuralProperty: 1 };\r\n default:\r\n return { tsType: 'unknown', typeName: 'Edm.String', structuralProperty: 0 };\r\n }\r\n}\r\n","/**\r\n * @xrmforge/typegen - Action/Function Generator\r\n *\r\n * Generates TypeScript files for type-safe Custom API execution:\r\n * - .d.ts: Parameter/Response interfaces and executor types\r\n * - .ts: Runtime modules that import factory functions from @xrmforge/helpers\r\n *\r\n * Input: CustomApiTypeInfo[] (from fixture JSON or live Dataverse query)\r\n * Output: Grouped by entity (bound) or \"global\" (unbound)\r\n *\r\n * @example Generated output for markant_NormalizePhone (unbound action):\r\n * ```typescript\r\n * // global.d.ts\r\n * declare namespace XrmForge.Actions {\r\n * interface NormalizePhoneParams { Input: string; AllowSuspicious?: boolean; }\r\n * interface NormalizePhoneResult { Normalized: string; Status: number; Message: string; }\r\n * }\r\n *\r\n * // global.ts\r\n * import { createUnboundAction } from '@xrmforge/helpers';\r\n * export const NormalizePhone = createUnboundAction<...>('markant_NormalizePhone', { ... });\r\n * ```\r\n */\r\n\r\nimport type {\r\n CustomApiTypeInfo,\r\n CustomApiRequestParameter,\r\n CustomApiResponseProperty,\r\n} from '../metadata/custom-api-types.js';\r\nimport { mapCustomApiParameterType, CustomApiBindingType } from '../metadata/custom-api-types.js';\r\nimport { toPascalCase } from './type-mapping.js';\r\n\r\n// ─── Options ────────────────────────────────────────────────────────────────\r\n\r\n/** Options for action/function generation */\r\nexport interface ActionGeneratorOptions {\r\n /** Import path for @xrmforge/helpers (default: \"@xrmforge/helpers\") */\r\n importPath?: string;\r\n}\r\n\r\n// ─── Naming ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Derive a clean PascalCase name from the Custom API uniquename.\r\n * Removes common prefixes like \"markant_\", \"dev1_\" etc.\r\n *\r\n * Examples:\r\n * - \"markant_NormalizePhone\" -> \"NormalizePhone\"\r\n * - \"markant_winquote\" -> \"WinQuote\"\r\n * - \"WhoAmI\" -> \"WhoAmI\"\r\n */\r\nfunction deriveActionName(uniquename: string): string {\r\n // Entferne Publisher-Prefix (alles vor und inklusive dem ersten Unterstrich)\r\n const withoutPrefix = uniquename.includes('_')\r\n ? uniquename.substring(uniquename.indexOf('_') + 1)\r\n : uniquename;\r\n return toPascalCase(withoutPrefix);\r\n}\r\n\r\n// ─── Interface Generation ───────────────────────────────────────────────────\r\n\r\nfunction generateParamsInterface(\r\n name: string,\r\n params: CustomApiRequestParameter[],\r\n): string {\r\n if (params.length === 0) return '';\r\n\r\n const lines: string[] = [];\r\n lines.push(`export interface ${name}Params {`);\r\n\r\n for (const param of params) {\r\n const mapped = mapCustomApiParameterType(param.type, param.logicalentityname);\r\n const optional = param.isoptional ? '?' : '';\r\n if (param.description) {\r\n lines.push(` /** ${param.description} */`);\r\n }\r\n lines.push(` ${param.uniquename}${optional}: ${mapped.tsType};`);\r\n }\r\n\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n\r\nfunction generateResultInterface(\r\n name: string,\r\n props: CustomApiResponseProperty[],\r\n): string {\r\n if (props.length === 0) return '';\r\n\r\n const lines: string[] = [];\r\n lines.push(`export interface ${name}Result {`);\r\n\r\n for (const prop of props) {\r\n const mapped = mapCustomApiParameterType(prop.type, prop.logicalentityname);\r\n if (prop.description) {\r\n lines.push(` /** ${prop.description} */`);\r\n }\r\n lines.push(` ${prop.uniquename}: ${mapped.tsType};`);\r\n }\r\n\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n\r\n// ─── Declaration (.d.ts) Generation ─────────────────────────────────────────\r\n\r\n/**\r\n * Generate declarations (interfaces only) for a group of Custom APIs.\r\n * Output is flat ES module style with `export interface`.\r\n *\r\n * @param apis - Custom APIs to generate (all in the same group: same entity or all global)\r\n * @param isFunction - true for functions, false for actions\r\n * @param _entityName - Entity name for bound APIs, undefined for global (reserved for future use)\r\n * @param _options - Generator options (reserved for future use)\r\n */\r\nexport function generateActionDeclarations(\r\n apis: CustomApiTypeInfo[],\r\n _isFunction: boolean,\r\n _entityName?: string,\r\n _options: ActionGeneratorOptions = {},\r\n): string {\r\n const lines: string[] = [];\r\n lines.push('// Auto-generated by @xrmforge/typegen - DO NOT EDIT');\r\n lines.push('');\r\n\r\n for (const apiInfo of apis) {\r\n const name = deriveActionName(apiInfo.api.uniquename);\r\n const hasParams = apiInfo.requestParameters.length > 0;\r\n const hasResult = apiInfo.responseProperties.length > 0;\r\n\r\n // JSDoc\r\n const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;\r\n lines.push(`/** ${description} (${apiInfo.api.uniquename}) */`);\r\n\r\n // Params interface\r\n if (hasParams) {\r\n lines.push(generateParamsInterface(name, apiInfo.requestParameters));\r\n lines.push('');\r\n }\r\n\r\n // Result interface\r\n if (hasResult) {\r\n lines.push(generateResultInterface(name, apiInfo.responseProperties));\r\n lines.push('');\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n// ─── Runtime Module (.ts) Generation ────────────────────────────────────────\r\n\r\n/**\r\n * Generate a single .ts file with both interfaces and runtime executors for a group of Custom APIs.\r\n * Combines what was previously split into .d.ts (interfaces) and .ts (executors).\r\n *\r\n * @param apis - Custom APIs to generate\r\n * @param isFunction - true for functions, false for actions\r\n * @param options - Generator options\r\n */\r\nexport function generateActionModule(\r\n apis: CustomApiTypeInfo[],\r\n isFunction: boolean,\r\n options: ActionGeneratorOptions = {},\r\n): string {\r\n const importPath = options.importPath || '@xrmforge/helpers';\r\n const lines: string[] = [];\r\n\r\n lines.push('// Auto-generated by @xrmforge/typegen - DO NOT EDIT');\r\n lines.push('');\r\n\r\n // Collect needed factory imports\r\n const imports = new Set<string>();\r\n for (const apiInfo of apis) {\r\n if (apiInfo.api.bindingtype === CustomApiBindingType.Global) {\r\n if (isFunction) {\r\n imports.add('createUnboundFunction');\r\n } else {\r\n imports.add('createUnboundAction');\r\n }\r\n } else {\r\n if (isFunction) {\r\n imports.add('createBoundFunction');\r\n } else {\r\n imports.add('createBoundAction');\r\n }\r\n }\r\n }\r\n\r\n lines.push(`import { ${[...imports].sort().join(', ')} } from '${importPath}';`);\r\n lines.push('');\r\n\r\n for (const apiInfo of apis) {\r\n const name = deriveActionName(apiInfo.api.uniquename);\r\n const hasParams = apiInfo.requestParameters.length > 0;\r\n const hasResult = apiInfo.responseProperties.length > 0;\r\n\r\n // JSDoc\r\n const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;\r\n lines.push(`/** ${description} (${apiInfo.api.uniquename}) */`);\r\n\r\n // Params interface (inline, exported)\r\n if (hasParams) {\r\n lines.push(generateParamsInterface(name, apiInfo.requestParameters));\r\n lines.push('');\r\n }\r\n\r\n // Result interface (inline, exported)\r\n if (hasResult) {\r\n lines.push(generateResultInterface(name, apiInfo.responseProperties));\r\n lines.push('');\r\n }\r\n\r\n // Executor export with type parameters for Params and Result types\r\n const typeArgs = buildTypeArgs(name, hasParams, hasResult);\r\n\r\n if (apiInfo.api.bindingtype === CustomApiBindingType.Global) {\r\n // Unbound\r\n if (isFunction) {\r\n const funcTypeArg = hasResult ? `<${name}Result>` : '<unknown>';\r\n lines.push(`export const ${name} = createUnboundFunction${funcTypeArg}('${apiInfo.api.uniquename}');`);\r\n } else if (hasParams) {\r\n const paramMeta = generateParameterMetaMap(apiInfo.requestParameters);\r\n lines.push(`export const ${name} = createUnboundAction${typeArgs}('${apiInfo.api.uniquename}', ${paramMeta});`);\r\n } else if (hasResult) {\r\n lines.push(`export const ${name} = createUnboundAction<${name}Result>('${apiInfo.api.uniquename}');`);\r\n } else {\r\n lines.push(`export const ${name} = createUnboundAction('${apiInfo.api.uniquename}');`);\r\n }\r\n } else {\r\n // Bound\r\n const entity = apiInfo.api.boundentitylogicalname!;\r\n if (isFunction) {\r\n const funcTypeArg = hasResult ? `<${name}Result>` : '<unknown>';\r\n lines.push(`export const ${name} = createBoundFunction${funcTypeArg}('${apiInfo.api.uniquename}', '${entity}');`);\r\n } else if (hasParams) {\r\n const paramMeta = generateParameterMetaMap(apiInfo.requestParameters);\r\n lines.push(`export const ${name} = createBoundAction${typeArgs}('${apiInfo.api.uniquename}', '${entity}', ${paramMeta});`);\r\n } else if (hasResult) {\r\n lines.push(`export const ${name} = createBoundAction<${name}Result>('${apiInfo.api.uniquename}', '${entity}');`);\r\n } else {\r\n lines.push(`export const ${name} = createBoundAction('${apiInfo.api.uniquename}', '${entity}');`);\r\n }\r\n }\r\n\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n// ─── Type Argument Builder ──────────────────────────────────────────────────\r\n\r\n/**\r\n * Build the generic type argument string for factory function calls.\r\n *\r\n * For actions with params: `<NameParams>` or `<NameParams, NameResult>`\r\n * For actions without params but with result: `<NameResult>`\r\n * For actions without params or result: empty string (no generics)\r\n */\r\nfunction buildTypeArgs(name: string, hasParams: boolean, hasResult: boolean): string {\r\n if (hasParams && hasResult) {\r\n return `<${name}Params, ${name}Result>`;\r\n }\r\n if (hasParams) {\r\n return `<${name}Params>`;\r\n }\r\n if (hasResult) {\r\n return `<${name}Result>`;\r\n }\r\n return '';\r\n}\r\n\r\n// ─── Parameter Metadata Map ─────────────────────────────────────────────────\r\n\r\nfunction generateParameterMetaMap(params: CustomApiRequestParameter[]): string {\r\n const entries: string[] = [];\r\n for (const param of params) {\r\n const mapped = mapCustomApiParameterType(param.type, param.logicalentityname);\r\n entries.push(\r\n ` ${param.uniquename}: { typeName: '${mapped.typeName}', structuralProperty: ${mapped.structuralProperty} }`,\r\n );\r\n }\r\n return `{\\n${entries.join(',\\n')},\\n}`;\r\n}\r\n\r\n// ─── Grouping Helper ────────────────────────────────────────────────────────\r\n\r\n/** Group result: key is entity logical name or \"global\" for unbound */\r\nexport interface GroupedCustomApis {\r\n actions: Map<string, CustomApiTypeInfo[]>;\r\n functions: Map<string, CustomApiTypeInfo[]>;\r\n}\r\n\r\n/**\r\n * Group Custom APIs by entity (bound) or \"global\" (unbound),\r\n * and separate actions from functions.\r\n */\r\nexport function groupCustomApis(apis: CustomApiTypeInfo[]): GroupedCustomApis {\r\n const actions = new Map<string, CustomApiTypeInfo[]>();\r\n const functions = new Map<string, CustomApiTypeInfo[]>();\r\n\r\n for (const api of apis) {\r\n const target = api.api.isfunction ? functions : actions;\r\n const key = api.api.bindingtype === CustomApiBindingType.Global\r\n ? 'global'\r\n : (api.api.boundentitylogicalname || 'global');\r\n\r\n if (!target.has(key)) {\r\n target.set(key, []);\r\n }\r\n target.get(key)!.push(api);\r\n }\r\n\r\n return { actions, functions };\r\n}\r\n","/**\r\n * @xrmforge/typegen - File Writer\r\n *\r\n * Writes generated .ts files to disk with proper directory structure:\r\n *\r\n * outputDir/\r\n * entities/\r\n * account.ts\r\n * contact.ts\r\n * optionsets/\r\n * account.ts (all OptionSets for one entity)\r\n * forms/\r\n * account.ts (all form interfaces for one entity)\r\n * fields/\r\n * account.ts (FieldsEnum + NavigationProperties)\r\n * index.ts (barrel re-export file)\r\n */\r\n\r\nimport { mkdir, writeFile, readFile, unlink } from 'node:fs/promises';\r\nimport { join, dirname } from 'node:path';\r\nimport type { GeneratedFile } from './types.js';\r\n\r\n/**\r\n * Write a generated file to disk, creating directories as needed.\r\n *\r\n * @param outputDir - Base output directory\r\n * @param file - Generated file to write\r\n * @returns true if file was written (content changed), false if unchanged\r\n */\r\nexport async function writeGeneratedFile(outputDir: string, file: GeneratedFile): Promise<boolean> {\r\n const absolutePath = join(outputDir, file.relativePath);\r\n\r\n // Check if file already exists with same content (avoid unnecessary writes)\r\n try {\r\n const existing = await readFile(absolutePath, 'utf-8');\r\n if (existing === file.content) {\r\n return false; // No change\r\n }\r\n } catch {\r\n // File doesn't exist yet, proceed with write\r\n }\r\n\r\n // Ensure directory exists\r\n await mkdir(dirname(absolutePath), { recursive: true });\r\n\r\n // Write file\r\n await writeFile(absolutePath, file.content, 'utf-8');\r\n\r\n return true;\r\n}\r\n\r\n/** Result of writing a batch of files */\r\nexport interface WriteResult {\r\n /** Number of files successfully written (content changed) */\r\n written: number;\r\n /** Number of files unchanged (already up to date) */\r\n unchanged: number;\r\n /** Warnings for files that could not be written (non-fatal) */\r\n warnings: string[];\r\n}\r\n\r\n/**\r\n * Write all generated files to disk.\r\n * A single file write failure does NOT abort the entire batch.\r\n * Errors are collected as warnings so the remaining files are still written.\r\n *\r\n * @param outputDir - Base output directory\r\n * @param files - Array of generated files\r\n * @returns WriteResult with counts and warnings\r\n */\r\nexport async function writeAllFiles(outputDir: string, files: GeneratedFile[]): Promise<WriteResult> {\r\n const result: WriteResult = { written: 0, unchanged: 0, warnings: [] };\r\n\r\n for (const file of files) {\r\n try {\r\n const changed = await writeGeneratedFile(outputDir, file);\r\n if (changed) {\r\n result.written++;\r\n } else {\r\n result.unchanged++;\r\n }\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n result.warnings.push(`Konnte ${file.relativePath} nicht schreiben: ${message}`);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Delete orphaned .d.ts files for entities that were removed from Dataverse.\r\n * Removes entity, optionset, and form files for the given entity names.\r\n *\r\n * @param outputDir - Base output directory\r\n * @param deletedEntityNames - Entity logical names whose files should be removed\r\n * @returns Number of files deleted\r\n */\r\nexport async function deleteOrphanedFiles(outputDir: string, deletedEntityNames: string[]): Promise<number> {\r\n let deleted = 0;\r\n const subdirs = ['entities', 'optionsets', 'forms', 'fields'];\r\n\r\n for (const entityName of deletedEntityNames) {\r\n for (const subdir of subdirs) {\r\n const filePath = join(outputDir, subdir, `${entityName}.ts`);\r\n try {\r\n await unlink(filePath);\r\n deleted++;\r\n } catch (error: unknown) {\r\n // File might not exist (entity had no forms/optionsets), that's fine\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\r\n throw error;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return deleted;\r\n}\r\n\r\n/** File header for generated files */\r\nconst GENERATED_HEADER = `// ──────────────────────────────────────────────────────────────────────────────\r\n// This file was generated by @xrmforge/typegen. Do not edit manually.\r\n// Re-run 'xrmforge generate' to update.\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n\r\n`;\r\n\r\n/**\r\n * Wrap generated content with the standard header.\r\n */\r\nexport function addGeneratedHeader(content: string): string {\r\n return GENERATED_HEADER + content;\r\n}\r\n\r\n/**\r\n * Convert a relative .ts path to an ESM import specifier with .js extension.\r\n * TypeScript ESM convention: import specifiers use .js, resolved to .ts at compile time.\r\n *\r\n * @example toImportSpecifier('entities/account.ts') => './entities/account.js'\r\n */\r\nfunction toImportSpecifier(relativePath: string): string {\r\n const withoutExt = relativePath.replace(/\\.ts$/, '');\r\n return `./${withoutExt}.js`;\r\n}\r\n\r\n/**\r\n * Generate a barrel index.ts that re-exports all generated files.\r\n *\r\n * @param files - All generated files\r\n * @returns Content for index.ts\r\n */\r\nexport function generateBarrelIndex(files: GeneratedFile[]): string {\r\n const lines: string[] = [GENERATED_HEADER];\r\n\r\n // Group by type for organized output\r\n const entities = files.filter((f) => f.type === 'entity');\r\n const optionsets = files.filter((f) => f.type === 'optionset');\r\n const forms = files.filter((f) => f.type === 'form');\r\n const fields = files.filter((f) => f.type === 'fields');\r\n const actions = files.filter((f) => f.type === 'action');\r\n\r\n if (entities.length > 0) {\r\n lines.push('// Entity Interfaces');\r\n for (const f of entities) {\r\n lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (optionsets.length > 0) {\r\n lines.push('// OptionSet Enums - import directly from individual files to avoid name conflicts:');\r\n for (const f of optionsets) {\r\n lines.push(`// import { ... } from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (forms.length > 0) {\r\n lines.push('// Form Interfaces');\r\n for (const f of forms) {\r\n lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (fields.length > 0) {\r\n lines.push('// Entity Fields & Navigation Properties - import directly from individual files to avoid name conflicts:');\r\n for (const f of fields) {\r\n lines.push(`// import { ... } from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (actions.length > 0) {\r\n lines.push('// Custom API Actions & Functions');\r\n for (const f of actions) {\r\n lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\r\n * @xrmforge/typegen - Type Generation Orchestrator\r\n *\r\n * Coordinates the full type generation pipeline:\r\n * 1. Fetch metadata for requested entities (via MetadataClient)\r\n * 2. Generate entity interfaces, OptionSet enums, form interfaces, fields enums\r\n * 3. Write .ts files to disk\r\n *\r\n * This is the main entry point that ties all components together.\r\n */\r\n\r\nimport type { TokenCredential } from '@azure/identity';\r\nimport { MetadataClient } from '../metadata/client.js';\r\nimport { MetadataCache } from '../metadata/cache.js';\r\nimport { ChangeDetector } from '../metadata/change-detector.js';\r\nimport { DataverseHttpClient } from '../http/client.js';\r\nimport { ErrorCode } from '../errors.js';\r\nimport { createLogger, type Logger } from '../logger.js';\r\nimport type { EntityTypeInfo, OptionSetMetadata } from '../metadata/types.js';\r\nimport { generateEntityInterface } from '../generators/entity-generator.js';\r\nimport { generateEntityOptionSets } from '../generators/optionset-generator.js';\r\nimport { generateEntityForms } from '../generators/form-generator.js';\r\nimport { generateActionDeclarations, generateActionModule, groupCustomApis } from '../generators/action-generator.js';\r\nimport { generateEntityFieldsEnum, generateEntityNavigationProperties } from '../generators/entity-fields-generator.js';\r\nimport { generateEntityNamesEnum } from '../generators/entity-names-generator.js';\r\nimport { generateActivityPartyInterface } from '../generators/activity-party.js';\r\nimport { isPartyListType } from '../generators/type-mapping.js';\r\nimport { addGeneratedHeader, writeAllFiles, generateBarrelIndex, deleteOrphanedFiles } from './file-writer.js';\r\nimport type {\r\n GenerateConfig,\r\n GenerationResult,\r\n EntityGenerationResult,\r\n GeneratedFile,\r\n CacheStats,\r\n} from './types.js';\r\n\r\n/**\r\n * Main orchestrator for type generation.\r\n *\r\n * Usage:\r\n * ```typescript\r\n * const orchestrator = new TypeGenerationOrchestrator(credential, {\r\n * environmentUrl: 'https://myorg.crm4.dynamics.com',\r\n * entities: ['account', 'contact'],\r\n * outputDir: './generated',\r\n * labelConfig: { primaryLanguage: 1033, secondaryLanguage: 1031 },\r\n * });\r\n * const result = await orchestrator.generate();\r\n * ```\r\n */\r\nexport class TypeGenerationOrchestrator {\r\n private readonly config: Required<GenerateConfig>;\r\n private readonly credential: TokenCredential;\r\n private readonly logger: Logger;\r\n\r\n constructor(credential: TokenCredential, config: GenerateConfig, logger?: Logger) {\r\n this.credential = credential;\r\n this.logger = logger ?? createLogger('orchestrator');\r\n\r\n // Apply defaults\r\n this.config = {\r\n environmentUrl: config.environmentUrl,\r\n entities: [...config.entities],\r\n solutionNames: config.solutionNames ?? [],\r\n outputDir: config.outputDir,\r\n labelConfig: config.labelConfig,\r\n generateEntities: config.generateEntities ?? true,\r\n generateForms: config.generateForms ?? true,\r\n generateOptionSets: config.generateOptionSets ?? true,\r\n generateActions: config.generateActions ?? false,\r\n actionsFilter: config.actionsFilter ?? '',\r\n useCache: config.useCache ?? false,\r\n cacheDir: config.cacheDir ?? '.xrmforge/cache',\r\n namespacePrefix: config.namespacePrefix ?? 'XrmForge',\r\n };\r\n }\r\n\r\n /**\r\n * Run the full type generation pipeline.\r\n *\r\n * @param options - Optional parameters\r\n * @param options.signal - AbortSignal to cancel the generation process.\r\n * When aborted, entities that have not yet started processing are skipped.\r\n * Entities already in progress may still complete or fail with an abort error.\r\n */\r\n async generate(options?: { signal?: AbortSignal }): Promise<GenerationResult> {\r\n const signal = options?.signal;\r\n const startTime = Date.now();\r\n const allFiles: GeneratedFile[] = [];\r\n const entityResults: EntityGenerationResult[] = [];\r\n\r\n // Check abort before starting\r\n if (signal?.aborted) {\r\n return { entities: [], totalFiles: 0, totalWarnings: 0, durationMs: 0 };\r\n }\r\n\r\n this.logger.info('Starting type generation', {\r\n entities: this.config.entities,\r\n outputDir: this.config.outputDir,\r\n useCache: this.config.useCache,\r\n });\r\n\r\n // 1. Create HTTP client and metadata client\r\n const httpClient = new DataverseHttpClient({\r\n environmentUrl: this.config.environmentUrl,\r\n credential: this.credential,\r\n });\r\n\r\n const metadataClient = new MetadataClient(httpClient);\r\n\r\n // 1b. Resolve solution entities if solutionNames is set\r\n if (this.config.solutionNames && this.config.solutionNames.length > 0) {\r\n this.logger.info(`Resolving entities from ${this.config.solutionNames.length} solution(s): ${this.config.solutionNames.join(', ')}`);\r\n const solutionEntityNames = await metadataClient.getEntityNamesForSolutions(this.config.solutionNames);\r\n this.logger.info(`Found ${solutionEntityNames.length} entities in solution(s)`);\r\n // Merge solution entities with manually specified entities, deduplicate\r\n const merged = new Set([...this.config.entities, ...solutionEntityNames]);\r\n this.config.entities = [...merged].sort();\r\n }\r\n\r\n if (this.config.entities.length === 0) {\r\n this.logger.warn('No entities to process. Check --entities or --solutions.');\r\n return {\r\n entities: [],\r\n totalFiles: 0,\r\n totalWarnings: 1,\r\n durationMs: Date.now() - startTime,\r\n };\r\n }\r\n\r\n // 2. Determine cache strategy\r\n let cacheStats: CacheStats | undefined;\r\n const entitiesToFetch = new Set<string>(this.config.entities);\r\n const cachedEntityInfos: Record<string, EntityTypeInfo> = {};\r\n let cache: MetadataCache | undefined;\r\n let newVersionStamp: string | null = null;\r\n const deletedEntityNames: string[] = [];\r\n\r\n if (this.config.useCache) {\r\n const cacheResult = await this.resolveCache(httpClient, entitiesToFetch);\r\n cache = cacheResult.cache;\r\n newVersionStamp = cacheResult.newVersionStamp;\r\n cacheStats = cacheResult.stats;\r\n\r\n // Move cached entities out of the fetch set\r\n for (const [name, info] of Object.entries(cacheResult.cachedEntities)) {\r\n cachedEntityInfos[name] = info;\r\n entitiesToFetch.delete(name);\r\n }\r\n deletedEntityNames.push(...cacheResult.deletedEntityNames);\r\n }\r\n\r\n // 3. Fetch metadata for entities that need fetching (parallel, R7-07)\r\n const fetchList = [...entitiesToFetch];\r\n const failedEntities = new Map<string, string>(); // entityName -> errorMsg\r\n if (fetchList.length > 0) {\r\n this.logger.info(`Fetching ${fetchList.length} entities from Dataverse`);\r\n\r\n const settled = await Promise.allSettled(\r\n fetchList.map((entityName) => {\r\n if (signal?.aborted) {\r\n return Promise.reject(new Error('Generation aborted'));\r\n }\r\n return metadataClient.getEntityTypeInfo(entityName).then((info) => {\r\n this.logger.info(`Fetched entity: ${entityName}`);\r\n return { entityName, info };\r\n });\r\n }),\r\n );\r\n\r\n for (let i = 0; i < settled.length; i++) {\r\n const outcome = settled[i]!;\r\n const entityName = fetchList[i]!;\r\n\r\n if (outcome.status === 'fulfilled') {\r\n cachedEntityInfos[outcome.value.entityName] = outcome.value.info;\r\n } else {\r\n const errorMsg = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);\r\n this.logger.error(`Failed to fetch entity: ${entityName}`, { error: outcome.reason });\r\n failedEntities.set(entityName, errorMsg);\r\n }\r\n }\r\n }\r\n\r\n // 3b. Generate ActivityParty interface if any entity has PartyList attributes\r\n if (this.config.generateEntities) {\r\n const hasPartyList = Object.values(cachedEntityInfos).some((info) =>\r\n info.attributes.some((a) => isPartyListType(a.AttributeType)),\r\n );\r\n if (hasPartyList) {\r\n const activityPartyContent = generateActivityPartyInterface();\r\n allFiles.push({\r\n relativePath: 'entities/_activity-party.ts',\r\n content: addGeneratedHeader(activityPartyContent),\r\n type: 'entity',\r\n });\r\n }\r\n }\r\n\r\n // 3c. Generate types for all entities in order (cached + freshly fetched)\r\n this.logger.info(`Generating types for ${this.config.entities.length - failedEntities.size} entities`);\r\n\r\n for (const entityName of this.config.entities) {\r\n if (signal?.aborted) break;\r\n\r\n // Check if fetch failed for this entity\r\n if (failedEntities.has(entityName)) {\r\n entityResults.push({\r\n entityLogicalName: entityName,\r\n files: [],\r\n warnings: [`Failed to process: ${failedEntities.get(entityName)}`],\r\n });\r\n continue;\r\n }\r\n\r\n const entityInfo = cachedEntityInfos[entityName];\r\n if (!entityInfo) continue; // Should not happen, but safety guard\r\n\r\n const result = this.generateEntityFiles(entityName, entityInfo);\r\n this.logger.info(`Generated entity: ${entityName} (${result.files.length} files)`);\r\n entityResults.push(result);\r\n allFiles.push(...result.files);\r\n }\r\n\r\n // 3d. Generate Custom API Action/Function executors\r\n if (this.config.generateActions && !signal?.aborted) {\r\n const actionFiles = await this.generateActions(metadataClient);\r\n allFiles.push(...actionFiles);\r\n }\r\n\r\n // 3e. Generate EntityNames enum (all entities in one file)\r\n if (this.config.entities.length > 0) {\r\n const entityNamesContent = generateEntityNamesEnum(this.config.entities);\r\n allFiles.push({\r\n relativePath: 'entity-names.ts',\r\n content: addGeneratedHeader(entityNamesContent),\r\n type: 'entity',\r\n });\r\n }\r\n\r\n // 4. Write barrel index\r\n if (allFiles.length > 0) {\r\n const indexContent = generateBarrelIndex(allFiles);\r\n const indexFile: GeneratedFile = {\r\n relativePath: 'index.ts',\r\n content: indexContent,\r\n type: 'entity',\r\n };\r\n allFiles.push(indexFile);\r\n }\r\n\r\n // 5. Write all files to disk (non-fatal: file write errors become warnings)\r\n const writeResult = await writeAllFiles(this.config.outputDir, allFiles);\r\n\r\n // 5b. Delete orphaned .d.ts files for deleted entities\r\n if (deletedEntityNames.length > 0) {\r\n const deleted = await deleteOrphanedFiles(this.config.outputDir, deletedEntityNames);\r\n if (deleted > 0) {\r\n this.logger.info(`Deleted ${deleted} orphaned files for removed entities`);\r\n }\r\n }\r\n\r\n // 6. Update cache with new data\r\n if (this.config.useCache && cache) {\r\n await this.updateCache(cache, cachedEntityInfos, deletedEntityNames, newVersionStamp);\r\n }\r\n\r\n const durationMs = Date.now() - startTime;\r\n const entityWarnings = entityResults.reduce((sum, r) => sum + r.warnings.length, 0);\r\n const totalWarnings = entityWarnings + writeResult.warnings.length;\r\n\r\n for (const w of writeResult.warnings) {\r\n this.logger.warn(w);\r\n }\r\n\r\n this.logger.info('Type generation complete', {\r\n entities: entityResults.length,\r\n filesWritten: writeResult.written,\r\n filesUnchanged: writeResult.unchanged,\r\n totalFiles: allFiles.length,\r\n totalWarnings,\r\n durationMs,\r\n cacheUsed: cacheStats?.cacheUsed ?? false,\r\n });\r\n\r\n return {\r\n entities: entityResults,\r\n totalFiles: allFiles.length,\r\n totalWarnings,\r\n durationMs,\r\n cacheStats,\r\n };\r\n }\r\n\r\n /**\r\n * Resolve the cache: load existing cache, detect changes, determine which\r\n * entities need to be fetched vs. can be served from cache.\r\n *\r\n * On any failure (corrupt cache, expired stamp), falls back to full refresh.\r\n */\r\n private async resolveCache(\r\n httpClient: DataverseHttpClient,\r\n requestedEntities: Set<string>,\r\n ): Promise<{\r\n cache: MetadataCache;\r\n cachedEntities: Record<string, EntityTypeInfo>;\r\n deletedEntityNames: string[];\r\n newVersionStamp: string | null;\r\n stats: CacheStats;\r\n }> {\r\n const cache = new MetadataCache(this.config.cacheDir);\r\n const changeDetector = new ChangeDetector(httpClient);\r\n\r\n // Try to load existing cache\r\n let cacheData;\r\n try {\r\n cacheData = await cache.load(this.config.environmentUrl);\r\n } catch {\r\n this.logger.warn('Failed to load metadata cache, performing full refresh');\r\n cacheData = null;\r\n }\r\n\r\n if (!cacheData || !cacheData.manifest.serverVersionStamp) {\r\n // No cache or no stamp: full refresh, but get initial stamp for next run\r\n this.logger.info('No valid cache found, performing full metadata refresh');\r\n const stamp = await changeDetector.getInitialVersionStamp();\r\n return {\r\n cache,\r\n cachedEntities: {},\r\n deletedEntityNames: [],\r\n newVersionStamp: stamp,\r\n stats: {\r\n cacheUsed: true,\r\n fullRefresh: true,\r\n entitiesFromCache: 0,\r\n entitiesFetched: requestedEntities.size,\r\n entitiesDeleted: 0,\r\n },\r\n };\r\n }\r\n\r\n // Cache exists with stamp: try delta detection\r\n let changeResult;\r\n try {\r\n changeResult = await changeDetector.detectChanges(cacheData.manifest.serverVersionStamp);\r\n } catch (error: unknown) {\r\n // Expired stamp or other error: fall back to full refresh\r\n const isExpired = error instanceof Error &&\r\n error.message.includes(ErrorCode.META_VERSION_STAMP_EXPIRED);\r\n if (isExpired) {\r\n this.logger.warn('Cache version stamp expired (>90 days), performing full refresh');\r\n } else {\r\n this.logger.warn('Change detection failed, performing full refresh', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n const stamp = await changeDetector.getInitialVersionStamp();\r\n return {\r\n cache,\r\n cachedEntities: {},\r\n deletedEntityNames: [],\r\n newVersionStamp: stamp,\r\n stats: {\r\n cacheUsed: true,\r\n fullRefresh: true,\r\n entitiesFromCache: 0,\r\n entitiesFetched: requestedEntities.size,\r\n entitiesDeleted: 0,\r\n },\r\n };\r\n }\r\n\r\n // Delta detection succeeded: determine which entities to fetch\r\n const changedSet = new Set(changeResult.changedEntityNames);\r\n const cachedEntities: Record<string, EntityTypeInfo> = {};\r\n let entitiesFromCache = 0;\r\n let entitiesFetched = 0;\r\n\r\n for (const entityName of requestedEntities) {\r\n if (changedSet.has(entityName) || !cacheData.entityTypeInfos[entityName]) {\r\n // Entity changed or not in cache: needs fresh fetch\r\n entitiesFetched++;\r\n } else {\r\n // Entity unchanged and in cache: use cached version\r\n cachedEntities[entityName] = cacheData.entityTypeInfos[entityName]!;\r\n entitiesFromCache++;\r\n }\r\n }\r\n\r\n // Deleted entities that were in our entity list\r\n const deletedEntityNames = changeResult.deletedEntityNames.filter(\r\n (name) => requestedEntities.has(name),\r\n );\r\n\r\n this.logger.info(`Cache delta: ${entitiesFromCache} from cache, ${entitiesFetched} to fetch, ${deletedEntityNames.length} deleted`);\r\n\r\n return {\r\n cache,\r\n cachedEntities,\r\n deletedEntityNames,\r\n newVersionStamp: changeResult.newVersionStamp,\r\n stats: {\r\n cacheUsed: true,\r\n fullRefresh: false,\r\n entitiesFromCache,\r\n entitiesFetched,\r\n entitiesDeleted: deletedEntityNames.length,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Update the metadata cache after a successful generation run.\r\n */\r\n private async updateCache(\r\n cache: MetadataCache,\r\n entityTypeInfos: Record<string, EntityTypeInfo>,\r\n deletedEntityNames: string[],\r\n newVersionStamp: string | null,\r\n ): Promise<void> {\r\n try {\r\n if (deletedEntityNames.length > 0) {\r\n await cache.removeEntities(this.config.environmentUrl, deletedEntityNames, newVersionStamp);\r\n }\r\n await cache.save(this.config.environmentUrl, entityTypeInfos, newVersionStamp);\r\n this.logger.info('Metadata cache updated');\r\n } catch (error: unknown) {\r\n // Cache update failure is non-fatal\r\n this.logger.warn('Failed to update metadata cache', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Generate all output files for a single entity from its metadata.\r\n */\r\n private generateEntityFiles(\r\n entityName: string,\r\n entityInfo: EntityTypeInfo,\r\n ): EntityGenerationResult {\r\n const warnings: string[] = [];\r\n const files: GeneratedFile[] = [];\r\n\r\n // Generate entity interface\r\n if (this.config.generateEntities) {\r\n const entityContent = generateEntityInterface(entityInfo, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n files.push({\r\n relativePath: `entities/${entityName}.ts`,\r\n content: addGeneratedHeader(entityContent),\r\n type: 'entity',\r\n });\r\n }\r\n\r\n // Generate OptionSet enums\r\n if (this.config.generateOptionSets) {\r\n const picklistAttrs = this.getPicklistAttributes(entityInfo);\r\n\r\n if (picklistAttrs.length > 0) {\r\n const optionSets = generateEntityOptionSets(picklistAttrs, entityName, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n\r\n if (optionSets.length > 0) {\r\n // Combine all OptionSets for one entity into a single file\r\n const combinedContent = optionSets.map((os) => os.content).join('\\n');\r\n files.push({\r\n relativePath: `optionsets/${entityName}.ts`,\r\n content: addGeneratedHeader(combinedContent),\r\n type: 'optionset',\r\n });\r\n }\r\n } else {\r\n warnings.push(`No OptionSet attributes found for ${entityName}`);\r\n }\r\n }\r\n\r\n // Generate form interfaces\r\n if (this.config.generateForms) {\r\n if (entityInfo.forms.length > 0) {\r\n const formResults = generateEntityForms(\r\n entityInfo.forms,\r\n entityName,\r\n entityInfo.attributes,\r\n {\r\n labelConfig: this.config.labelConfig,\r\n },\r\n );\r\n\r\n if (formResults.length > 0) {\r\n // Combine all forms for one entity into a single file\r\n const combinedContent = formResults.map((f) => f.content).join('\\n');\r\n files.push({\r\n relativePath: `forms/${entityName}.ts`,\r\n content: addGeneratedHeader(combinedContent),\r\n type: 'form',\r\n });\r\n }\r\n } else {\r\n warnings.push(`No forms found for ${entityName}`);\r\n }\r\n }\r\n\r\n // Generate entity fields enum and navigation properties (R4-03)\r\n if (this.config.generateEntities) {\r\n const fieldsEnumContent = generateEntityFieldsEnum(entityInfo, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n const navPropsContent = generateEntityNavigationProperties(entityInfo, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n\r\n // Combine both outputs into a single file (navProps may be empty if no lookups)\r\n const combinedFieldsContent = navPropsContent\r\n ? `${fieldsEnumContent}\\n${navPropsContent}`\r\n : fieldsEnumContent;\r\n\r\n files.push({\r\n relativePath: `fields/${entityName}.ts`,\r\n content: addGeneratedHeader(combinedFieldsContent),\r\n type: 'fields',\r\n });\r\n }\r\n\r\n return { entityLogicalName: entityName, files, warnings };\r\n }\r\n\r\n /**\r\n * Generate Custom API Action/Function executor files.\r\n */\r\n private async generateActions(metadataClient: MetadataClient): Promise<GeneratedFile[]> {\r\n const files: GeneratedFile[] = [];\r\n\r\n this.logger.info('Fetching Custom APIs...');\r\n let customApis = await metadataClient.getCustomApis();\r\n\r\n // Apply prefix filter if configured\r\n if (this.config.actionsFilter) {\r\n const prefix = this.config.actionsFilter.toLowerCase();\r\n const before = customApis.length;\r\n customApis = customApis.filter((api) => api.api.uniquename.toLowerCase().startsWith(prefix));\r\n this.logger.info(`Filtered Custom APIs by prefix \"${this.config.actionsFilter}\": ${before} -> ${customApis.length}`);\r\n }\r\n\r\n if (customApis.length > 0) {\r\n const importPath = '@xrmforge/helpers';\r\n const grouped = groupCustomApis(customApis);\r\n\r\n for (const [key, apis] of grouped.actions) {\r\n const entityName = key === 'global' ? undefined : key;\r\n const declarations = generateActionDeclarations(apis, false, entityName, { importPath });\r\n const module = generateActionModule(apis, false, { importPath });\r\n\r\n // Combine declarations and module into a single .ts file\r\n files.push({\r\n relativePath: `actions/${key}.ts`,\r\n content: addGeneratedHeader(`${declarations}\\n${module}`),\r\n type: 'action',\r\n });\r\n }\r\n\r\n for (const [key, apis] of grouped.functions) {\r\n const entityName = key === 'global' ? undefined : key;\r\n const declarations = generateActionDeclarations(apis, true, entityName, { importPath });\r\n const module = generateActionModule(apis, true, { importPath });\r\n\r\n // Combine declarations and module into a single .ts file\r\n files.push({\r\n relativePath: `functions/${key}.ts`,\r\n content: addGeneratedHeader(`${declarations}\\n${module}`),\r\n type: 'action',\r\n });\r\n }\r\n\r\n this.logger.info(`Generated ${grouped.actions.size} action groups, ${grouped.functions.size} function groups`);\r\n } else {\r\n this.logger.info('No Custom APIs found');\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Extract picklist attributes with their OptionSet metadata.\r\n * Maps the raw EntityTypeInfo data to the format expected by the OptionSet generator.\r\n */\r\n private getPicklistAttributes(\r\n entityInfo: EntityTypeInfo,\r\n ): Array<{ SchemaName: string; OptionSet: OptionSetMetadata | null; GlobalOptionSet: OptionSetMetadata | null }> {\r\n const result: Array<{ SchemaName: string; OptionSet: OptionSetMetadata | null; GlobalOptionSet: OptionSetMetadata | null }> = [];\r\n\r\n for (const attr of entityInfo.picklistAttributes) {\r\n result.push({\r\n SchemaName: attr.SchemaName,\r\n OptionSet: attr.OptionSet ?? null,\r\n GlobalOptionSet: attr.GlobalOptionSet ?? null,\r\n });\r\n }\r\n\r\n // Also include State and Status attributes\r\n for (const attr of entityInfo.stateAttributes) {\r\n result.push({\r\n SchemaName: attr.SchemaName,\r\n OptionSet: attr.OptionSet ?? null,\r\n GlobalOptionSet: null,\r\n });\r\n }\r\n for (const attr of entityInfo.statusAttributes) {\r\n result.push({\r\n SchemaName: attr.SchemaName,\r\n OptionSet: attr.OptionSet ?? null,\r\n GlobalOptionSet: null,\r\n });\r\n }\r\n\r\n return result;\r\n }\r\n}\r\n"],"mappings":"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","names":["ErrorCode","LogLevel","log","path","resolve","log","log","path","log","log","path","log","labelToPascalMember","join"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/logger.ts","../src/auth/credential.ts","../src/http/client.ts","../src/metadata/xml-parser.ts","../src/metadata/form-parser.ts","../src/metadata/client.ts","../src/metadata/cache.ts","../src/metadata/change-detector.ts","../src/metadata/labels.ts","../src/generators/type-mapping.ts","../src/generators/activity-party.ts","../src/generators/label-utils.ts","../src/generators/entity-generator.ts","../src/generators/optionset-generator.ts","../src/generators/form-generator.ts","../src/generators/entity-fields-generator.ts","../src/generators/entity-names-generator.ts","../src/metadata/custom-api-types.ts","../src/generators/action-generator.ts","../src/orchestrator/file-writer.ts","../src/orchestrator/orchestrator.ts"],"sourcesContent":["/**\r\n * @xrmforge/typegen - Error Types\r\n *\r\n * Centralized error hierarchy for consistent error handling across the framework.\r\n * Every error carries a machine-readable code, a human-readable message,\r\n * and optional context for debugging.\r\n */\r\n\r\nexport enum ErrorCode {\r\n // Authentication errors (1xxx)\r\n AUTH_MISSING_CONFIG = 'AUTH_1001',\r\n AUTH_INVALID_CREDENTIALS = 'AUTH_1002',\r\n AUTH_TOKEN_FAILED = 'AUTH_1003',\r\n AUTH_TOKEN_EXPIRED = 'AUTH_1004',\r\n\r\n // API errors (2xxx)\r\n API_REQUEST_FAILED = 'API_2001',\r\n API_RATE_LIMITED = 'API_2002',\r\n API_NOT_FOUND = 'API_2003',\r\n API_UNAUTHORIZED = 'API_2004',\r\n API_TIMEOUT = 'API_2005',\r\n\r\n // Metadata errors (3xxx)\r\n META_ENTITY_NOT_FOUND = 'META_3001',\r\n META_SOLUTION_NOT_FOUND = 'META_3002',\r\n META_FORM_PARSE_FAILED = 'META_3003',\r\n META_ATTRIBUTE_UNKNOWN_TYPE = 'META_3004',\r\n META_VERSION_STAMP_EXPIRED = 'META_3005',\r\n\r\n // Generation errors (4xxx)\r\n GEN_OUTPUT_WRITE_FAILED = 'GEN_4001',\r\n GEN_TEMPLATE_FAILED = 'GEN_4002',\r\n GEN_INVALID_IDENTIFIER = 'GEN_4003',\r\n\r\n // Config errors (5xxx)\r\n CONFIG_INVALID = 'CONFIG_5001',\r\n CONFIG_FILE_NOT_FOUND = 'CONFIG_5002',\r\n CONFIG_ENV_VAR_MISSING = 'CONFIG_5003',\r\n}\r\n\r\n/**\r\n * Base error class for all XrmForge errors.\r\n * Carries a structured error code and optional context object.\r\n */\r\nexport class XrmForgeError extends Error {\r\n public readonly code: ErrorCode;\r\n public readonly context: Record<string, unknown>;\r\n\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(`[${code}] ${message}`);\r\n this.name = 'XrmForgeError';\r\n this.code = code;\r\n this.context = context;\r\n\r\n // Maintains proper stack trace in V8 environments\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, XrmForgeError);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Authentication-specific error.\r\n */\r\nexport class AuthenticationError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'AuthenticationError';\r\n }\r\n}\r\n\r\n/**\r\n * Dataverse API request error.\r\n */\r\nexport class ApiRequestError extends XrmForgeError {\r\n public readonly statusCode: number | undefined;\r\n public readonly responseBody: string | undefined;\r\n\r\n constructor(\r\n code: ErrorCode,\r\n message: string,\r\n context: Record<string, unknown> & {\r\n statusCode?: number;\r\n responseBody?: string;\r\n url?: string;\r\n } = {},\r\n ) {\r\n super(code, message, context);\r\n this.name = 'ApiRequestError';\r\n this.statusCode = context.statusCode;\r\n this.responseBody = context.responseBody;\r\n }\r\n}\r\n\r\n/**\r\n * Metadata retrieval or parsing error.\r\n */\r\nexport class MetadataError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'MetadataError';\r\n }\r\n}\r\n\r\n/**\r\n * Type generation or file output error.\r\n */\r\nexport class GenerationError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'GenerationError';\r\n }\r\n}\r\n\r\n/**\r\n * Configuration validation error.\r\n */\r\nexport class ConfigError extends XrmForgeError {\r\n constructor(code: ErrorCode, message: string, context: Record<string, unknown> = {}) {\r\n super(code, message, context);\r\n this.name = 'ConfigError';\r\n }\r\n}\r\n\r\n/**\r\n * Type guard to check if an unknown error is an XrmForgeError.\r\n */\r\nexport function isXrmForgeError(error: unknown): error is XrmForgeError {\r\n return error instanceof XrmForgeError;\r\n}\r\n\r\n/**\r\n * Type guard for API rate limit errors (HTTP 429).\r\n */\r\nexport function isRateLimitError(error: unknown): error is ApiRequestError {\r\n return error instanceof ApiRequestError && error.code === ErrorCode.API_RATE_LIMITED;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Logger\r\n *\r\n * Abstracted logging interface that decouples log output from the modules.\r\n * Supports: CLI (human-readable), CI/CD (structured JSON), silent (library use).\r\n *\r\n * Every logger instance carries a `scope` (e.g. \"auth\", \"metadata\", \"http\")\r\n * so log consumers can filter by origin module.\r\n *\r\n * Consumers can provide their own LogSink to integrate with any logging framework.\r\n */\r\n\r\nexport enum LogLevel {\r\n DEBUG = 0,\r\n INFO = 1,\r\n WARN = 2,\r\n ERROR = 3,\r\n SILENT = 4,\r\n}\r\n\r\nexport interface LogEntry {\r\n level: LogLevel;\r\n scope: string;\r\n message: string;\r\n context?: Record<string, unknown>;\r\n timestamp: Date;\r\n}\r\n\r\n/**\r\n * Interface for log output destinations.\r\n * Implement this to route XrmForge logs into your own logging system.\r\n */\r\nexport interface LogSink {\r\n write(entry: LogEntry): void;\r\n\r\n /**\r\n * Write an inline progress update (no trailing newline).\r\n * Used for long-running operations where each entity gets a status indicator.\r\n *\r\n * Sinks that don't support inline progress (e.g. JSON) should fall back\r\n * to writing a regular INFO entry.\r\n */\r\n writeProgress(message: string): void;\r\n\r\n /**\r\n * Complete an inline progress line with a trailing message and newline.\r\n */\r\n writeProgressEnd(message: string): void;\r\n}\r\n\r\n/**\r\n * Default CLI log sink with human-readable output and ANSI color indicators.\r\n */\r\nexport class ConsoleLogSink implements LogSink {\r\n private static readonly LEVEL_PREFIX: Readonly<Record<LogLevel, string>> = {\r\n [LogLevel.DEBUG]: '\\x1b[90m[DBG]\\x1b[0m',\r\n [LogLevel.INFO]: '\\x1b[36m[INF]\\x1b[0m',\r\n [LogLevel.WARN]: '\\x1b[33m[WRN]\\x1b[0m',\r\n [LogLevel.ERROR]: '\\x1b[31m[ERR]\\x1b[0m',\r\n [LogLevel.SILENT]: '',\r\n };\r\n\r\n write(entry: LogEntry): void {\r\n if (entry.level === LogLevel.SILENT) return;\r\n\r\n const prefix = ConsoleLogSink.LEVEL_PREFIX[entry.level];\r\n const scope = `\\x1b[90m[${entry.scope}]\\x1b[0m`;\r\n const message = `${prefix} ${scope} ${entry.message}`;\r\n\r\n switch (entry.level) {\r\n case LogLevel.ERROR:\r\n console.error(message);\r\n break;\r\n case LogLevel.WARN:\r\n console.warn(message);\r\n break;\r\n default:\r\n console.log(message);\r\n }\r\n\r\n if (entry.context && entry.level === LogLevel.DEBUG) {\r\n console.log(' Context:', JSON.stringify(entry.context, null, 2));\r\n }\r\n }\r\n\r\n writeProgress(message: string): void {\r\n process.stdout.write(message);\r\n }\r\n\r\n writeProgressEnd(message: string): void {\r\n console.log(message);\r\n }\r\n}\r\n\r\n/**\r\n * Structured JSON log sink for CI/CD pipelines and machine-readable output.\r\n */\r\nexport class JsonLogSink implements LogSink {\r\n write(entry: LogEntry): void {\r\n if (entry.level === LogLevel.SILENT) return;\r\n\r\n const output = {\r\n timestamp: entry.timestamp.toISOString(),\r\n level: LogLevel[entry.level],\r\n scope: entry.scope,\r\n message: entry.message,\r\n ...(entry.context ? { context: entry.context } : {}),\r\n };\r\n\r\n console.log(JSON.stringify(output));\r\n }\r\n\r\n writeProgress(message: string): void {\r\n console.log(JSON.stringify({\r\n timestamp: new Date().toISOString(),\r\n level: 'INFO',\r\n scope: 'progress',\r\n message: message.trim(),\r\n }));\r\n }\r\n\r\n writeProgressEnd(message: string): void {\r\n console.log(JSON.stringify({\r\n timestamp: new Date().toISOString(),\r\n level: 'INFO',\r\n scope: 'progress',\r\n message: message.trim(),\r\n }));\r\n }\r\n}\r\n\r\n/**\r\n * Silent log sink that discards all output. Used when running as a library.\r\n */\r\nexport class SilentLogSink implements LogSink {\r\n write(_entry: LogEntry): void {}\r\n writeProgress(_message: string): void {}\r\n writeProgressEnd(_message: string): void {}\r\n}\r\n\r\n// ─── Logger Class ────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Logger with scope prefix and configurable sink/level.\r\n *\r\n * Usage:\r\n * ```ts\r\n * const log = createLogger('auth');\r\n * log.info('Token acquired', { expiresIn: '3600s' });\r\n * // Output: [INF] [auth] Token acquired\r\n * ```\r\n */\r\nexport class Logger {\r\n private readonly scope: string;\r\n private readonly getSink: () => LogSink;\r\n private readonly getMinLevel: () => LogLevel;\r\n\r\n constructor(scope: string, getSink: () => LogSink, getMinLevel: () => LogLevel) {\r\n this.scope = scope;\r\n this.getSink = getSink;\r\n this.getMinLevel = getMinLevel;\r\n }\r\n\r\n debug(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.DEBUG, message, context);\r\n }\r\n\r\n info(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.INFO, message, context);\r\n }\r\n\r\n warn(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.WARN, message, context);\r\n }\r\n\r\n error(message: string, context?: Record<string, unknown>): void {\r\n this.log(LogLevel.ERROR, message, context);\r\n }\r\n\r\n /**\r\n * Write an inline progress update (no newline).\r\n */\r\n progress(message: string): void {\r\n if (this.getMinLevel() > LogLevel.INFO) return;\r\n this.getSink().writeProgress(message);\r\n }\r\n\r\n /**\r\n * Complete an inline progress line.\r\n */\r\n progressEnd(message: string): void {\r\n if (this.getMinLevel() > LogLevel.INFO) return;\r\n this.getSink().writeProgressEnd(message);\r\n }\r\n\r\n private log(level: LogLevel, message: string, context?: Record<string, unknown>): void {\r\n if (level < this.getMinLevel()) return;\r\n\r\n this.getSink().write({\r\n level,\r\n scope: this.scope,\r\n message,\r\n context,\r\n timestamp: new Date(),\r\n });\r\n }\r\n}\r\n\r\n// ─── Global Configuration ────────────────────────────────────────────────────\r\n\r\nlet _sharedSink: LogSink = new ConsoleLogSink();\r\nlet _sharedMinLevel: LogLevel = LogLevel.INFO;\r\n\r\n/**\r\n * Configure logging globally for all @xrmforge modules.\r\n * Can be called at any time; existing loggers will pick up the new configuration\r\n * automatically because they reference the shared state via closures.\r\n *\r\n * @example\r\n * ```ts\r\n * configureLogging({ sink: new JsonLogSink(), minLevel: LogLevel.WARN });\r\n * ```\r\n */\r\nexport function configureLogging(options: { sink?: LogSink; minLevel?: LogLevel }): void {\r\n if (options.sink !== undefined) _sharedSink = options.sink;\r\n if (options.minLevel !== undefined) _sharedMinLevel = options.minLevel;\r\n}\r\n\r\n/**\r\n * Create a scoped logger instance. All modules should use this instead of console.log.\r\n *\r\n * The logger reads the global sink and minLevel at each log call (not at creation time),\r\n * so `configureLogging()` takes effect even on previously created loggers.\r\n *\r\n * @param scope - Module identifier shown in log output, e.g. \"auth\", \"metadata\", \"http\"\r\n */\r\nexport function createLogger(scope: string): Logger {\r\n return new Logger(\r\n scope,\r\n () => _sharedSink,\r\n () => _sharedMinLevel,\r\n );\r\n}\r\n","/**\r\n * @xrmforge/typegen - Authentication Module\r\n *\r\n * Handles authentication to Dataverse Web API using MSAL (@azure/identity).\r\n * Supports: Client Credentials (Service Principal), Interactive Browser, Device Code.\r\n *\r\n * Token acquisition and caching is handled by the DataverseHttpClient.\r\n * This module is responsible only for creating the correct TokenCredential.\r\n */\r\n\r\nimport {\r\n ClientSecretCredential,\r\n InteractiveBrowserCredential,\r\n DeviceCodeCredential,\r\n type TokenCredential,\r\n} from '@azure/identity';\r\nimport { AuthenticationError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('auth');\r\n\r\n// ─── Configuration Types ─────────────────────────────────────────────────────\r\n\r\nexport type AuthMethod = 'client-credentials' | 'interactive' | 'device-code' | 'token';\r\n\r\nexport interface ClientCredentialsAuth {\r\n method: 'client-credentials';\r\n tenantId: string;\r\n clientId: string;\r\n clientSecret: string;\r\n}\r\n\r\nexport interface InteractiveAuth {\r\n method: 'interactive';\r\n tenantId?: string;\r\n clientId?: string;\r\n}\r\n\r\nexport interface DeviceCodeAuth {\r\n method: 'device-code';\r\n tenantId?: string;\r\n clientId?: string;\r\n}\r\n\r\nexport interface TokenAuth {\r\n method: 'token';\r\n /** Pre-acquired Bearer token (e.g. from TokenVault, Key Vault, CI/CD secret) */\r\n token: string;\r\n}\r\n\r\nexport type AuthConfig = ClientCredentialsAuth | InteractiveAuth | DeviceCodeAuth | TokenAuth;\r\n\r\n/**\r\n * Default App ID provided by Microsoft for dev/prototyping scenarios.\r\n * For production, create a dedicated App Registration in Azure AD.\r\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/xrm-tooling/use-connection-strings-xrm-tooling-connect\r\n */\r\nconst DEFAULT_CLIENT_ID = '51f81489-12ee-4a9e-aaae-a2591f45987d';\r\n\r\n// ─── Credential Factory ──────────────────────────────────────────────────────\r\n\r\n/**\r\n * Creates an Azure Identity TokenCredential from the provided auth configuration.\r\n * Validates required fields before attempting credential creation.\r\n *\r\n * @throws {AuthenticationError} if required configuration values are missing\r\n */\r\nexport function createCredential(config: AuthConfig): TokenCredential {\r\n switch (config.method) {\r\n case 'client-credentials':\r\n return createClientCredential(config);\r\n\r\n case 'interactive':\r\n return createInteractiveCredential(config);\r\n\r\n case 'device-code':\r\n return createDeviceCodeCredential(config);\r\n\r\n case 'token':\r\n return createStaticTokenCredential(config);\r\n\r\n default: {\r\n // Exhaustiveness check: this should never happen with proper TypeScript usage\r\n const exhaustiveCheck: never = config;\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_MISSING_CONFIG,\r\n `Unknown authentication method: \"${(exhaustiveCheck as AuthConfig).method}\". ` +\r\n `Supported methods: client-credentials, interactive, device-code.`,\r\n );\r\n }\r\n }\r\n}\r\n\r\n// ─── Internal Credential Builders ────────────────────────────────────────────\r\n\r\nfunction createClientCredential(config: ClientCredentialsAuth): TokenCredential {\r\n const missing: string[] = [];\r\n if (!config.tenantId?.trim()) missing.push('tenantId');\r\n if (!config.clientId?.trim()) missing.push('clientId');\r\n if (!config.clientSecret?.trim()) missing.push('clientSecret');\r\n\r\n if (missing.length > 0) {\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_MISSING_CONFIG,\r\n `Client credentials authentication requires: ${missing.join(', ')}. ` +\r\n `These can be set in xrmforge.config.json or via environment variables ` +\r\n `(XRMFORGE_TENANT_ID, XRMFORGE_CLIENT_ID, XRMFORGE_CLIENT_SECRET).`,\r\n { missingFields: missing },\r\n );\r\n }\r\n\r\n log.debug('Creating client credentials (Service Principal)', {\r\n tenantId: config.tenantId,\r\n clientId: config.clientId,\r\n });\r\n\r\n return new ClientSecretCredential(config.tenantId, config.clientId, config.clientSecret);\r\n}\r\n\r\nfunction createInteractiveCredential(config: InteractiveAuth): TokenCredential {\r\n const clientId = config.clientId?.trim() || DEFAULT_CLIENT_ID;\r\n\r\n log.debug('Creating interactive browser credential', {\r\n clientId,\r\n tenantId: config.tenantId ?? 'common',\r\n usingDefaultClientId: !config.clientId,\r\n });\r\n\r\n return new InteractiveBrowserCredential({\r\n tenantId: config.tenantId,\r\n clientId,\r\n });\r\n}\r\n\r\nfunction createDeviceCodeCredential(config: DeviceCodeAuth): TokenCredential {\r\n const clientId = config.clientId?.trim() || DEFAULT_CLIENT_ID;\r\n\r\n log.debug('Creating device code credential', {\r\n clientId,\r\n tenantId: config.tenantId ?? 'common',\r\n usingDefaultClientId: !config.clientId,\r\n });\r\n\r\n return new DeviceCodeCredential({\r\n tenantId: config.tenantId,\r\n clientId,\r\n userPromptCallback: (info) => {\r\n log.info('Device Code Authentication required:');\r\n log.info(info.message);\r\n },\r\n });\r\n}\r\n\r\nfunction createStaticTokenCredential(config: TokenAuth): TokenCredential {\r\n if (!config.token?.trim()) {\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_MISSING_CONFIG,\r\n 'Token authentication requires a non-empty token. ' +\r\n 'Set XRMFORGE_TOKEN environment variable or use --token flag.',\r\n );\r\n }\r\n\r\n log.debug('Using pre-acquired token (static credential)');\r\n\r\n return new StaticTokenCredential(config.token);\r\n}\r\n\r\n// ─── Static Token Credential ────────────────────────────────────────────────\r\n\r\n/**\r\n * A TokenCredential that wraps a pre-acquired Bearer token.\r\n * Useful for integration with external token sources like:\r\n * - Markant TokenVault (Get-VaultToken -System 'dataverse_dev')\r\n * - Azure Key Vault\r\n * - CI/CD pipeline secrets\r\n *\r\n * Note: This credential does NOT handle token refresh. If the token expires,\r\n * the next API call will fail with HTTP 401.\r\n */\r\nexport class StaticTokenCredential implements TokenCredential {\r\n private readonly token: string;\r\n\r\n constructor(token: string) {\r\n this.token = token;\r\n }\r\n\r\n async getToken(): Promise<{ token: string; expiresOnTimestamp: number }> {\r\n // Return the static token with a far-future expiry.\r\n // The HttpClient's 401-retry logic will clear the cache and call getToken again,\r\n // but since we have no refresh mechanism, it will return the same (possibly expired) token.\r\n return {\r\n token: this.token,\r\n expiresOnTimestamp: Date.now() + 60 * 60 * 1000, // Pretend 1 hour validity\r\n };\r\n }\r\n}\r\n\r\n","/**\r\n * @xrmforge/typegen - Dataverse HTTP Client\r\n *\r\n * Resilient HTTP client for the Dataverse Web API.\r\n *\r\n * Features:\r\n * - Token caching with 5-minute buffer before expiry\r\n * - Automatic retry with exponential backoff and jitter\r\n * - Rate limit awareness (HTTP 429 with Retry-After)\r\n * - Request timeout via AbortController\r\n * - Concurrency control (semaphore pattern, NOT recursive)\r\n * - Automatic OData paging via @odata.nextLink with safety limit\r\n * - Input sanitization helpers against OData injection\r\n */\r\n\r\nimport type { TokenCredential, AccessToken } from '@azure/identity';\r\nimport { ApiRequestError, AuthenticationError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('http');\r\n\r\n// ─── Internal Constants ──────────────────────────────────────────────────────\r\n\r\n/** Buffer before token expiry to trigger re-acquisition (5 minutes) */\r\nconst TOKEN_BUFFER_MS = 5 * 60 * 1000;\r\n\r\n/** Maximum backoff delay cap for exponential retry (60 seconds) */\r\nconst MAX_BACKOFF_MS = 60_000;\r\n\r\n/** Maximum characters of response body stored in error context */\r\nconst MAX_RESPONSE_BODY_LENGTH = 2000;\r\n\r\n/** Maximum length of user-provided values in error messages (prevents log injection) */\r\nconst MAX_ERROR_VALUE_LENGTH = 100;\r\n\r\n/** Maximum consecutive HTTP 429 retries before giving up (prevents infinite loops) */\r\nconst DEFAULT_MAX_RATE_LIMIT_RETRIES = 10;\r\n\r\n// ─── Configuration ───────────────────────────────────────────────────────────\r\n\r\nexport interface HttpClientOptions {\r\n /** Dataverse environment URL, e.g. \"https://myorg.crm4.dynamics.com\" */\r\n environmentUrl: string;\r\n\r\n /** Azure Identity credential */\r\n credential: TokenCredential;\r\n\r\n /** API version (default: \"v9.2\") */\r\n apiVersion?: string;\r\n\r\n /** Maximum retry attempts for transient errors (default: 3) */\r\n maxRetries?: number;\r\n\r\n /** Base delay in ms for exponential backoff (default: 1000) */\r\n retryBaseDelayMs?: number;\r\n\r\n /** Request timeout in ms (default: 30000) */\r\n timeoutMs?: number;\r\n\r\n /** Maximum concurrent requests to Dataverse (default: 5) */\r\n maxConcurrency?: number;\r\n\r\n /** Maximum pages to follow via @odata.nextLink (default: 100, safety limit) */\r\n maxPages?: number;\r\n\r\n /** Maximum consecutive HTTP 429 retries before giving up (default: 10) */\r\n maxRateLimitRetries?: number;\r\n\r\n /**\r\n * Read-only mode (default: true).\r\n * When true, the client will ONLY allow GET requests and throw an error\r\n * for any POST, PATCH, PUT, or DELETE attempt.\r\n *\r\n * SAFETY: XrmForge typegen is a read-only tool. It must NEVER modify\r\n * data in Dataverse environments. This flag defaults to true and should\r\n * only be set to false for the @xrmforge/webapi package (future).\r\n */\r\n readOnly?: boolean;\r\n}\r\n\r\n// ─── Token Cache ─────────────────────────────────────────────────────────────\r\n\r\ninterface CachedToken {\r\n token: string;\r\n expiresAt: number; // Unix timestamp in ms\r\n}\r\n\r\n// ─── Client ──────────────────────────────────────────────────────────────────\r\n\r\nexport class DataverseHttpClient {\r\n private readonly baseUrl: string;\r\n private readonly apiVersion: string;\r\n private readonly credential: TokenCredential;\r\n private readonly maxRetries: number;\r\n private readonly retryBaseDelayMs: number;\r\n private readonly timeoutMs: number;\r\n private readonly maxConcurrency: number;\r\n private readonly maxPages: number;\r\n private readonly maxRateLimitRetries: number;\r\n private readonly readOnly: boolean;\r\n\r\n private cachedToken: CachedToken | null = null;\r\n /** Pending token refresh promise (prevents concurrent token requests) */\r\n private pendingTokenRefresh: Promise<string> | null = null;\r\n\r\n // Semaphore for concurrency control (non-recursive)\r\n private activeConcurrentRequests = 0;\r\n private readonly waitQueue: Array<() => void> = [];\r\n\r\n constructor(options: HttpClientOptions) {\r\n this.baseUrl = options.environmentUrl.replace(/\\/$/, '');\r\n this.apiVersion = options.apiVersion ?? 'v9.2';\r\n this.credential = options.credential;\r\n this.maxRetries = options.maxRetries ?? 3;\r\n this.retryBaseDelayMs = options.retryBaseDelayMs ?? 1000;\r\n this.timeoutMs = options.timeoutMs ?? 30_000;\r\n this.maxConcurrency = options.maxConcurrency ?? 5;\r\n this.maxPages = options.maxPages ?? 100;\r\n this.maxRateLimitRetries = options.maxRateLimitRetries ?? DEFAULT_MAX_RATE_LIMIT_RETRIES;\r\n this.readOnly = options.readOnly ?? true; // SAFETY: default to read-only\r\n }\r\n\r\n /**\r\n * Full API base URL, e.g. \"https://myorg.crm4.dynamics.com/api/data/v9.2\"\r\n */\r\n get apiUrl(): string {\r\n return `${this.baseUrl}/api/data/${this.apiVersion}`;\r\n }\r\n\r\n // ─── Public API ──────────────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute a GET request against the Dataverse Web API.\r\n * Handles token caching, retries, rate limits, and timeout.\r\n *\r\n * @param path - API path (relative or absolute URL)\r\n * @param signal - Optional AbortSignal to cancel the request\r\n */\r\n async get<T>(path: string, signal?: AbortSignal): Promise<T> {\r\n const url = this.resolveUrl(path);\r\n return this.executeWithConcurrency<T>(url, signal);\r\n }\r\n\r\n /**\r\n * Execute a GET request and automatically follow @odata.nextLink for paging.\r\n * Returns all pages combined into a single array.\r\n *\r\n * Safety: Stops after `maxPages` iterations to prevent infinite loops.\r\n *\r\n * @param path - API path (relative or absolute URL)\r\n * @param signal - Optional AbortSignal to cancel the request\r\n */\r\n async getAll<T>(path: string, signal?: AbortSignal): Promise<T[]> {\r\n interface ODataPage {\r\n value: T[];\r\n '@odata.nextLink'?: string;\r\n }\r\n\r\n const allResults: T[] = [];\r\n let currentUrl: string | null = this.resolveUrl(path);\r\n let page = 0;\r\n\r\n while (currentUrl) {\r\n // Check abort before each page\r\n if (signal?.aborted) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Request aborted after ${page} pages (${allResults.length} records retrieved)`,\r\n { url: currentUrl, pagesCompleted: page },\r\n );\r\n }\r\n\r\n page++;\r\n if (page > this.maxPages) {\r\n log.warn(\r\n `Stopped paging after ${this.maxPages} pages (safety limit). ` +\r\n `${allResults.length} records retrieved. Increase maxPages if this is expected.`,\r\n );\r\n break;\r\n }\r\n\r\n const response: ODataPage = await this.executeWithConcurrency<ODataPage>(currentUrl, signal);\r\n\r\n allResults.push(...response.value);\r\n currentUrl = response['@odata.nextLink'] ?? null;\r\n\r\n if (currentUrl) {\r\n log.debug(`Following @odata.nextLink (page ${page}), ${allResults.length} records so far`);\r\n }\r\n }\r\n\r\n return allResults;\r\n }\r\n\r\n // ─── Read-Only Enforcement ─────────────────────────────────────────────\r\n\r\n /**\r\n * Returns true if this client is in read-only mode (the safe default).\r\n */\r\n get isReadOnly(): boolean {\r\n return this.readOnly;\r\n }\r\n\r\n /**\r\n * Assert that a non-GET operation is allowed.\r\n * Throws immediately if the client is in read-only mode.\r\n *\r\n * @throws {ApiRequestError} always in read-only mode\r\n * @internal This method exists so that future packages (e.g. @xrmforge/webapi)\r\n * can reuse the HTTP client for write operations when readOnly is explicitly false.\r\n */\r\n assertWriteAllowed(operation: string): void {\r\n if (this.readOnly) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `BLOCKED: Write operation \"${operation}\" rejected. ` +\r\n `This client is in read-only mode (readOnly: true). ` +\r\n `XrmForge typegen must NEVER modify data in Dataverse. ` +\r\n `Set readOnly: false only for @xrmforge/webapi (not for typegen).`,\r\n { operation, readOnly: true },\r\n );\r\n }\r\n }\r\n\r\n // ─── Input Sanitization ──────────────────────────────────────────────────\r\n\r\n /**\r\n * Validate that a value is a safe OData identifier (entity name, attribute name).\r\n * Prevents OData injection by allowing only: starts with letter/underscore,\r\n * followed by alphanumeric/underscore.\r\n *\r\n * @throws {ApiRequestError} if the value contains invalid characters\r\n */\r\n static sanitizeIdentifier(value: string): string {\r\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(value)) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Invalid OData identifier: \"${value.substring(0, MAX_ERROR_VALUE_LENGTH).replace(/[\\r\\n]/g, '')}\". ` +\r\n `Only letters, digits, and underscores allowed; must start with a letter or underscore.`,\r\n { value: value.substring(0, MAX_ERROR_VALUE_LENGTH) },\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n /**\r\n * Validate that a value is a properly formatted GUID.\r\n *\r\n * @throws {ApiRequestError} if the format is invalid\r\n */\r\n static sanitizeGuid(value: string): string {\r\n const guidPattern =\r\n /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;\r\n if (!guidPattern.test(value)) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Invalid GUID format: \"${value}\".`,\r\n { value },\r\n );\r\n }\r\n return value;\r\n }\r\n\r\n /**\r\n * Escape a string for use inside OData single-quoted string literals.\r\n * Doubles single quotes to prevent injection.\r\n */\r\n static escapeODataString(value: string): string {\r\n return value.replace(/'/g, \"''\");\r\n }\r\n\r\n // ─── Token Management ────────────────────────────────────────────────────\r\n\r\n private async getToken(): Promise<string> {\r\n // Return cached token if it has more than 5 minutes of validity remaining\r\n if (this.cachedToken && this.cachedToken.expiresAt - Date.now() > TOKEN_BUFFER_MS) {\r\n return this.cachedToken.token;\r\n }\r\n\r\n // If a refresh is already in progress, wait for it instead of starting a second one\r\n if (this.pendingTokenRefresh) {\r\n return this.pendingTokenRefresh;\r\n }\r\n\r\n this.pendingTokenRefresh = this.refreshToken();\r\n try {\r\n return await this.pendingTokenRefresh;\r\n } finally {\r\n this.pendingTokenRefresh = null;\r\n }\r\n }\r\n\r\n /** Internal: actually acquire a new token from the credential provider. */\r\n private async refreshToken(): Promise<string> {\r\n log.debug('Requesting new access token');\r\n\r\n const scope = `${this.baseUrl}/.default`;\r\n let tokenResponse: AccessToken | null;\r\n\r\n try {\r\n tokenResponse = await this.credential.getToken(scope);\r\n } catch (error: unknown) {\r\n const cause = error instanceof Error ? error.message : String(error);\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_TOKEN_FAILED,\r\n `Failed to acquire access token for ${this.baseUrl}. ` +\r\n `Verify your authentication configuration.\\n` +\r\n `Cause: ${cause}`,\r\n {\r\n environmentUrl: this.baseUrl,\r\n originalError: cause,\r\n },\r\n );\r\n }\r\n\r\n if (!tokenResponse) {\r\n throw new AuthenticationError(\r\n ErrorCode.AUTH_TOKEN_FAILED,\r\n `No access token returned for ${this.baseUrl}. ` +\r\n `This may indicate invalid credentials or insufficient permissions.`,\r\n { environmentUrl: this.baseUrl },\r\n );\r\n }\r\n\r\n this.cachedToken = {\r\n token: tokenResponse.token,\r\n expiresAt: tokenResponse.expiresOnTimestamp,\r\n };\r\n\r\n log.debug('Access token acquired', {\r\n expiresIn: `${Math.round((tokenResponse.expiresOnTimestamp - Date.now()) / 1000)}s`,\r\n });\r\n\r\n return tokenResponse.token;\r\n }\r\n\r\n // ─── Concurrency Control ─────────────────────────────────────────────────\r\n\r\n /**\r\n * Execute a request within the concurrency semaphore.\r\n * The semaphore is acquired ONCE per logical request. Retries happen\r\n * INSIDE the semaphore to avoid the recursive slot exhaustion bug.\r\n */\r\n private async executeWithConcurrency<T>(url: string, signal?: AbortSignal, method: 'GET' | 'POST' = 'GET', requestBody?: unknown): Promise<T> {\r\n await this.acquireSlot();\r\n try {\r\n return await this.executeWithRetry<T>(url, 1, 0, signal, method, requestBody);\r\n } finally {\r\n this.releaseSlot();\r\n }\r\n }\r\n\r\n private acquireSlot(): Promise<void> {\r\n if (this.activeConcurrentRequests < this.maxConcurrency) {\r\n this.activeConcurrentRequests++;\r\n return Promise.resolve();\r\n }\r\n\r\n return new Promise<void>((resolve) => {\r\n this.waitQueue.push(() => {\r\n this.activeConcurrentRequests++;\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n private releaseSlot(): void {\r\n this.activeConcurrentRequests--;\r\n const next = this.waitQueue.shift();\r\n if (next) next();\r\n }\r\n\r\n // ─── Retry Logic (runs INSIDE a single concurrency slot) ─────────────────\r\n\r\n private async executeWithRetry<T>(url: string, attempt: number, rateLimitRetries: number = 0, signal?: AbortSignal, method: 'GET' | 'POST' = 'GET', requestBody?: unknown): Promise<T> {\r\n // Check if already aborted before starting\r\n if (signal?.aborted) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n 'Request aborted before execution',\r\n { url },\r\n );\r\n }\r\n\r\n const token = await this.getToken();\r\n\r\n // Combine user's AbortSignal with per-request timeout\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\r\n\r\n // If the user's signal fires, abort our controller too\r\n const onUserAbort = () => controller.abort();\r\n signal?.addEventListener('abort', onUserAbort, { once: true });\r\n\r\n let response: Response;\r\n try {\r\n log.debug(`${method} ${url}`, { attempt });\r\n\r\n const headers: Record<string, string> = {\r\n Authorization: `Bearer ${token}`,\r\n 'OData-MaxVersion': '4.0',\r\n 'OData-Version': '4.0',\r\n Accept: 'application/json',\r\n Prefer: 'odata.include-annotations=\"*\"',\r\n };\r\n if (method === 'POST') {\r\n headers['Content-Type'] = 'application/json';\r\n }\r\n\r\n response = await fetch(url, {\r\n method,\r\n headers,\r\n body: requestBody !== undefined ? JSON.stringify(requestBody) : undefined,\r\n signal: controller.signal,\r\n });\r\n } catch (fetchError: unknown) {\r\n clearTimeout(timeoutId);\r\n signal?.removeEventListener('abort', onUserAbort);\r\n\r\n // Distinguish user abort from timeout\r\n if ((fetchError instanceof Error) && fetchError.name === 'AbortError') {\r\n // If the USER aborted (not our timeout), don't retry\r\n if (signal?.aborted) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n 'Request aborted by caller',\r\n { url, attempt },\r\n );\r\n }\r\n\r\n // Timeout: retry if attempts remain\r\n if (attempt <= this.maxRetries) {\r\n const delay = this.calculateBackoff(attempt);\r\n log.warn(`Request timed out, retrying in ${delay}ms (${attempt}/${this.maxRetries})`, {\r\n url,\r\n });\r\n await this.sleep(delay);\r\n return this.executeWithRetry<T>(url, attempt + 1, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n throw new ApiRequestError(\r\n ErrorCode.API_TIMEOUT,\r\n `Request timed out after ${this.timeoutMs}ms (${this.maxRetries} retries exhausted)`,\r\n { url, timeoutMs: this.timeoutMs },\r\n );\r\n }\r\n\r\n // Network error\r\n if (attempt <= this.maxRetries) {\r\n const delay = this.calculateBackoff(attempt);\r\n log.warn(`Network error, retrying in ${delay}ms (${attempt}/${this.maxRetries})`, {\r\n url,\r\n error: fetchError instanceof Error ? fetchError.message : String(fetchError),\r\n });\r\n await this.sleep(delay);\r\n return this.executeWithRetry<T>(url, attempt + 1, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n throw new ApiRequestError(\r\n ErrorCode.API_REQUEST_FAILED,\r\n `Network error after ${this.maxRetries} retries`,\r\n {\r\n url,\r\n originalError: fetchError instanceof Error ? fetchError.message : String(fetchError),\r\n },\r\n );\r\n } finally {\r\n clearTimeout(timeoutId);\r\n signal?.removeEventListener('abort', onUserAbort);\r\n }\r\n\r\n // ── Handle HTTP Errors ──\r\n\r\n if (!response.ok) {\r\n return this.handleHttpError<T>(response, url, attempt, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n log.debug(`${method} ${url} -> ${response.status}`, { attempt });\r\n return response.json() as Promise<T>;\r\n }\r\n\r\n private async handleHttpError<T>(\r\n response: Response,\r\n url: string,\r\n attempt: number,\r\n rateLimitRetries: number,\r\n signal?: AbortSignal,\r\n method: 'GET' | 'POST' = 'GET',\r\n requestBody?: unknown,\r\n ): Promise<T> {\r\n const body = await response.text();\r\n\r\n // 429 Rate Limited: retry with separate counter to prevent infinite loops\r\n if (response.status === 429) {\r\n if (rateLimitRetries >= this.maxRateLimitRetries) {\r\n throw new ApiRequestError(\r\n ErrorCode.API_RATE_LIMITED,\r\n `Rate limit retries exhausted (${this.maxRateLimitRetries} consecutive 429 responses)`,\r\n { url, rateLimitRetries },\r\n );\r\n }\r\n\r\n const retryAfterHeader = response.headers.get('Retry-After');\r\n const retryAfterMs = retryAfterHeader\r\n ? parseInt(retryAfterHeader, 10) * 1000\r\n : this.calculateBackoff(rateLimitRetries + 1);\r\n\r\n log.warn(`Rate limited (HTTP 429). Waiting ${retryAfterMs}ms (${rateLimitRetries + 1}/${this.maxRateLimitRetries}).`, {\r\n url,\r\n retryAfterHeader,\r\n });\r\n\r\n await this.sleep(retryAfterMs);\r\n // Do NOT increment attempt for 429: these are not \"failures\", they are throttling\r\n return this.executeWithRetry<T>(url, attempt, rateLimitRetries + 1, signal, method, requestBody);\r\n }\r\n\r\n // 401 Unauthorized: clear token cache and retry once\r\n if (response.status === 401 && attempt === 1) {\r\n log.warn('HTTP 401 received, clearing token cache and retrying');\r\n this.cachedToken = null;\r\n return this.executeWithRetry<T>(url, attempt + 1, 0, signal, method, requestBody);\r\n }\r\n\r\n // 5xx Server Errors: retryable\r\n if (response.status >= 500 && attempt <= this.maxRetries) {\r\n const delay = this.calculateBackoff(attempt);\r\n log.warn(\r\n `Server error ${response.status}, retrying in ${delay}ms (${attempt}/${this.maxRetries})`,\r\n );\r\n await this.sleep(delay);\r\n return this.executeWithRetry<T>(url, attempt + 1, rateLimitRetries, signal, method, requestBody);\r\n }\r\n\r\n // Non-retryable error: throw\r\n const errorCode =\r\n response.status === 401\r\n ? ErrorCode.API_UNAUTHORIZED\r\n : response.status === 404\r\n ? ErrorCode.API_NOT_FOUND\r\n : ErrorCode.API_REQUEST_FAILED;\r\n\r\n throw new ApiRequestError(\r\n errorCode,\r\n `Dataverse API error: HTTP ${response.status} ${response.statusText}`,\r\n {\r\n url,\r\n statusCode: response.status,\r\n responseBody: body.substring(0, MAX_RESPONSE_BODY_LENGTH),\r\n },\r\n );\r\n }\r\n\r\n // ─── Helpers ─────────────────────────────────────────────────────────────\r\n\r\n private resolveUrl(path: string): string {\r\n return path.startsWith('http') ? path : `${this.apiUrl}${path}`;\r\n }\r\n\r\n private calculateBackoff(attempt: number): number {\r\n const exponential = this.retryBaseDelayMs * Math.pow(2, attempt - 1);\r\n const jitter = Math.random() * this.retryBaseDelayMs;\r\n return Math.min(exponential + jitter, MAX_BACKOFF_MS);\r\n }\r\n\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n }\r\n}\r\n","/**\r\n * @xrmforge/typegen - XML Parser Abstraction\r\n *\r\n * Interface for XML parsing, decoupled from any specific parser library.\r\n * Allows swapping the underlying parser (currently fast-xml-parser)\r\n * by implementing a single interface. (Goldene Regel 14)\r\n */\r\n\r\n// ─── Parser Interface ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Parsed XML element with attributes and children.\r\n */\r\nexport interface XmlElement {\r\n /** Element tag name */\r\n tag: string;\r\n /** Element attributes as key-value pairs */\r\n attributes: Record<string, string>;\r\n /** Child elements */\r\n children: XmlElement[];\r\n /** Text content (if any) */\r\n text?: string;\r\n}\r\n\r\n/**\r\n * Abstraction for XML parsing. Implementations must convert an XML string\r\n * into a tree of XmlElement objects.\r\n *\r\n * To swap the underlying parser library, implement this interface\r\n * and pass it to the FormXml parser. Only this file needs to change.\r\n */\r\nexport interface XmlParser {\r\n parse(xml: string): XmlElement;\r\n}\r\n\r\n// ─── fast-xml-parser Implementation ──────────────────────────────────────────\r\n\r\nimport { XMLParser } from 'fast-xml-parser';\r\n\r\n/**\r\n * XML parser implementation using fast-xml-parser.\r\n * Zero dependencies (fast-xml-parser itself has none), 26 KB minified.\r\n */\r\nexport class FastXmlParser implements XmlParser {\r\n private readonly parser: XMLParser;\r\n\r\n constructor() {\r\n this.parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: '@_',\r\n allowBooleanAttributes: true,\r\n preserveOrder: true,\r\n trimValues: true,\r\n });\r\n }\r\n\r\n parse(xml: string): XmlElement {\r\n const result = this.parser.parse(xml);\r\n return this.convertToXmlElement(result);\r\n }\r\n\r\n private convertToXmlElement(parsed: unknown): XmlElement {\r\n if (!Array.isArray(parsed) || parsed.length === 0) {\r\n return { tag: 'root', attributes: {}, children: [] };\r\n }\r\n\r\n // fast-xml-parser with preserveOrder returns an array of objects\r\n // Each object has one key (the tag name) and optionally :@_attrs\r\n const children = parsed.map((item: Record<string, unknown>) => this.convertNode(item));\r\n\r\n if (children.length === 1) {\r\n return children[0]!;\r\n }\r\n\r\n return { tag: 'root', attributes: {}, children };\r\n }\r\n\r\n private convertNode(node: Record<string, unknown>): XmlElement {\r\n const attrs: Record<string, string> = {};\r\n let tag = '';\r\n let children: XmlElement[] = [];\r\n let text: string | undefined;\r\n\r\n for (const key of Object.keys(node)) {\r\n if (key === ':@') {\r\n // Attributes object\r\n const attrObj = node[key] as Record<string, string>;\r\n for (const [attrKey, attrVal] of Object.entries(attrObj)) {\r\n // Remove the @_ prefix added by fast-xml-parser\r\n const cleanKey = attrKey.startsWith('@_') ? attrKey.slice(2) : attrKey;\r\n attrs[cleanKey] = String(attrVal);\r\n }\r\n } else if (key === '#text') {\r\n text = String(node[key]);\r\n } else {\r\n tag = key;\r\n const childContent = node[key];\r\n if (Array.isArray(childContent)) {\r\n children = childContent.map((child: Record<string, unknown>) => this.convertNode(child));\r\n }\r\n }\r\n }\r\n\r\n return { tag, attributes: attrs, children, text };\r\n }\r\n}\r\n\r\n// ─── Default Instance ────────────────────────────────────────────────────────\r\n\r\n/** Default parser instance. Use this unless you need a custom parser. */\r\nexport const defaultXmlParser: XmlParser = new FastXmlParser();\r\n","/**\r\n * @xrmforge/typegen - FormXml Parser\r\n *\r\n * Parses Dataverse FormXml into structured TypeScript objects.\r\n * Extracts tabs, sections, controls (data-bound + special) for generating\r\n * typed FormContext interfaces with compile-time field validation.\r\n *\r\n * Uses the XmlParser abstraction (Goldene Regel 14) instead of regex.\r\n */\r\n\r\nimport type { FormControl, FormSpecialControl, FormTab, FormSection, ParsedForm, SystemFormMetadata, SpecialControlType } from './types.js';\r\nimport type { XmlParser, XmlElement } from './xml-parser.js';\r\nimport { defaultXmlParser } from './xml-parser.js';\r\nimport { MetadataError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('form-parser');\r\n\r\n// ─── Known Control ClassIds ─────────────────────────────────────────────────\r\n// Extracted from real FormXml across 25 Markant entities (2026-03-30)\r\n\r\n/** Well-known classid GUIDs for special (non-data-bound) controls */\r\nconst SPECIAL_CONTROL_CLASSIDS: Record<string, SpecialControlType> = {\r\n // Subgrid (read-only list of related records)\r\n 'e7a81278-8635-4d9e-8d4d-59480b391c5b': 'subgrid',\r\n // Editable Grid (inline-editable list)\r\n '02d4264b-47e2-4b4c-aa95-f439f3f4d458': 'editablegrid',\r\n // Quick View Form (embedded read-only form of related record)\r\n '5c5600e0-1d6e-4205-a272-be80da87fd42': 'quickview',\r\n // Web Resource (custom HTML/JS content)\r\n '9fdf5f91-88b1-47f4-ad53-c11efc01a01d': 'webresource',\r\n // Bing Map\r\n '62b0df79-0464-470f-8af7-4483cfea0c7d': 'map',\r\n // Notes/Timeline\r\n '06375649-c143-495e-a496-c962e5b4488e': 'notes',\r\n};\r\n\r\n/**\r\n * Parse a SystemFormMetadata response into a structured ParsedForm.\r\n *\r\n * @param form - The system form metadata from the API\r\n * @param parser - XML parser to use (defaults to fast-xml-parser)\r\n * @throws {MetadataError} if the formxml cannot be parsed\r\n */\r\nexport function parseForm(form: SystemFormMetadata, parser: XmlParser = defaultXmlParser): ParsedForm {\r\n const tabs = parseTabs(form.formxml, form.name, parser);\r\n const allControls = tabs.flatMap((tab) =>\r\n tab.sections.flatMap((section) => section.controls),\r\n );\r\n const allSpecialControls = tabs.flatMap((tab) =>\r\n tab.sections.flatMap((section) => section.specialControls),\r\n );\r\n\r\n return {\r\n name: form.name,\r\n formId: form.formid,\r\n isDefault: form.isdefault,\r\n tabs,\r\n allControls,\r\n allSpecialControls,\r\n };\r\n}\r\n\r\n/**\r\n * Extract all data-bound control field names from FormXml (flattened).\r\n * Simpler alternative to full parsing when only the field list is needed.\r\n */\r\nexport function extractControlFields(formxml: string, parser: XmlParser = defaultXmlParser): string[] {\r\n if (!formxml || formxml.trim().length === 0) {\r\n return [];\r\n }\r\n\r\n try {\r\n const root = parser.parse(formxml);\r\n const fields: string[] = [];\r\n collectDatafieldNames(root, fields);\r\n return fields;\r\n } catch {\r\n log.warn('Failed to parse formxml for field extraction, returning empty list');\r\n return [];\r\n }\r\n}\r\n\r\n// ─── Internal Parsing ────────────────────────────────────────────────────────\r\n\r\nfunction parseTabs(formxml: string, formName: string, parser: XmlParser): FormTab[] {\r\n if (!formxml || formxml.trim().length === 0) {\r\n log.warn(`Empty formxml for form \"${formName}\"`);\r\n return [];\r\n }\r\n\r\n let root: XmlElement;\r\n try {\r\n root = parser.parse(formxml);\r\n } catch (error: unknown) {\r\n throw new MetadataError(\r\n ErrorCode.META_FORM_PARSE_FAILED,\r\n `Failed to parse FormXml for form \"${formName}\"`,\r\n {\r\n formName,\r\n originalError: error instanceof Error ? error.message : String(error),\r\n },\r\n );\r\n }\r\n\r\n const tabs: FormTab[] = [];\r\n const tabElements = findElements(root, 'tab');\r\n\r\n for (const tabEl of tabElements) {\r\n const tabName = tabEl.attributes['name'] ?? '';\r\n const tabVisible = tabEl.attributes['visible'] !== 'false';\r\n\r\n // Extract tab label from <labels><label description=\"...\" /></labels>\r\n const tabLabel = extractLabel(tabEl);\r\n\r\n const sections = parseSections(tabEl);\r\n tabs.push({ name: tabName, label: tabLabel, visible: tabVisible, sections });\r\n }\r\n\r\n return tabs;\r\n}\r\n\r\nfunction parseSections(tabElement: XmlElement): FormSection[] {\r\n const sections: FormSection[] = [];\r\n const sectionElements = findElements(tabElement, 'section');\r\n\r\n for (const sectionEl of sectionElements) {\r\n const sectionName = sectionEl.attributes['name'] ?? '';\r\n const sectionVisible = sectionEl.attributes['visible'] !== 'false';\r\n const sectionLabel = extractLabel(sectionEl);\r\n\r\n const controls = parseDataControls(sectionEl);\r\n const specialControls = parseSpecialControls(sectionEl);\r\n\r\n sections.push({\r\n name: sectionName,\r\n label: sectionLabel,\r\n visible: sectionVisible,\r\n controls,\r\n specialControls,\r\n });\r\n }\r\n\r\n return sections;\r\n}\r\n\r\n/** Parse data-bound controls (controls with a datafieldname attribute) */\r\nfunction parseDataControls(sectionElement: XmlElement): FormControl[] {\r\n const controls: FormControl[] = [];\r\n const controlElements = findElements(sectionElement, 'control');\r\n\r\n for (const controlEl of controlElements) {\r\n const datafieldname = controlEl.attributes['datafieldname'];\r\n if (!datafieldname) continue; // Special controls are handled separately\r\n\r\n controls.push({\r\n id: controlEl.attributes['id'] ?? '',\r\n datafieldname,\r\n classid: controlEl.attributes['classid'] ?? '',\r\n });\r\n }\r\n\r\n return controls;\r\n}\r\n\r\n/** Parse special controls (subgrids, quick views, web resources, etc.) */\r\nfunction parseSpecialControls(sectionElement: XmlElement): FormSpecialControl[] {\r\n const controls: FormSpecialControl[] = [];\r\n const controlElements = findElements(sectionElement, 'control');\r\n\r\n for (const controlEl of controlElements) {\r\n // Only process controls WITHOUT datafieldname (these are special controls)\r\n if (controlEl.attributes['datafieldname']) continue;\r\n\r\n const classid = (controlEl.attributes['classid'] ?? '').replace(/[{}]/g, '').toLowerCase();\r\n const controlType = SPECIAL_CONTROL_CLASSIDS[classid];\r\n\r\n // Skip controls we don't recognize (standard field controls without datafieldname are noise)\r\n if (!controlType) continue;\r\n\r\n const special: FormSpecialControl = {\r\n id: controlEl.attributes['id'] ?? '',\r\n classid,\r\n controlType,\r\n };\r\n\r\n // Extract parameters for subgrids and quick views\r\n if (controlType === 'subgrid' || controlType === 'editablegrid') {\r\n special.targetEntityType = extractParameter(controlEl, 'TargetEntityType');\r\n special.relationshipName = extractParameter(controlEl, 'RelationshipName');\r\n }\r\n\r\n if (controlType === 'webresource') {\r\n special.webResourceName = extractParameter(controlEl, 'Url');\r\n }\r\n\r\n controls.push(special);\r\n }\r\n\r\n return controls;\r\n}\r\n\r\n// ─── XML Tree Navigation ─────────────────────────────────────────────────────\r\n\r\n/** Recursively find all elements with a given tag name */\r\nfunction findElements(element: XmlElement, tagName: string): XmlElement[] {\r\n const results: XmlElement[] = [];\r\n\r\n if (element.tag === tagName) {\r\n results.push(element);\r\n }\r\n\r\n for (const child of element.children) {\r\n results.push(...findElements(child, tagName));\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/** Recursively collect all datafieldname attributes from control elements */\r\nfunction collectDatafieldNames(element: XmlElement, fields: string[]): void {\r\n if (element.tag === 'control') {\r\n const name = element.attributes['datafieldname'];\r\n if (name && !fields.includes(name)) {\r\n fields.push(name);\r\n }\r\n }\r\n\r\n for (const child of element.children) {\r\n collectDatafieldNames(child, fields);\r\n }\r\n}\r\n\r\n/** Extract label text from a <labels><label description=\"...\" /></labels> child */\r\nfunction extractLabel(element: XmlElement): string | undefined {\r\n const labelsEl = element.children.find((c) => c.tag === 'labels');\r\n if (!labelsEl) return undefined;\r\n\r\n const labelEl = labelsEl.children.find((c) => c.tag === 'label');\r\n if (!labelEl) return undefined;\r\n\r\n return labelEl.attributes['description'] || undefined;\r\n}\r\n\r\n/** Extract a parameter value from <parameters><ParameterName>Value</ParameterName></parameters> */\r\nfunction extractParameter(controlElement: XmlElement, paramName: string): string | undefined {\r\n const paramsEl = controlElement.children.find((c) => c.tag === 'parameters');\r\n if (!paramsEl) return undefined;\r\n\r\n const paramEl = paramsEl.children.find((c) => c.tag === paramName);\r\n if (!paramEl) return undefined;\r\n\r\n return paramEl.text || undefined;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Metadata Client\r\n *\r\n * High-level client for querying Dataverse Metadata API endpoints.\r\n * Built on top of DataverseHttpClient for resilient HTTP communication.\r\n *\r\n * Provides methods for:\r\n * - Entity metadata with attributes\r\n * - Typed attribute queries (Picklist, Lookup, Status/State)\r\n * - Form metadata (SystemForms + FormXml parsing)\r\n * - Global OptionSet definitions\r\n * - Solution-based entity filtering\r\n * - Relationship metadata (1:N, N:N)\r\n */\r\n\r\nimport { DataverseHttpClient } from '../http/client.js';\r\nimport { MetadataError, ErrorCode } from '../errors.js';\r\nimport { createLogger } from '../logger.js';\r\nimport { parseForm } from './form-parser.js';\r\nimport type {\r\n EntityMetadata,\r\n PicklistAttributeMetadata,\r\n LookupAttributeMetadata,\r\n StatusAttributeMetadata,\r\n StateAttributeMetadata,\r\n OptionSetMetadata,\r\n SystemFormMetadata,\r\n ParsedForm,\r\n OneToManyRelationshipMetadata,\r\n ManyToManyRelationshipMetadata,\r\n SolutionComponent,\r\n EntityTypeInfo,\r\n} from './types.js';\r\nimport type {\r\n CustomApiTypeInfo,\r\n CustomApiMetadata,\r\n CustomApiRequestParameter,\r\n CustomApiResponseProperty,\r\n} from './custom-api-types.js';\r\n\r\nconst log = createLogger('metadata');\r\n\r\n// ─── OData Response Wrappers ─────────────────────────────────────────────────\r\n\r\ninterface ODataCollection<T> {\r\n value: T[];\r\n}\r\n\r\ninterface SolutionRecord {\r\n solutionid: string;\r\n uniquename: string;\r\n friendlyname: string;\r\n}\r\n\r\n// ─── Dataverse Constants ─────────────────────────────────────────────────────\r\n\r\n/** Dataverse SystemForm type code for Main forms */\r\nconst FORM_TYPE_MAIN = 2;\r\n\r\n/** Dataverse SolutionComponent type code for Entity */\r\nconst COMPONENT_TYPE_ENTITY = 1;\r\n\r\n// ─── Select Constants ────────────────────────────────────────────────────────\r\n\r\nconst ENTITY_SELECT = 'LogicalName,SchemaName,EntitySetName,DisplayName,PrimaryIdAttribute,PrimaryNameAttribute,OwnershipType,IsCustomEntity,LogicalCollectionName,MetadataId';\r\nconst ATTRIBUTE_SELECT = 'LogicalName,SchemaName,AttributeType,AttributeTypeName,DisplayName,IsPrimaryId,IsPrimaryName,RequiredLevel,IsValidForRead,IsValidForCreate,IsValidForUpdate,MetadataId';\r\nconst FORM_SELECT = 'name,formid,formxml,description,isdefault';\r\n\r\n// ─── Client ──────────────────────────────────────────────────────────────────\r\n\r\nexport class MetadataClient {\r\n private readonly http: DataverseHttpClient;\r\n\r\n constructor(httpClient: DataverseHttpClient) {\r\n this.http = httpClient;\r\n }\r\n\r\n // ─── Entity Metadata ───────────────────────────────────────────────────\r\n\r\n /**\r\n * Get metadata for a single entity by LogicalName, including all attributes.\r\n *\r\n * @throws {MetadataError} if the entity is not found\r\n */\r\n async getEntityWithAttributes(logicalName: string): Promise<EntityMetadata> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.info(`Fetching entity metadata: ${safeName}`);\r\n\r\n const entity = await this.http.get<EntityMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')?$select=${ENTITY_SELECT}&$expand=Attributes($select=${ATTRIBUTE_SELECT})`,\r\n );\r\n\r\n log.info(`Entity \"${safeName}\": ${entity.Attributes?.length ?? 0} attributes`);\r\n return entity;\r\n }\r\n\r\n /**\r\n * List all entities (without attributes) for discovery.\r\n * Use `$filter` parameter to narrow results.\r\n */\r\n async listEntities(filter?: string): Promise<EntityMetadata[]> {\r\n let path = `/EntityDefinitions?$select=${ENTITY_SELECT}`;\r\n if (filter) {\r\n path += `&$filter=${filter}`;\r\n }\r\n\r\n log.info('Listing entities');\r\n return this.http.getAll<EntityMetadata>(path);\r\n }\r\n\r\n // ─── Typed Attribute Queries ───────────────────────────────────────────\r\n\r\n /**\r\n * Get all Picklist attributes with their OptionSets for an entity.\r\n * Includes both local and global OptionSets.\r\n */\r\n async getPicklistAttributes(logicalName: string): Promise<PicklistAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching Picklist attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<PicklistAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata?$select=LogicalName,SchemaName,MetadataId&$expand=OptionSet($select=Options,Name,IsGlobal,MetadataId),GlobalOptionSet($select=Options,Name,MetadataId)`,\r\n );\r\n }\r\n\r\n /**\r\n * Get all Lookup attributes with their target entity names.\r\n */\r\n async getLookupAttributes(logicalName: string): Promise<LookupAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching Lookup attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<LookupAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.LookupAttributeMetadata?$select=LogicalName,SchemaName,Targets,MetadataId`,\r\n );\r\n }\r\n\r\n /**\r\n * Get Status attributes (statuscode) with their OptionSets.\r\n */\r\n async getStatusAttributes(logicalName: string): Promise<StatusAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching Status attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<StatusAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.StatusAttributeMetadata?$select=LogicalName,SchemaName,MetadataId&$expand=OptionSet($select=Options)`,\r\n );\r\n }\r\n\r\n /**\r\n * Get State attributes (statecode) with their OptionSets.\r\n */\r\n async getStateAttributes(logicalName: string): Promise<StateAttributeMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching State attributes for: ${safeName}`);\r\n\r\n return this.http.getAll<StateAttributeMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/Attributes/Microsoft.Dynamics.CRM.StateAttributeMetadata?$select=LogicalName,SchemaName,MetadataId&$expand=OptionSet($select=Options)`,\r\n );\r\n }\r\n\r\n // ─── Form Metadata ────────────────────────────────────────────────────\r\n\r\n /**\r\n * Get and parse Main forms (type=2) for an entity.\r\n * Returns parsed form structures with tabs, sections, and controls.\r\n */\r\n async getMainForms(logicalName: string): Promise<ParsedForm[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.info(`Fetching Main forms for: ${safeName}`);\r\n\r\n const forms = await this.http.getAll<SystemFormMetadata>(\r\n `/systemforms?$filter=objecttypecode eq '${safeName}' and type eq ${FORM_TYPE_MAIN}&$select=${FORM_SELECT}`,\r\n );\r\n\r\n log.info(`Found ${forms.length} Main form(s) for \"${safeName}\"`);\r\n\r\n return forms.map((form) => {\r\n try {\r\n return parseForm(form);\r\n } catch (error: unknown) {\r\n log.warn(`Failed to parse form \"${form.name}\" (${form.formid}), skipping`, {\r\n formName: form.name,\r\n formId: form.formid,\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n // Return a minimal parsed form with no controls rather than failing entirely\r\n return {\r\n name: form.name,\r\n formId: form.formid,\r\n isDefault: form.isdefault,\r\n tabs: [],\r\n allControls: [],\r\n allSpecialControls: [],\r\n };\r\n }\r\n });\r\n }\r\n\r\n // ─── Global OptionSets ─────────────────────────────────────────────────\r\n\r\n /**\r\n * Get a global OptionSet by its exact name.\r\n */\r\n async getGlobalOptionSet(name: string): Promise<OptionSetMetadata> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(name);\r\n\r\n log.debug(`Fetching GlobalOptionSet: ${safeName}`);\r\n\r\n return this.http.get<OptionSetMetadata>(\r\n `/GlobalOptionSetDefinitions(Name='${safeName}')`,\r\n );\r\n }\r\n\r\n /**\r\n * List all global OptionSets (names and types only).\r\n */\r\n async listGlobalOptionSets(): Promise<OptionSetMetadata[]> {\r\n log.debug('Listing all GlobalOptionSets');\r\n\r\n return this.http.getAll<OptionSetMetadata>(\r\n `/GlobalOptionSetDefinitions?$select=Name,DisplayName,OptionSetType,IsGlobal,MetadataId`,\r\n );\r\n }\r\n\r\n // ─── Relationships ─────────────────────────────────────────────────────\r\n\r\n /**\r\n * Get all 1:N relationships where this entity is the referenced (parent) entity.\r\n */\r\n async getOneToManyRelationships(logicalName: string): Promise<OneToManyRelationshipMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching 1:N relationships for: ${safeName}`);\r\n\r\n return this.http.getAll<OneToManyRelationshipMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/OneToManyRelationships?$select=SchemaName,ReferencingEntity,ReferencingAttribute,ReferencedEntity,ReferencedAttribute,MetadataId`,\r\n );\r\n }\r\n\r\n /**\r\n * Get all N:N relationships for an entity.\r\n */\r\n async getManyToManyRelationships(logicalName: string): Promise<ManyToManyRelationshipMetadata[]> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.debug(`Fetching N:N relationships for: ${safeName}`);\r\n\r\n return this.http.getAll<ManyToManyRelationshipMetadata>(\r\n `/EntityDefinitions(LogicalName='${safeName}')/ManyToManyRelationships?$select=SchemaName,Entity1LogicalName,Entity2LogicalName,IntersectEntityName,MetadataId`,\r\n );\r\n }\r\n\r\n // ─── Solution Filter ───────────────────────────────────────────────────\r\n\r\n /**\r\n * Get all entity LogicalNames that belong to a specific solution.\r\n * Resolves SolutionComponent MetadataIds to EntityDefinition LogicalNames.\r\n *\r\n * @param solutionUniqueName - The unique name of the solution\r\n * @returns Array of entity LogicalNames (e.g. [\"account\", \"contact\"])\r\n */\r\n async getEntityNamesForSolution(solutionUniqueName: string): Promise<string[]> {\r\n const safeName = DataverseHttpClient.escapeODataString(solutionUniqueName);\r\n\r\n log.info(`Fetching solution: ${solutionUniqueName}`);\r\n\r\n // Step 1: Get solution ID\r\n const solutions = await this.http.get<ODataCollection<SolutionRecord>>(\r\n `/solutions?$filter=uniquename eq '${safeName}'&$select=solutionid,uniquename,friendlyname`,\r\n );\r\n\r\n if (solutions.value.length === 0) {\r\n throw new MetadataError(\r\n ErrorCode.META_SOLUTION_NOT_FOUND,\r\n `Solution \"${solutionUniqueName}\" not found`,\r\n { solutionUniqueName },\r\n );\r\n }\r\n\r\n const solutionId = solutions.value[0]!.solutionid;\r\n const solutionName = solutions.value[0]!.friendlyname;\r\n\r\n log.info(`Solution \"${solutionName}\" (${solutionId})`);\r\n\r\n // Step 2: Get entity components (componenttype=1)\r\n const components = await this.http.getAll<SolutionComponent>(\r\n `/solutioncomponents?$filter=_solutionid_value eq ${DataverseHttpClient.sanitizeGuid(solutionId)} and componenttype eq ${COMPONENT_TYPE_ENTITY}&$select=objectid,componenttype`,\r\n );\r\n\r\n log.info(`Solution \"${solutionName}\" contains ${components.length} entity components`);\r\n\r\n if (components.length === 0) return [];\r\n\r\n // Step 3: Resolve MetadataIds to LogicalNames via EntityDefinitions\r\n // The objectid in solutioncomponents is the MetadataId of the EntityDefinition,\r\n // NOT the LogicalName. We need an additional query to resolve.\r\n const metadataIds = components.map((c) => c.objectid);\r\n const filterClauses = metadataIds.map((id) => `MetadataId eq ${DataverseHttpClient.sanitizeGuid(id)}`);\r\n\r\n // Batch in groups of 15 to avoid excessively long filter strings\r\n const BATCH_SIZE = 15;\r\n const logicalNames: string[] = [];\r\n\r\n for (let i = 0; i < filterClauses.length; i += BATCH_SIZE) {\r\n const batch = filterClauses.slice(i, i + BATCH_SIZE);\r\n const filter = batch.join(' or ');\r\n const entities = await this.http.getAll<{ LogicalName: string }>(\r\n `/EntityDefinitions?$filter=${filter}&$select=LogicalName`,\r\n );\r\n for (const e of entities) {\r\n logicalNames.push(e.LogicalName);\r\n }\r\n }\r\n\r\n log.info(`Resolved ${logicalNames.length} entity logical names from solution \"${solutionName}\"`);\r\n\r\n return logicalNames;\r\n }\r\n\r\n /**\r\n * Get all entity LogicalNames from multiple solutions, merged and deduplicated.\r\n *\r\n * @param solutionUniqueNames - Array of solution unique names\r\n * @returns Deduplicated array of entity LogicalNames\r\n */\r\n async getEntityNamesForSolutions(solutionUniqueNames: string[]): Promise<string[]> {\r\n const allNames = new Set<string>();\r\n\r\n for (const name of solutionUniqueNames) {\r\n const names = await this.getEntityNamesForSolution(name);\r\n for (const n of names) {\r\n allNames.add(n);\r\n }\r\n }\r\n\r\n const result = [...allNames].sort();\r\n log.info(`${result.length} unique entities from ${solutionUniqueNames.length} solutions`);\r\n return result;\r\n }\r\n\r\n // ─── Aggregated Metadata ───────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch complete metadata for a single entity: all attributes (typed),\r\n * forms, and relationships. This is the primary method for type generation.\r\n *\r\n * Makes 7 parallel API calls per entity for optimal performance.\r\n */\r\n async getEntityTypeInfo(logicalName: string): Promise<EntityTypeInfo> {\r\n const safeName = DataverseHttpClient.sanitizeIdentifier(logicalName);\r\n\r\n log.info(`Fetching complete type info for: ${safeName}`);\r\n\r\n const [entity, picklistAttributes, lookupAttributes, statusAttributes, stateAttributes, forms, relationships] =\r\n await Promise.all([\r\n this.getEntityWithAttributes(safeName),\r\n this.getPicklistAttributes(safeName),\r\n this.getLookupAttributes(safeName),\r\n this.getStatusAttributes(safeName),\r\n this.getStateAttributes(safeName),\r\n this.getMainForms(safeName),\r\n this.getRelationships(safeName),\r\n ]);\r\n\r\n const result: EntityTypeInfo = {\r\n entity,\r\n attributes: entity.Attributes ?? [],\r\n picklistAttributes,\r\n lookupAttributes,\r\n statusAttributes,\r\n stateAttributes,\r\n forms,\r\n oneToManyRelationships: relationships.oneToMany,\r\n manyToManyRelationships: relationships.manyToMany,\r\n };\r\n\r\n log.info(\r\n `Type info for \"${safeName}\": ${result.attributes.length} attrs, ` +\r\n `${picklistAttributes.length} picklists, ${lookupAttributes.length} lookups, ` +\r\n `${stateAttributes.length} state, ${forms.length} forms, ` +\r\n `${relationships.oneToMany.length} 1:N, ${relationships.manyToMany.length} N:N`,\r\n );\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Fetch complete metadata for multiple entities in parallel.\r\n * Respects the HTTP client's concurrency limit automatically.\r\n */\r\n async getMultipleEntityTypeInfos(logicalNames: string[]): Promise<EntityTypeInfo[]> {\r\n log.info(`Fetching type info for ${logicalNames.length} entities`);\r\n return Promise.all(logicalNames.map((name) => this.getEntityTypeInfo(name)));\r\n }\r\n\r\n // ─── Custom API Metadata ───────────────────────────────────────────────\r\n\r\n /**\r\n * Fetch all Custom APIs with their request parameters and response properties.\r\n *\r\n * Queries the customapi, customapirequestparameter, and customapiresponseproperty\r\n * tables and joins them into CustomApiTypeInfo objects.\r\n *\r\n * @param solutionFilter - Optional: filter by solution unique name\r\n * @returns Array of complete Custom API definitions\r\n */\r\n async getCustomApis(solutionFilter?: string): Promise<CustomApiTypeInfo[]> {\r\n log.info('Fetching Custom APIs...');\r\n\r\n // 1. Load all Custom APIs\r\n let apiUrl = '/customapis?$select=uniquename,bindingtype,isfunction,boundentitylogicalname,displayname,description';\r\n if (solutionFilter) {\r\n const safeSolution = DataverseHttpClient.sanitizeIdentifier(solutionFilter);\r\n apiUrl += `&$filter=solutionid/uniquename eq '${safeSolution}'`;\r\n }\r\n\r\n const apis = await this.http.get<ODataCollection<CustomApiMetadata & { customapiid: string }>>(apiUrl);\r\n log.info(`Found ${apis.value.length} Custom APIs`);\r\n\r\n if (apis.value.length === 0) return [];\r\n\r\n // 2. Load all request parameters\r\n const params = await this.http.get<ODataCollection<CustomApiRequestParameter & { _customapiid_value: string }>>(\r\n '/customapirequestparameters?$select=uniquename,type,isoptional,logicalentityname,description,_customapiid_value',\r\n );\r\n\r\n // 3. Load all response properties\r\n const props = await this.http.get<ODataCollection<CustomApiResponseProperty & { _customapiid_value: string }>>(\r\n '/customapiresponseproperties?$select=uniquename,type,logicalentityname,description,_customapiid_value',\r\n );\r\n\r\n // 4. Group parameters and properties by Custom API ID\r\n const paramsByApi = new Map<string, CustomApiRequestParameter[]>();\r\n for (const p of params.value) {\r\n const apiId = p._customapiid_value;\r\n if (!paramsByApi.has(apiId)) paramsByApi.set(apiId, []);\r\n paramsByApi.get(apiId)!.push({\r\n uniquename: p.uniquename,\r\n type: p.type,\r\n isoptional: p.isoptional,\r\n logicalentityname: p.logicalentityname,\r\n description: p.description,\r\n });\r\n }\r\n\r\n const propsByApi = new Map<string, CustomApiResponseProperty[]>();\r\n for (const p of props.value) {\r\n const apiId = p._customapiid_value;\r\n if (!propsByApi.has(apiId)) propsByApi.set(apiId, []);\r\n propsByApi.get(apiId)!.push({\r\n uniquename: p.uniquename,\r\n type: p.type,\r\n logicalentityname: p.logicalentityname,\r\n description: p.description,\r\n });\r\n }\r\n\r\n // 5. Build CustomApiTypeInfo objects\r\n const result: CustomApiTypeInfo[] = [];\r\n for (const api of apis.value) {\r\n result.push({\r\n api: {\r\n uniquename: api.uniquename,\r\n bindingtype: api.bindingtype,\r\n isfunction: api.isfunction,\r\n boundentitylogicalname: api.boundentitylogicalname,\r\n displayname: api.displayname,\r\n description: api.description,\r\n },\r\n requestParameters: paramsByApi.get(api.customapiid) ?? [],\r\n responseProperties: propsByApi.get(api.customapiid) ?? [],\r\n });\r\n }\r\n\r\n log.info(`Loaded ${result.length} Custom APIs with parameters and response properties`);\r\n return result;\r\n }\r\n\r\n // ─── Internal Helpers ──────────────────────────────────────────────────\r\n\r\n private async getRelationships(logicalName: string): Promise<{\r\n oneToMany: OneToManyRelationshipMetadata[];\r\n manyToMany: ManyToManyRelationshipMetadata[];\r\n }> {\r\n const [oneToMany, manyToMany] = await Promise.all([\r\n this.getOneToManyRelationships(logicalName),\r\n this.getManyToManyRelationships(logicalName),\r\n ]);\r\n return { oneToMany, manyToMany };\r\n }\r\n}\r\n","/**\r\n * @xrmforge/typegen - Metadata Cache\r\n *\r\n * File-system based metadata cache using Dataverse's RetrieveMetadataChanges\r\n * ServerVersionStamp for efficient delta detection.\r\n *\r\n * On first run: full metadata retrieval, saved to .xrmforge/cache/metadata.json\r\n * On subsequent runs: delta query with stored VersionStamp, only changed entities refreshed\r\n * On expired stamp (90-day window or system maintenance): automatic full reload\r\n *\r\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/cache-schema-data\r\n */\r\n\r\nimport { promises as fs } from 'node:fs';\r\nimport * as path from 'node:path';\r\nimport { createLogger } from '../logger.js';\r\nimport type { EntityTypeInfo } from './types.js';\r\n\r\nconst log = createLogger('cache');\r\n\r\n// ─── Cache Structure ─────────────────────────────────────────────────────────\r\n\r\ninterface CacheManifest {\r\n /** XrmForge version that created this cache */\r\n version: string;\r\n /** Dataverse environment URL */\r\n environmentUrl: string;\r\n /** ServerVersionStamp from last RetrieveMetadataChanges call */\r\n serverVersionStamp: string | null;\r\n /** ISO timestamp of last full or delta refresh */\r\n lastRefreshed: string;\r\n /** Entity logical names in this cache */\r\n entities: string[];\r\n}\r\n\r\ninterface CacheData {\r\n manifest: CacheManifest;\r\n entityTypeInfos: Record<string, EntityTypeInfo>;\r\n}\r\n\r\n// ─── Constants ───────────────────────────────────────────────────────────────\r\n\r\nconst CACHE_DIR = '.xrmforge/cache';\r\nconst CACHE_FILE = 'metadata.json';\r\nconst CACHE_VERSION = '1';\r\n\r\n// ─── Cache Manager ───────────────────────────────────────────────────────────\r\n\r\nexport class MetadataCache {\r\n private readonly cacheDir: string;\r\n private readonly cacheFilePath: string;\r\n\r\n /**\r\n * @param cacheDir - Directory where cache files are stored.\r\n * Can be an absolute path or relative to cwd.\r\n * Defaults to \".xrmforge/cache\" when constructed without argument.\r\n */\r\n constructor(cacheDir: string = CACHE_DIR) {\r\n this.cacheDir = path.resolve(cacheDir);\r\n this.cacheFilePath = path.join(this.cacheDir, CACHE_FILE);\r\n }\r\n\r\n /**\r\n * Load cached metadata from disk.\r\n * Returns null if no cache exists, cache is for a different environment,\r\n * or cache format is incompatible.\r\n */\r\n async load(environmentUrl: string): Promise<CacheData | null> {\r\n try {\r\n const raw = await fs.readFile(this.cacheFilePath, 'utf-8');\r\n const data = JSON.parse(raw) as CacheData;\r\n\r\n // Validate cache compatibility\r\n if (data.manifest.version !== CACHE_VERSION) {\r\n log.info('Cache version mismatch, will do full refresh');\r\n return null;\r\n }\r\n\r\n if (data.manifest.environmentUrl !== environmentUrl) {\r\n log.info('Cache is for a different environment, will do full refresh', {\r\n cached: data.manifest.environmentUrl,\r\n current: environmentUrl,\r\n });\r\n return null;\r\n }\r\n\r\n log.info(`Loaded metadata cache: ${data.manifest.entities.length} entities, ` +\r\n `last refreshed ${data.manifest.lastRefreshed}`);\r\n\r\n return data;\r\n } catch (error: unknown) {\r\n if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n log.info('No metadata cache found, will do full refresh');\r\n } else {\r\n log.warn('Failed to read metadata cache, will do full refresh', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Save metadata to the file-system cache.\r\n */\r\n async save(\r\n environmentUrl: string,\r\n entityTypeInfos: Record<string, EntityTypeInfo>,\r\n serverVersionStamp: string | null,\r\n ): Promise<void> {\r\n const data: CacheData = {\r\n manifest: {\r\n version: CACHE_VERSION,\r\n environmentUrl,\r\n serverVersionStamp,\r\n lastRefreshed: new Date().toISOString(),\r\n entities: Object.keys(entityTypeInfos).sort(),\r\n },\r\n entityTypeInfos,\r\n };\r\n\r\n await fs.mkdir(this.cacheDir, { recursive: true });\r\n\r\n // Atomic write: write to temp file, then rename (prevents corrupt cache on crash)\r\n const tmpPath = this.cacheFilePath + '.tmp';\r\n await fs.writeFile(tmpPath, JSON.stringify(data, null, 2), 'utf-8');\r\n await fs.rename(tmpPath, this.cacheFilePath);\r\n\r\n log.info(`Saved metadata cache: ${data.manifest.entities.length} entities`);\r\n }\r\n\r\n /**\r\n * Get the stored ServerVersionStamp for delta queries.\r\n * Returns null if no cache exists.\r\n */\r\n async getVersionStamp(environmentUrl: string): Promise<string | null> {\r\n const cache = await this.load(environmentUrl);\r\n return cache?.manifest.serverVersionStamp ?? null;\r\n }\r\n\r\n /**\r\n * Update specific entities in the cache (delta update).\r\n * Merges new/changed entities into the existing cache.\r\n */\r\n async updateEntities(\r\n environmentUrl: string,\r\n updatedEntities: Record<string, EntityTypeInfo>,\r\n newVersionStamp: string | null,\r\n ): Promise<void> {\r\n const existing = await this.load(environmentUrl);\r\n const merged = existing?.entityTypeInfos ?? {};\r\n\r\n for (const [name, info] of Object.entries(updatedEntities)) {\r\n merged[name] = info;\r\n }\r\n\r\n await this.save(environmentUrl, merged, newVersionStamp);\r\n\r\n log.info(`Delta cache update: ${Object.keys(updatedEntities).length} entities updated`);\r\n }\r\n\r\n /**\r\n * Remove specific entities from the cache (for deleted entities).\r\n */\r\n async removeEntities(\r\n environmentUrl: string,\r\n deletedEntityNames: string[],\r\n newVersionStamp: string | null,\r\n ): Promise<void> {\r\n const existing = await this.load(environmentUrl);\r\n if (!existing) return;\r\n\r\n const merged = existing.entityTypeInfos;\r\n for (const name of deletedEntityNames) {\r\n delete merged[name];\r\n }\r\n\r\n await this.save(environmentUrl, merged, newVersionStamp);\r\n\r\n log.info(`Removed ${deletedEntityNames.length} entities from cache`);\r\n }\r\n\r\n /**\r\n * Delete the entire cache.\r\n */\r\n async clear(): Promise<void> {\r\n try {\r\n await fs.unlink(this.cacheFilePath);\r\n log.info('Metadata cache cleared');\r\n } catch (error: unknown) {\r\n if (!(error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT')) {\r\n throw error;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Check if a cache file exists.\r\n */\r\n async exists(): Promise<boolean> {\r\n try {\r\n await fs.access(this.cacheFilePath);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n}\r\n","/**\n * @xrmforge/typegen - Metadata Change Detector\n *\n * Uses the Dataverse RetrieveMetadataChanges action to determine\n * which entities have changed since the last generation run.\n * This enables incremental type generation (only re-fetch changed entities).\n *\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/webapi/samples/retrievemetadatachanges\n */\n\nimport type { DataverseHttpClient } from '../http/client.js';\nimport { MetadataError, ErrorCode } from '../errors.js';\nimport { createLogger } from '../logger.js';\n\nconst log = createLogger('change-detector');\n\n/** Result of a change detection query */\nexport interface ChangeDetectionResult {\n /** Entity logical names that have changed (new or modified) */\n changedEntityNames: string[];\n /** Entity logical names that have been deleted */\n deletedEntityNames: string[];\n /** New server version stamp (store this for the next run) */\n newVersionStamp: string;\n}\n\n/** Dataverse error code for expired version stamps (90-day limit) */\nconst EXPIRED_VERSION_STAMP_ERROR = '0x80044352';\n\n/**\n * Detects metadata changes in Dataverse using RetrieveMetadataChanges.\n *\n * Usage:\n * ```typescript\n * const detector = new ChangeDetector(httpClient);\n * const result = await detector.detectChanges(cachedVersionStamp);\n * // result.changedEntityNames = ['account', 'contact']\n * // result.newVersionStamp = 'new-stamp-to-cache'\n * ```\n */\nexport class ChangeDetector {\n constructor(private readonly http: DataverseHttpClient) {}\n\n /**\n * Detect which entities have changed since the given version stamp.\n *\n * @param clientVersionStamp - The ServerVersionStamp from the last run (from cache)\n * @returns Changed entity names, deleted entity names, and new version stamp\n * @throws {MetadataError} with META_VERSION_STAMP_EXPIRED if stamp is too old (>90 days)\n */\n async detectChanges(clientVersionStamp: string): Promise<ChangeDetectionResult> {\n log.info('Detecting metadata changes since last run');\n\n const query = {\n Criteria: {\n FilterOperator: 'And',\n Conditions: [],\n },\n Properties: {\n AllProperties: false,\n PropertyNames: ['LogicalName'],\n },\n };\n\n // RetrieveMetadataChanges is a Function (GET), not an Action (POST).\n // Parameters are passed as URL parameters using parameter aliases.\n const queryJson = encodeURIComponent(JSON.stringify(query));\n const deletedFilter = `Microsoft.Dynamics.CRM.DeletedMetadataFilters'Default'`;\n\n const path = `/RetrieveMetadataChanges(Query=@q,ClientVersionStamp=@s,DeletedMetadataFilters=@d)` +\n `?@q=${queryJson}&@s='${clientVersionStamp}'&@d=${deletedFilter}`;\n\n interface RetrieveMetadataChangesResponse {\n EntityMetadata: Array<{\n LogicalName: string;\n HasChanged: boolean | null;\n MetadataId: string;\n }>;\n ServerVersionStamp: string;\n DeletedMetadata?: {\n Keys?: string[];\n [key: string]: unknown;\n };\n }\n\n let response: RetrieveMetadataChangesResponse;\n try {\n response = await this.http.get<RetrieveMetadataChangesResponse>(path);\n } catch (error: unknown) {\n // Check for expired version stamp\n if (this.isExpiredVersionStampError(error)) {\n throw new MetadataError(\n ErrorCode.META_VERSION_STAMP_EXPIRED,\n 'Cached version stamp has expired (>90 days). A full metadata refresh is required.',\n { clientVersionStamp },\n );\n }\n throw error;\n }\n\n // Extract changed entity names (HasChanged = true or null means changed)\n const changedEntityNames = (response.EntityMetadata ?? [])\n .filter((e) => e.HasChanged !== false)\n .map((e) => e.LogicalName)\n .filter(Boolean);\n\n // Extract deleted entity names from DeletedMetadata\n const deletedEntityNames: string[] = [];\n if (response.DeletedMetadata?.Keys) {\n // DeletedMetadata.Keys contains MetadataIds, not LogicalNames\n // We log them but cannot resolve to names without the cache\n log.info(`${response.DeletedMetadata.Keys.length} entity metadata IDs were deleted`);\n }\n\n const newVersionStamp = response.ServerVersionStamp;\n\n log.info(`Change detection complete: ${changedEntityNames.length} changed, ${deletedEntityNames.length} deleted`, {\n changedEntityNames: changedEntityNames.length <= 10 ? changedEntityNames : `${changedEntityNames.length} entities`,\n newVersionStamp: newVersionStamp.substring(0, 20) + '...',\n });\n\n return {\n changedEntityNames,\n deletedEntityNames,\n newVersionStamp,\n };\n }\n\n /**\n * Perform an initial metadata query to get the first ServerVersionStamp.\n * This is used on the very first run (no cache exists).\n *\n * @returns The initial server version stamp\n */\n async getInitialVersionStamp(): Promise<string> {\n log.info('Fetching initial server version stamp');\n\n const query = {\n Criteria: {\n FilterOperator: 'And',\n Conditions: [],\n },\n Properties: {\n AllProperties: false,\n PropertyNames: ['LogicalName'],\n },\n };\n\n // RetrieveMetadataChanges is a Function (GET) - no ClientVersionStamp for initial call\n const queryParam = encodeURIComponent(JSON.stringify(query));\n const path = `/RetrieveMetadataChanges(Query=@q)?@q=${queryParam}`;\n\n interface InitialResponse {\n ServerVersionStamp: string;\n }\n\n const response = await this.http.get<InitialResponse>(path);\n\n log.info('Initial version stamp acquired');\n return response.ServerVersionStamp;\n }\n\n /** Check if an error is the expired version stamp error (0x80044352) */\n private isExpiredVersionStampError(error: unknown): boolean {\n const msg = error instanceof Error ? error.message : String(error);\n // Check message and context.responseBody (HttpClient puts body text in context)\n const errorRecord = error as Record<string, Record<string, unknown>> | undefined;\n const contextBody = errorRecord?.context\n ? String(errorRecord.context['responseBody'] ?? '')\n : '';\n const combined = msg + contextBody;\n return combined.includes(EXPIRED_VERSION_STAMP_ERROR) || combined.includes('ExpiredVersionStamp');\n }\n}\n","/**\r\n * @xrmforge/typegen - Label Utilities\r\n *\r\n * Extracts and formats localized labels from Dataverse metadata.\r\n * Supports dual-language output (Goldene Regel 15):\r\n * - Primary language for identifiers and first JSDoc line\r\n * - Secondary language as optional addition in JSDoc\r\n *\r\n * Format: \"Primary Label | Sekundäres Label\"\r\n * If secondary language is not available: \"Primary Label\" only.\r\n */\r\n\r\nimport type { Label, LocalizedLabel } from './types.js';\r\n\r\n// ─── Configuration ───────────────────────────────────────────────────────────\r\n\r\nexport interface LabelConfig {\r\n /** Primary language LCID (used for identifiers and first JSDoc line). Default: 1033 (English) */\r\n primaryLanguage: number;\r\n /** Optional secondary language LCID (added as comment). Example: 1031 (German) */\r\n secondaryLanguage?: number;\r\n}\r\n\r\n/** Default: English only */\r\nexport const DEFAULT_LABEL_CONFIG: LabelConfig = {\r\n primaryLanguage: 1033,\r\n};\r\n\r\n// ─── Label Extraction ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Extract a label string for the primary language.\r\n * Falls back to UserLocalizedLabel if the specific LCID is not found.\r\n * Returns empty string if no label is available.\r\n */\r\nexport function getPrimaryLabel(label: Label | null | undefined, config: LabelConfig): string {\r\n if (!label) return '';\r\n return getLabelForLanguage(label, config.primaryLanguage);\r\n}\r\n\r\n/**\r\n * Extract a dual-language JSDoc string.\r\n * Returns \"Primary | Secondary\" if both languages available,\r\n * or just \"Primary\" if secondary is missing or not configured.\r\n */\r\nexport function getJSDocLabel(label: Label | null | undefined, config: LabelConfig): string {\r\n if (!label) return '';\r\n\r\n const primary = getLabelForLanguage(label, config.primaryLanguage);\r\n if (!primary) return '';\r\n\r\n if (!config.secondaryLanguage) return primary;\r\n\r\n const secondary = getLabelForLanguage(label, config.secondaryLanguage);\r\n if (!secondary || secondary === primary) return primary;\r\n\r\n return `${primary} | ${secondary}`;\r\n}\r\n\r\n/**\r\n * Extract a label for a specific language code.\r\n * Searches LocalizedLabels first, falls back to UserLocalizedLabel.\r\n */\r\nfunction getLabelForLanguage(label: Label, languageCode: number): string {\r\n // Search in LocalizedLabels array first (most precise)\r\n const localized = label.LocalizedLabels?.find(\r\n (l: LocalizedLabel) => l.LanguageCode === languageCode,\r\n );\r\n if (localized?.Label) return localized.Label;\r\n\r\n // Fall back to UserLocalizedLabel\r\n if (label.UserLocalizedLabel?.Label) return label.UserLocalizedLabel.Label;\r\n\r\n return '';\r\n}\r\n\r\n// ─── Transliteration ─────────────────────────────────────────────────────────\r\n\r\n/** German umlaut transliteration map (R6-03) */\r\nconst TRANSLITERATION_MAP: Record<string, string> = {\r\n 'ä': 'ae', 'ö': 'oe', 'ü': 'ue', 'ß': 'ss',\r\n 'Ä': 'Ae', 'Ö': 'Oe', 'Ü': 'Ue',\r\n};\r\n\r\n/**\r\n * Transliterate German umlauts to ASCII equivalents for TypeScript identifiers.\r\n * Other non-ASCII characters are removed in the subsequent cleaning step.\r\n */\r\nexport function transliterateUmlauts(text: string): string {\r\n return text.replace(/[äöüßÄÖÜ]/g, (char) => TRANSLITERATION_MAP[char] ?? char);\r\n}\r\n\r\n// ─── Identifier Generation ───────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert a label string to a valid TypeScript identifier (PascalCase).\r\n * Transliterates German umlauts (ä to ae, ö to oe, ü to ue, ß to ss),\r\n * then removes remaining invalid characters.\r\n *\r\n * @returns A valid TypeScript identifier, or null if the label cannot be converted\r\n */\r\nexport function labelToIdentifier(label: string): string | null {\r\n if (!label || label.trim().length === 0) return null;\r\n\r\n // Transliterate umlauts first (ä -> ae, ö -> oe, etc.)\r\n const transliterated = transliterateUmlauts(label);\r\n\r\n // Remove characters that are not ASCII letters, digits, spaces, or underscores\r\n let cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n cleaned = cleaned.trim();\r\n\r\n if (cleaned.length === 0) return null;\r\n\r\n // Convert to PascalCase: split by spaces/underscores, capitalize first letter of each word\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n const pascal = parts\r\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\r\n .join('');\r\n\r\n if (pascal.length === 0) return null;\r\n\r\n // Ensure it starts with a letter (not a digit)\r\n if (/^\\d/.test(pascal)) {\r\n return `_${pascal}`;\r\n }\r\n\r\n return pascal;\r\n}\r\n\r\n/**\r\n * Generate unique enum member names from OptionSet labels.\r\n * Handles duplicates by appending _{Value} to colliding names.\r\n * Falls back to Value_{numericValue} for unconvertible labels.\r\n *\r\n * @param options - Array of { Value, Label } from OptionSetMetadata\r\n * @param config - Label configuration for language selection\r\n * @returns Array of { name, value, jsDocLabel } for enum generation\r\n */\r\nexport function generateEnumMembers(\r\n options: Array<{ Value: number; Label: Label }>,\r\n config: LabelConfig,\r\n): Array<{ name: string; value: number; jsDocLabel: string }> {\r\n // Step 1: Generate initial names from labels\r\n const members = options.map((option) => {\r\n const primaryLabel = getPrimaryLabel(option.Label, config);\r\n const identifier = labelToIdentifier(primaryLabel);\r\n const jsDocLabel = getJSDocLabel(option.Label, config);\r\n\r\n return {\r\n rawName: identifier ?? `Value_${option.Value}`,\r\n value: option.Value,\r\n jsDocLabel: jsDocLabel || `Value ${option.Value}`,\r\n fromLabel: identifier !== null,\r\n };\r\n });\r\n\r\n // Step 2: Detect duplicates and disambiguate\r\n const nameCount = new Map<string, number>();\r\n for (const m of members) {\r\n nameCount.set(m.rawName, (nameCount.get(m.rawName) ?? 0) + 1);\r\n }\r\n\r\n const nameUsed = new Map<string, boolean>();\r\n const result: Array<{ name: string; value: number; jsDocLabel: string }> = [];\r\n\r\n for (const m of members) {\r\n const count = nameCount.get(m.rawName) ?? 1;\r\n\r\n if (count === 1) {\r\n // Unique name, use as-is\r\n result.push({ name: m.rawName, value: m.value, jsDocLabel: m.jsDocLabel });\r\n } else {\r\n // Duplicate: first occurrence keeps the name, subsequent get _{Value}\r\n if (!nameUsed.get(m.rawName)) {\r\n nameUsed.set(m.rawName, true);\r\n result.push({ name: m.rawName, value: m.value, jsDocLabel: m.jsDocLabel });\r\n } else {\r\n result.push({ name: `${m.rawName}_${m.value}`, value: m.value, jsDocLabel: m.jsDocLabel });\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ─── Query Parameter ─────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Build the LabelLanguages query parameter for Dataverse Metadata API.\r\n * Returns the parameter string to append to metadata queries.\r\n *\r\n * @example\r\n * getLabelLanguagesParam({ primaryLanguage: 1033, secondaryLanguage: 1031 })\r\n * // Returns \"&LabelLanguages=1033,1031\"\r\n */\r\nexport function getLabelLanguagesParam(config: LabelConfig): string {\r\n const languages = [config.primaryLanguage];\r\n if (config.secondaryLanguage && config.secondaryLanguage !== config.primaryLanguage) {\r\n languages.push(config.secondaryLanguage);\r\n }\r\n return `&LabelLanguages=${languages.join(',')}`;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Type Mapping\r\n *\r\n * Maps Dataverse AttributeType values to TypeScript types for:\r\n * 1. Entity interfaces (Web API data types)\r\n * 2. Form interfaces (Xrm.Attributes.* types from @types/xrm)\r\n * 3. Control interfaces (Xrm.Controls.* types from @types/xrm)\r\n *\r\n * This is the bridge between Dataverse metadata and generated TypeScript.\r\n * Goldene Regel 1: All types extend @types/xrm, never replace.\r\n */\r\n\r\nimport type { AttributeMetadata } from '../metadata/types.js';\r\nimport { createLogger } from '../logger.js';\r\n\r\nconst log = createLogger('type-mapping');\r\n\r\n// ─── Entity Data Types (Web API responses) ───────────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to TypeScript type for entity interfaces.\r\n * These represent the raw data types returned by the Web API.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @param isLookup - Whether this is a lookup field (uses _fieldname_value pattern)\r\n * @returns TypeScript type string (e.g. \"string\", \"number\", \"boolean\")\r\n */\r\nexport function getEntityPropertyType(attributeType: string, isLookup: boolean = false): string {\r\n if (isLookup) return 'string'; // GUIDs are strings in Web API\r\n\r\n const mapping = ENTITY_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n log.warn(`Unmapped AttributeType \"${attributeType}\" falling back to \"unknown\"`);\r\n return 'unknown'; // Unmapped types get 'unknown' (safer than 'any')\r\n}\r\n\r\n/** Dataverse AttributeType to TypeScript data type */\r\nconst ENTITY_TYPE_MAP: Record<string, string> = {\r\n // String types\r\n String: 'string',\r\n Memo: 'string',\r\n EntityName: 'string',\r\n\r\n // Numeric types\r\n Integer: 'number',\r\n BigInt: 'number',\r\n Decimal: 'number',\r\n Double: 'number',\r\n Money: 'number',\r\n\r\n // Boolean\r\n Boolean: 'boolean',\r\n\r\n // OptionSet types (numeric values in Web API)\r\n Picklist: 'number',\r\n State: 'number',\r\n Status: 'number',\r\n // MultiSelectPicklist: Web API returns comma-separated string (e.g. \"595300000,595300001\")\r\n // Verified live on markant-dev.crm4.dynamics.com 2026-03-29\r\n MultiSelectPicklist: 'string',\r\n\r\n // Date/Time (ISO 8601 strings in Web API)\r\n DateTime: 'string',\r\n\r\n // Identifiers\r\n Uniqueidentifier: 'string',\r\n\r\n // Lookup (handled separately via _value pattern, but base type is string)\r\n Lookup: 'string',\r\n Customer: 'string',\r\n Owner: 'string',\r\n PartyList: 'string',\r\n\r\n // Binary/Image (not typically in entity interfaces, filtered by shouldIncludeInEntityInterface)\r\n Virtual: 'unknown',\r\n CalendarRules: 'unknown',\r\n};\r\n\r\n// ─── Form Attribute Types (@types/xrm) ──────────────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to Xrm.Attributes.* type for form interfaces.\r\n * These represent the getAttribute() return types on FormContext.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @returns Fully qualified Xrm attribute type string\r\n */\r\nexport function getFormAttributeType(attributeType: string): string {\r\n const mapping = FORM_ATTRIBUTE_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n log.warn(`Unmapped form AttributeType \"${attributeType}\" falling back to generic Attribute`);\r\n return 'Xrm.Attributes.Attribute'; // Generic fallback\r\n}\r\n\r\n/** Dataverse AttributeType to Xrm.Attributes.* type */\r\nconst FORM_ATTRIBUTE_TYPE_MAP: Record<string, string> = {\r\n // String types\r\n String: 'Xrm.Attributes.StringAttribute',\r\n Memo: 'Xrm.Attributes.StringAttribute',\r\n\r\n // Numeric types\r\n Integer: 'Xrm.Attributes.NumberAttribute',\r\n BigInt: 'Xrm.Attributes.NumberAttribute',\r\n Decimal: 'Xrm.Attributes.NumberAttribute',\r\n Double: 'Xrm.Attributes.NumberAttribute',\r\n Money: 'Xrm.Attributes.NumberAttribute',\r\n\r\n // Boolean\r\n Boolean: 'Xrm.Attributes.BooleanAttribute',\r\n\r\n // OptionSet types\r\n Picklist: 'Xrm.Attributes.OptionSetAttribute',\r\n State: 'Xrm.Attributes.OptionSetAttribute',\r\n Status: 'Xrm.Attributes.OptionSetAttribute',\r\n MultiSelectPicklist: 'Xrm.Attributes.MultiSelectOptionSetAttribute',\r\n\r\n // Date/Time\r\n DateTime: 'Xrm.Attributes.DateAttribute',\r\n\r\n // Lookup types\r\n Lookup: 'Xrm.Attributes.LookupAttribute',\r\n Customer: 'Xrm.Attributes.LookupAttribute',\r\n Owner: 'Xrm.Attributes.LookupAttribute',\r\n PartyList: 'Xrm.Attributes.LookupAttribute',\r\n\r\n // Entity Name (string attribute in forms)\r\n EntityName: 'Xrm.Attributes.StringAttribute',\r\n};\r\n\r\n// ─── Form Control Types (@types/xrm) ────────────────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to Xrm.Controls.* type for form interfaces.\r\n * These represent the getControl() return types on FormContext.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @returns Fully qualified Xrm control type string\r\n */\r\nexport function getFormControlType(attributeType: string): string {\r\n const mapping = FORM_CONTROL_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n return 'Xrm.Controls.StandardControl'; // Generic fallback\r\n}\r\n\r\n/** Dataverse AttributeType to Xrm.Controls.* type */\r\nconst FORM_CONTROL_TYPE_MAP: Record<string, string> = {\r\n // Standard controls\r\n String: 'Xrm.Controls.StringControl',\r\n Memo: 'Xrm.Controls.StringControl',\r\n Integer: 'Xrm.Controls.NumberControl',\r\n BigInt: 'Xrm.Controls.NumberControl',\r\n Decimal: 'Xrm.Controls.NumberControl',\r\n Double: 'Xrm.Controls.NumberControl',\r\n Money: 'Xrm.Controls.NumberControl',\r\n Boolean: 'Xrm.Controls.StandardControl',\r\n DateTime: 'Xrm.Controls.DateControl',\r\n EntityName: 'Xrm.Controls.StandardControl',\r\n\r\n // OptionSet controls\r\n Picklist: 'Xrm.Controls.OptionSetControl',\r\n State: 'Xrm.Controls.OptionSetControl',\r\n Status: 'Xrm.Controls.OptionSetControl',\r\n MultiSelectPicklist: 'Xrm.Controls.MultiSelectOptionSetControl',\r\n\r\n // Lookup controls\r\n Lookup: 'Xrm.Controls.LookupControl',\r\n Customer: 'Xrm.Controls.LookupControl',\r\n Owner: 'Xrm.Controls.LookupControl',\r\n PartyList: 'Xrm.Controls.LookupControl',\r\n};\r\n\r\n// ─── Mock Value Types (for @xrmforge/testing) ──────────────────────────────\r\n\r\n/**\r\n * Map Dataverse AttributeType to JavaScript value type for mock objects.\r\n * Used by the form generator to create MockValues types for @xrmforge/testing.\r\n *\r\n * @param attributeType - The AttributeType from Dataverse metadata\r\n * @returns TypeScript value type string (e.g. \"string | null\", \"number | null\")\r\n */\r\nexport function getFormMockValueType(attributeType: string): string {\r\n const mapping = FORM_MOCK_VALUE_TYPE_MAP[attributeType];\r\n if (mapping) return mapping;\r\n\r\n return 'unknown';\r\n}\r\n\r\n/** Dataverse AttributeType to JavaScript value type */\r\nconst FORM_MOCK_VALUE_TYPE_MAP: Record<string, string> = {\r\n // String types\r\n String: 'string | null',\r\n Memo: 'string | null',\r\n EntityName: 'string | null',\r\n\r\n // Numeric types\r\n Integer: 'number | null',\r\n BigInt: 'number | null',\r\n Decimal: 'number | null',\r\n Double: 'number | null',\r\n Money: 'number | null',\r\n\r\n // Boolean\r\n Boolean: 'boolean | null',\r\n\r\n // OptionSet types (numeric values at runtime)\r\n Picklist: 'number | null',\r\n State: 'number | null',\r\n Status: 'number | null',\r\n MultiSelectPicklist: 'number[] | null',\r\n\r\n // Date/Time\r\n DateTime: 'Date | null',\r\n\r\n // Lookup types\r\n Lookup: 'Xrm.LookupValue[] | null',\r\n Customer: 'Xrm.LookupValue[] | null',\r\n Owner: 'Xrm.LookupValue[] | null',\r\n PartyList: 'Xrm.LookupValue[] | null',\r\n};\r\n\r\n// ─── Identifier Utilities ────────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert a Dataverse LogicalName to a safe TypeScript identifier.\r\n * Validates that the result is a valid identifier.\r\n *\r\n * @param logicalName - Dataverse field or entity logical name\r\n * @returns A valid TypeScript identifier\r\n * @throws Never throws; returns the input if already valid, prefixes with _ if starts with digit\r\n */\r\nexport function toSafeIdentifier(logicalName: string): string {\r\n // Most Dataverse logical names are already valid identifiers\r\n if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(logicalName)) {\r\n return logicalName;\r\n }\r\n\r\n // Remove invalid characters\r\n let safe = logicalName.replace(/[^a-zA-Z0-9_$]/g, '_');\r\n\r\n // Ensure it doesn't start with a digit\r\n if (/^\\d/.test(safe)) {\r\n safe = `_${safe}`;\r\n }\r\n\r\n // Ensure it's not empty\r\n if (safe.length === 0) {\r\n return '_unnamed';\r\n }\r\n\r\n return safe;\r\n}\r\n\r\n/**\r\n * Convert a Dataverse LogicalName to PascalCase for use as interface/type name.\r\n *\r\n * @example\r\n * toPascalCase('account') // 'Account'\r\n * toPascalCase('markant_cdhcontactsource') // 'MarkantCdhcontactsource'\r\n */\r\nexport function toPascalCase(logicalName: string): string {\r\n return logicalName\r\n .split('_')\r\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\r\n .join('');\r\n}\r\n\r\n// ─── Lookup Field Name Utilities ─────────────────────────────────────────────\r\n\r\n/**\r\n * Convert a lookup attribute LogicalName to its Web API value property name.\r\n * In the Web API, lookup fields are represented as `_fieldname_value`.\r\n *\r\n * @example\r\n * toLookupValueProperty('primarycontactid') // '_primarycontactid_value'\r\n * toLookupValueProperty('ownerid') // '_ownerid_value'\r\n */\r\nexport function toLookupValueProperty(logicalName: string): string {\r\n return `_${logicalName}_value`;\r\n}\r\n\r\n/**\r\n * Determine if an attribute is a single-value lookup type.\r\n * PartyList is NOT included: it's a collection navigation property (ActivityParty[]),\r\n * not a single _fieldname_value property in the Web API.\r\n */\r\nexport function isLookupType(attributeType: string): boolean {\r\n return attributeType === 'Lookup' || attributeType === 'Customer' || attributeType === 'Owner';\r\n}\r\n\r\n/**\r\n * Determine if an attribute is a PartyList (ActivityParty collection).\r\n * PartyList fields (to, from, cc, bcc, requiredattendees, optionalattendees)\r\n * are navigation properties in the Web API, not flat lookup properties.\r\n */\r\nexport function isPartyListType(attributeType: string): boolean {\r\n return attributeType === 'PartyList';\r\n}\r\n\r\n/**\r\n * Determine if an attribute should be included in entity interfaces.\r\n * Excludes virtual/calculated fields that are not readable via Web API.\r\n *\r\n * Filtered types:\r\n * - Virtual, CalendarRules: not readable via Web API\r\n * - ManagedProperty: solution metadata (iscustomizable etc.), not business data\r\n * - EntityName: internal companion fields for lookups; entity type info is only\r\n * available via @Microsoft.Dynamics.CRM.lookuplogicalname OData annotation,\r\n * not as a standalone property in Web API responses\r\n */\r\nexport function shouldIncludeInEntityInterface(attr: AttributeMetadata): boolean {\r\n // Exclude virtual attributes (images, file, calculated), but keep MultiSelectPicklist\r\n // (MultiSelectPicklist has AttributeType \"Virtual\" but @odata.type distinguishes it)\r\n if (attr.AttributeType === 'Virtual' || attr.AttributeType === 'CalendarRules') {\r\n const odataType = (attr as unknown as Record<string, unknown>)['@odata.type'] as string | undefined;\r\n if (odataType !== '#Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata') {\r\n return false;\r\n }\r\n }\r\n\r\n // Exclude solution metadata (not business data)\r\n if (attr.AttributeType === 'ManagedProperty') {\r\n return false;\r\n }\r\n\r\n // Exclude EntityName companion fields (e.g. owneridtype, regardingobjectidtype)\r\n // Entity type info comes from OData annotations, not from these fields\r\n if (attr.AttributeType === 'EntityName') {\r\n return false;\r\n }\r\n\r\n // Exclude attributes that cannot be read\r\n if (attr.IsValidForRead === false) {\r\n return false;\r\n }\r\n\r\n return true;\r\n}\r\n","/**\r\n * @xrmforge/typegen - ActivityParty Base Interface\r\n *\r\n * ActivityParty is a Dataverse system entity that represents participants\r\n * in activities (email, appointment, phonecall, fax, etc.).\r\n *\r\n * PartyList fields (to, from, cc, bcc, requiredattendees, optionalattendees)\r\n * are collection-valued navigation properties that return ActivityParty arrays.\r\n *\r\n * This interface is generated once and referenced by all Activity entities\r\n * that have PartyList fields.\r\n *\r\n * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/activityparty\r\n */\r\n\r\n/**\r\n * Generate the ActivityParty base interface declaration.\r\n * This is a fixed structure (system entity, never customized).\r\n *\r\n * @returns TypeScript declaration string\r\n */\r\nexport function generateActivityPartyInterface(): string {\r\n const lines: string[] = [];\r\n\r\n lines.push('/**');\r\n lines.push(' * Activity Party - Teilnehmer einer Aktivität (E-Mail, Termin, etc.)');\r\n lines.push(' * Wird von PartyList-Feldern (to, from, cc, bcc, requiredattendees) referenziert.');\r\n lines.push(' * @see https://learn.microsoft.com/en-us/power-apps/developer/data-platform/reference/entities/activityparty');\r\n lines.push(' */');\r\n lines.push('export interface ActivityParty {');\r\n lines.push(' /** Primary key */');\r\n lines.push(' activitypartyid?: string;');\r\n lines.push(' /** Referenz auf die zugehörige Aktivität */');\r\n lines.push(' _activityid_value?: string;');\r\n lines.push(' /** Referenz auf den Teilnehmer (account | contact | systemuser | queue | knowledgearticle) */');\r\n lines.push(' _partyid_value?: string;');\r\n lines.push(' /**');\r\n lines.push(' * Rolle des Teilnehmers:');\r\n lines.push(' * 1=Sender, 2=To, 3=CC, 4=BCC, 5=Required Attendee,');\r\n lines.push(' * 6=Optional Attendee, 7=Organizer, 8=Regarding, 9=Owner,');\r\n lines.push(' * 10=Resource, 11=Customer, 12=Chat Participant, 13=Related');\r\n lines.push(' */');\r\n lines.push(' participationtypemask?: number;');\r\n lines.push(' /** E-Mail-Adresse für die Zustellung */');\r\n lines.push(' addressused?: string;');\r\n lines.push(' /** Aufwand des Teilnehmers (bei Serviceterminen) */');\r\n lines.push(' effort?: number;');\r\n lines.push(' /** Name des Teilnehmers (wenn nicht aufgelöst) */');\r\n lines.push(' unresolvedpartyname?: string;');\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\r\n * @xrmforge/typegen - Generator-specific Label Utilities\r\n *\r\n * This module contains ONLY generator-specific label functions.\r\n * Core label extraction (getPrimaryLabel, getJSDocLabel, LabelConfig etc.)\r\n * is provided by the canonical implementation in metadata/labels.ts (R6-02).\r\n *\r\n * Re-exports from metadata/labels.ts are provided for convenience.\r\n */\r\n\r\n// Re-export canonical label utilities from metadata/labels.ts\r\nexport {\r\n getPrimaryLabel,\r\n getJSDocLabel as formatDualLabel,\r\n labelToIdentifier,\r\n transliterateUmlauts,\r\n DEFAULT_LABEL_CONFIG,\r\n} from '../metadata/labels.js';\r\nexport type { LabelConfig } from '../metadata/labels.js';\r\n\r\n// Re-export getSecondaryLabel functionality via getJSDocLabel\r\n// (getSecondaryLabel was a generator-only concept, not needed separately)\r\n\r\nimport type { Label } from '../metadata/types.js';\r\nimport type { LabelConfig } from '../metadata/labels.js';\r\n\r\n/**\r\n * Extract the secondary language label, if configured and available.\r\n * Returns undefined if no secondary language is configured or label not found.\r\n */\r\nexport function getSecondaryLabel(label: Label, config: LabelConfig): string | undefined {\r\n if (!config.secondaryLanguage) return undefined;\r\n\r\n const secondary = label.LocalizedLabels.find((l) => l.LanguageCode === config.secondaryLanguage);\r\n return (secondary && secondary.Label) || undefined;\r\n}\r\n\r\n// ─── Generator-specific: Enum Member Names ──────────────────────────────────\r\n\r\nimport { transliterateUmlauts } from '../metadata/labels.js';\r\n\r\n/**\r\n * Convert a label to a valid PascalCase TypeScript identifier for enum members.\r\n * Transliterates German umlauts (R6-03), then removes remaining invalid characters.\r\n *\r\n * @example\r\n * labelToEnumMember(\"Preferred Customer\") // \"PreferredCustomer\"\r\n * labelToEnumMember(\"Bevorzügter Kunde\") // \"BevorzuegterKunde\"\r\n * labelToEnumMember(\"100% Complete\") // \"_100Complete\"\r\n * labelToEnumMember(\"\") // \"\" (caller handles empty)\r\n */\r\nexport function labelToEnumMember(labelText: string): string {\r\n if (!labelText) return '';\r\n\r\n // Transliterate umlauts first (ä -> ae, ö -> oe, etc.)\r\n const transliterated = transliterateUmlauts(labelText);\r\n\r\n // Remove characters that are not ASCII letters, digits, spaces or underscores\r\n const cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n\r\n // Split on whitespace/underscore and PascalCase each word\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n if (parts.length === 0) return '';\r\n\r\n const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');\r\n\r\n // Ensure it doesn't start with a digit\r\n if (/^\\d/.test(pascal)) {\r\n return `_${pascal}`;\r\n }\r\n\r\n return pascal;\r\n}\r\n\r\n// ─── Generator-specific: Disambiguate Duplicate Enum Members ─────────────────\r\n\r\n/**\r\n * Disambiguate duplicate enum member names by appending the numeric value.\r\n * Only the second and subsequent duplicates get the suffix.\r\n * Re-checks that the suffixed name doesn't collide with an existing name.\r\n *\r\n * @example\r\n * disambiguateEnumMembers([\r\n * { name: \"Active\", value: 1 },\r\n * { name: \"Active\", value: 2 },\r\n * ])\r\n * // [{ name: \"Active\", value: 1 }, { name: \"Active_2\", value: 2 }]\r\n *\r\n * // Edge case: \"Active_2\" already exists as a label-derived name\r\n * disambiguateEnumMembers([\r\n * { name: \"Active\", value: 1 },\r\n * { name: \"Active\", value: 2 },\r\n * { name: \"Active_2\", value: 3 },\r\n * ])\r\n * // [{ name: \"Active\", value: 1 }, { name: \"Active_2_v2\", value: 2 }, { name: \"Active_2\", value: 3 }]\r\n */\r\nexport function disambiguateEnumMembers(\r\n members: Array<{ name: string; value: number }>,\r\n): Array<{ name: string; value: number }> {\r\n // First pass: collect all original names to detect collisions\r\n const allOriginalNames = new Set(members.map((m) => m.name));\r\n const usedNames = new Set<string>();\r\n const result: Array<{ name: string; value: number }> = [];\r\n\r\n for (const { name, value } of members) {\r\n let finalName = name;\r\n\r\n if (usedNames.has(finalName)) {\r\n // Try _value suffix first\r\n finalName = `${name}_${value}`;\r\n\r\n // If that also collides (with an original name or already used), keep suffixing\r\n let attempt = 2;\r\n while (usedNames.has(finalName) || (allOriginalNames.has(finalName) && finalName !== name)) {\r\n finalName = `${name}_${value}_v${attempt}`;\r\n attempt++;\r\n }\r\n }\r\n\r\n usedNames.add(finalName);\r\n result.push({ name: finalName, value });\r\n }\r\n\r\n return result;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Entity Interface Generator\r\n *\r\n * Generates TypeScript declaration files (.d.ts) for Dataverse entity interfaces.\r\n * These interfaces represent the data types returned by the Web API.\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.Entities {\r\n * interface Account {\r\n * accountid: string | null;\r\n * name: string | null;\r\n * // ...\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { EntityTypeInfo } from '../metadata/types.js';\r\nimport {\r\n getEntityPropertyType,\r\n isLookupType,\r\n isPartyListType,\r\n toLookupValueProperty,\r\n shouldIncludeInEntityInterface,\r\n toPascalCase,\r\n} from './type-mapping.js';\r\nimport { formatDualLabel, type LabelConfig, DEFAULT_LABEL_CONFIG } from './label-utils.js';\r\n\r\n/** Options for entity interface generation */\r\nexport interface EntityGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n}\r\n\r\n/**\r\n * Generate a TypeScript declaration for an entity interface.\r\n *\r\n * @param info - Complete entity metadata (from MetadataClient.getEntityTypeInfo)\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string (.d.ts content)\r\n */\r\nexport function generateEntityInterface(info: EntityTypeInfo, options: EntityGeneratorOptions = {}): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityName = toPascalCase(info.entity.LogicalName);\r\n const entityLabel = formatDualLabel(info.entity.DisplayName, labelConfig);\r\n\r\n const lines: string[] = [];\r\n\r\n // Check if ActivityParty import is needed (entity has PartyList fields)\r\n const partyListAttrs = info.attributes.filter((a) => isPartyListType(a.AttributeType));\r\n if (partyListAttrs.length > 0) {\r\n lines.push(\"import type { ActivityParty } from './_activity-party.js';\");\r\n lines.push('');\r\n }\r\n\r\n // Entity JSDoc\r\n if (entityLabel) {\r\n lines.push(`/** ${entityLabel} */`);\r\n }\r\n lines.push(`export interface ${entityName} {`);\r\n\r\n // Filter and sort attributes\r\n const includedAttrs = info.attributes\r\n .filter(shouldIncludeInEntityInterface)\r\n .sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));\r\n\r\n // Build lookup map for target info in JSDoc\r\n const lookupTargets = new Map<string, string[]>();\r\n for (const la of info.lookupAttributes) {\r\n if (la.Targets && la.Targets.length > 0) {\r\n lookupTargets.set(la.LogicalName, la.Targets);\r\n }\r\n }\r\n\r\n for (const attr of includedAttrs) {\r\n const isLookup = isLookupType(attr.AttributeType);\r\n const propertyName = isLookup ? toLookupValueProperty(attr.LogicalName) : attr.LogicalName;\r\n const tsType = getEntityPropertyType(attr.AttributeType, isLookup);\r\n\r\n // Build JSDoc\r\n const label = formatDualLabel(attr.DisplayName, labelConfig);\r\n const jsdocParts: string[] = [];\r\n if (label) jsdocParts.push(label);\r\n\r\n // Add lookup target info\r\n if (isLookup) {\r\n const targets = lookupTargets.get(attr.LogicalName);\r\n if (targets && targets.length > 0) {\r\n jsdocParts.push(`Lookup (${targets.join(' | ')})`);\r\n }\r\n }\r\n\r\n // Add read-only marker\r\n if (!attr.IsValidForCreate && !attr.IsValidForUpdate && !attr.IsPrimaryId) {\r\n jsdocParts.push('read-only');\r\n }\r\n\r\n if (jsdocParts.length > 0) {\r\n lines.push(` /** ${jsdocParts.join(' - ')} */`);\r\n }\r\n\r\n lines.push(` ${propertyName}: ${tsType} | null;`);\r\n }\r\n\r\n // PartyList: single navigation property for the entity's ActivityParty collection.\r\n // Multiple PartyList fields (to, from, cc, bcc, requiredattendees) share ONE\r\n // navigation property per entity (e.g. email_activity_parties).\r\n if (partyListAttrs.length > 0) {\r\n // Find the activity party relationship (there's typically one per activity entity)\r\n const relationship = info.oneToManyRelationships.find(\r\n (r) => r.ReferencingEntity === 'activityparty',\r\n );\r\n const navPropName = relationship\r\n ? relationship.SchemaName.charAt(0).toLowerCase() + relationship.SchemaName.slice(1)\r\n : `${info.entity.LogicalName}_activity_parties`;\r\n\r\n lines.push('');\r\n lines.push(` /** ActivityParty collection (${partyListAttrs.length} PartyList-Felder: ${partyListAttrs.map((a) => a.LogicalName).join(', ')}) */`);\r\n lines.push(` ${navPropName}: ActivityParty[] | null;`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\r\n * @xrmforge/typegen - OptionSet Enum Generator\r\n *\r\n * Generates TypeScript const enums from Dataverse OptionSet metadata.\r\n * Uses const enum because D365 form scripts have no module system at runtime,\r\n * so enum values must be inlined at compile time.\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.OptionSets {\r\n * const enum AccountCategoryCode {\r\n * PreferredCustomer = 1,\r\n * Standard = 2,\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { OptionSetMetadata } from '../metadata/types.js';\r\nimport {\r\n formatDualLabel,\r\n getPrimaryLabel,\r\n labelToEnumMember,\r\n disambiguateEnumMembers,\r\n type LabelConfig,\r\n DEFAULT_LABEL_CONFIG,\r\n} from './label-utils.js';\r\nimport { toPascalCase } from './type-mapping.js';\r\n\r\n/** Options for OptionSet enum generation */\r\nexport interface OptionSetGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n}\r\n\r\n/**\r\n * Generate a TypeScript const enum declaration from an OptionSet.\r\n *\r\n * @param optionSet - OptionSet metadata from Dataverse\r\n * @param entityLogicalName - Entity this OptionSet belongs to (for naming local OptionSets)\r\n * @param attributeSchemaName - Attribute schema name (for naming local OptionSets)\r\n * @param options - Generator options\r\n * @returns TypeScript const enum declaration string\r\n */\r\nexport function generateOptionSetEnum(\r\n optionSet: OptionSetMetadata,\r\n _entityLogicalName: string,\r\n attributeSchemaName: string,\r\n options: OptionSetGeneratorOptions = {},\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n\r\n // Determine enum name\r\n const enumName = optionSet.IsGlobal\r\n ? toPascalCase(optionSet.Name)\r\n : toPascalCase(attributeSchemaName);\r\n\r\n // Build members from options\r\n const rawMembers = optionSet.Options.map((opt) => {\r\n const label = getPrimaryLabel(opt.Label, labelConfig);\r\n const memberName = labelToEnumMember(label);\r\n return {\r\n name: memberName || `Value_${opt.Value}`, // Fallback for empty/invalid labels\r\n value: opt.Value,\r\n option: opt,\r\n };\r\n });\r\n\r\n // Disambiguate duplicate member names\r\n const disambiguated = disambiguateEnumMembers(\r\n rawMembers.map((m) => ({ name: m.name, value: m.value })),\r\n );\r\n\r\n const lines: string[] = [];\r\n\r\n // Enum JSDoc\r\n const enumLabel = formatDualLabel(optionSet.DisplayName, labelConfig);\r\n if (enumLabel) {\r\n lines.push(`/** ${enumLabel} (${optionSet.Name}) */`);\r\n }\r\n\r\n lines.push(`export const enum ${enumName} {`);\r\n\r\n // Members\r\n for (let i = 0; i < disambiguated.length; i++) {\r\n const member = disambiguated[i]!;\r\n const rawMember = rawMembers[i];\r\n if (!rawMember) continue;\r\n\r\n // JSDoc with original label (always show, even if member name was derived from it)\r\n const memberLabel = formatDualLabel(rawMember.option.Label, labelConfig);\r\n if (memberLabel) {\r\n lines.push(` /** ${memberLabel} */`);\r\n }\r\n\r\n lines.push(` ${member.name} = ${member.value},`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate multiple OptionSet enums for all picklist attributes of an entity.\r\n * Handles both local and global OptionSets.\r\n *\r\n * @param picklistAttributes - Picklist attributes with their OptionSet metadata\r\n * @param entityLogicalName - Entity logical name\r\n * @param options - Generator options\r\n * @returns Array of { enumName, content } for each generated enum\r\n */\r\nexport function generateEntityOptionSets(\r\n picklistAttributes: Array<{ SchemaName: string; OptionSet: OptionSetMetadata | null; GlobalOptionSet: OptionSetMetadata | null }>,\r\n entityLogicalName: string,\r\n options: OptionSetGeneratorOptions = {},\r\n): Array<{ enumName: string; content: string }> {\r\n const results: Array<{ enumName: string; content: string }> = [];\r\n const generatedGlobals = new Set<string>();\r\n\r\n for (const attr of picklistAttributes) {\r\n const optionSet = attr.OptionSet || attr.GlobalOptionSet;\r\n if (!optionSet || !optionSet.Options || optionSet.Options.length === 0) continue;\r\n\r\n // Skip global OptionSets that were already generated\r\n if (optionSet.IsGlobal && generatedGlobals.has(optionSet.Name)) continue;\r\n if (optionSet.IsGlobal) generatedGlobals.add(optionSet.Name);\r\n\r\n const enumName = optionSet.IsGlobal\r\n ? toPascalCase(optionSet.Name)\r\n : toPascalCase(attr.SchemaName);\r\n\r\n const content = generateOptionSetEnum(optionSet, entityLogicalName, attr.SchemaName, options);\r\n results.push({ enumName, content });\r\n }\r\n\r\n return results;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Form Interface Generator\r\n *\r\n * Generates TypeScript form interfaces with compile-time field validation.\r\n *\r\n * Architecture:\r\n * 1. Union Type (LeadFormFields): restricts getAttribute to form-specific fields only\r\n * 2. Mapped Return Type (LeadAttributeMap): maps field name to correct Xrm type\r\n * 3. Generic getAttribute<K>: returns the exact type for each field\r\n * 4. Fields const enum: provides autocomplete with dual-language labels\r\n * 5. NO fallback getAttribute(name: string): unknown fields are compile errors\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.Forms.Account {\r\n * type AccountMainFormFields = \"name\" | \"telephone1\" | \"revenue\";\r\n *\r\n * type AccountMainFormAttributeMap = {\r\n * name: Xrm.Attributes.StringAttribute;\r\n * telephone1: Xrm.Attributes.StringAttribute;\r\n * revenue: Xrm.Attributes.NumberAttribute;\r\n * };\r\n *\r\n * type AccountMainFormControlMap = {\r\n * name: Xrm.Controls.StringControl;\r\n * telephone1: Xrm.Controls.StringControl;\r\n * revenue: Xrm.Controls.NumberControl;\r\n * };\r\n *\r\n * const enum AccountMainFormFields {\r\n * Name = 'name',\r\n * Telephone1 = 'telephone1',\r\n * Revenue = 'revenue',\r\n * }\r\n *\r\n * interface AccountMainForm extends Omit<Xrm.FormContext, 'getAttribute' | 'getControl'> {\r\n * getAttribute<K extends AccountMainFormFields>(name: K): AccountMainFormAttributeMap[K];\r\n * getAttribute(index: number): Xrm.Attributes.Attribute;\r\n * getAttribute(): Xrm.Attributes.Attribute[];\r\n * getControl<K extends AccountMainFormFields>(name: K): AccountMainFormControlMap[K];\r\n * getControl(index: number): Xrm.Controls.Control;\r\n * getControl(): Xrm.Controls.Control[];\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { ParsedForm, AttributeMetadata, SpecialControlType } from '../metadata/types.js';\r\nimport {\r\n getFormAttributeType,\r\n getFormControlType,\r\n getFormMockValueType,\r\n toPascalCase,\r\n} from './type-mapping.js';\r\nimport { transliterateUmlauts, formatDualLabel, getPrimaryLabel, type LabelConfig, DEFAULT_LABEL_CONFIG } from './label-utils.js';\r\n\r\n/** Map special control types to @types/xrm control interfaces */\r\nfunction specialControlToXrmType(controlType: SpecialControlType): string | null {\r\n switch (controlType) {\r\n case 'subgrid': return 'Xrm.Controls.GridControl';\r\n case 'editablegrid': return 'Xrm.Controls.GridControl';\r\n case 'quickview': return 'Xrm.Controls.QuickFormControl';\r\n case 'webresource': return 'Xrm.Controls.IframeControl';\r\n case 'iframe': return 'Xrm.Controls.IframeControl';\r\n case 'notes': return 'Xrm.Controls.Control';\r\n case 'map': return 'Xrm.Controls.Control';\r\n default: return null;\r\n }\r\n}\r\n\r\n/** Options for form interface generation */\r\nexport interface FormGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n /** Form types to include (default: [2] = Main only) */\r\n formTypes?: number[];\r\n}\r\n\r\n/** Build a safe TypeScript identifier from a form/tab/section name */\r\nfunction toSafeFormName(formName: string): string {\r\n const result = transliterateUmlauts(formName)\r\n .replace(/[^a-zA-Z0-9\\s]/g, '')\r\n .split(/\\s+/)\r\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\r\n .join('');\r\n\r\n // Prefix with _ if starts with digit (e.g. GUID-based section names)\r\n if (/^\\d/.test(result)) return `_${result}`;\r\n if (result.length === 0) return '_Unnamed';\r\n return result;\r\n}\r\n\r\n/**\r\n * Build the interface base name, avoiding redundant prefix.\r\n * \"Account\" + \"Account\" -> \"Account\" (not \"AccountAccount\")\r\n * \"Lead\" + \"Markant Lead\" -> \"LeadMarkantLead\"\r\n */\r\nfunction buildFormBaseName(entityPascal: string, safeFormName: string): string {\r\n // If form name starts with entity name, don't prefix\r\n if (safeFormName.startsWith(entityPascal)) {\r\n return safeFormName;\r\n }\r\n return `${entityPascal}${safeFormName}`;\r\n}\r\n\r\n/** Convert a label to a PascalCase enum member name */\r\nfunction labelToPascalMember(label: string): string {\r\n if (!label) return '';\r\n const transliterated = transliterateUmlauts(label);\r\n const cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n if (parts.length === 0) return '';\r\n const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join('');\r\n if (/^\\d/.test(pascal)) return `_${pascal}`;\r\n return pascal;\r\n}\r\n\r\n/**\r\n * Generate a complete form declaration: union type, mapped types, fields enum, and interface.\r\n *\r\n * @param form - Parsed form structure (from FormXml parser)\r\n * @param entityLogicalName - Entity this form belongs to\r\n * @param attributeMap - Map of LogicalName to AttributeMetadata for type resolution\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string\r\n */\r\nexport function generateFormInterface(\r\n form: ParsedForm,\r\n entityLogicalName: string,\r\n attributeMap: Map<string, AttributeMetadata>,\r\n options: FormGeneratorOptions = {},\r\n baseNameOverride?: string,\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityPascal = toPascalCase(entityLogicalName);\r\n const baseName = baseNameOverride || buildFormBaseName(entityPascal, toSafeFormName(form.name));\r\n const interfaceName = `${baseName}Form`;\r\n const fieldsTypeName = `${baseName}FormFields`;\r\n const attrMapName = `${baseName}FormAttributeMap`;\r\n const ctrlMapName = `${baseName}FormControlMap`;\r\n\r\n // Get unique field names from form controls (deduplicate across tabs/sections)\r\n const fieldNames = new Set<string>();\r\n for (const control of form.allControls) {\r\n if (control.datafieldname) {\r\n fieldNames.add(control.datafieldname);\r\n }\r\n }\r\n\r\n // Resolve field types from attribute metadata\r\n const fields: Array<{\r\n logicalName: string;\r\n attributeType: string;\r\n formAttributeType: string;\r\n formControlType: string;\r\n label: string;\r\n enumMemberName: string;\r\n }> = [];\r\n\r\n const usedEnumNames = new Set<string>();\r\n\r\n for (const fieldName of [...fieldNames].sort()) {\r\n const attr = attributeMap.get(fieldName);\r\n if (!attr) continue;\r\n\r\n // Build enum member name from label (English primary)\r\n const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);\r\n let enumMember = labelToPascalMember(primaryLabel);\r\n if (!enumMember) {\r\n enumMember = toPascalCase(fieldName);\r\n }\r\n\r\n // Disambiguate enum member names\r\n const originalEnumMember = enumMember;\r\n let counter = 2;\r\n while (usedEnumNames.has(enumMember)) {\r\n enumMember = `${originalEnumMember}${counter}`;\r\n counter++;\r\n }\r\n usedEnumNames.add(enumMember);\r\n\r\n const dualLabel = formatDualLabel(attr.DisplayName, labelConfig);\r\n\r\n fields.push({\r\n logicalName: fieldName,\r\n attributeType: attr.AttributeType,\r\n formAttributeType: getFormAttributeType(attr.AttributeType),\r\n formControlType: getFormControlType(attr.AttributeType),\r\n label: dualLabel,\r\n enumMemberName: enumMember,\r\n });\r\n }\r\n\r\n const lines: string[] = [];\r\n\r\n // 1. Union Type: restricts which field names are valid\r\n lines.push(`/** Valid field names for the \"${form.name}\" form */`);\r\n if (fields.length === 0) {\r\n lines.push(`export type ${fieldsTypeName} = never;`);\r\n } else {\r\n lines.push(`export type ${fieldsTypeName} =`);\r\n for (let i = 0; i < fields.length; i++) {\r\n const separator = i === fields.length - 1 ? ';' : '';\r\n lines.push(` | \"${fields[i]!.logicalName}\"${separator}`);\r\n }\r\n }\r\n lines.push('');\r\n\r\n // 2. Attribute Map: maps field name to Xrm.Attributes.* type\r\n lines.push(`/** Attribute type map for \"${form.name}\" */`);\r\n if (fields.length === 0) {\r\n lines.push(`export type ${attrMapName} = Record<string, never>;`);\r\n } else {\r\n lines.push(`export type ${attrMapName} = {`);\r\n for (const field of fields) {\r\n lines.push(` ${field.logicalName}: ${field.formAttributeType};`);\r\n }\r\n lines.push('};');\r\n }\r\n lines.push('');\r\n\r\n // 3. Control Map: maps field name to Xrm.Controls.* type\r\n lines.push(`/** Control type map for \"${form.name}\" */`);\r\n if (fields.length === 0) {\r\n lines.push(`export type ${ctrlMapName} = Record<string, never>;`);\r\n } else {\r\n lines.push(`export type ${ctrlMapName} = {`);\r\n for (const field of fields) {\r\n lines.push(` ${field.logicalName}: ${field.formControlType};`);\r\n }\r\n lines.push('};');\r\n }\r\n lines.push('');\r\n\r\n // 4. Fields const enum: autocomplete with dual-language labels\r\n lines.push(`/** Field constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${fieldsTypeName}Enum {`);\r\n for (const field of fields) {\r\n if (field.label) {\r\n lines.push(` /** ${field.label} */`);\r\n }\r\n lines.push(` ${field.enumMemberName} = '${field.logicalName}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n\r\n // 4b. Tabs const enum\r\n const namedTabs = form.tabs.filter((t) => t.name);\r\n if (namedTabs.length > 0) {\r\n const tabsEnumName = `${baseName}FormTabs`;\r\n\r\n // Pre-compute disambiguated tab member names (Bug 2 fix: duplicate tab names)\r\n const usedTabMembers = new Set<string>();\r\n const tabMemberNames: string[] = [];\r\n for (const tab of namedTabs) {\r\n let memberName = toSafeFormName(tab.name) || toPascalCase(tab.name);\r\n const originalName = memberName;\r\n let counter = 2;\r\n while (usedTabMembers.has(memberName)) {\r\n memberName = `${originalName}${counter}`;\r\n counter++;\r\n }\r\n usedTabMembers.add(memberName);\r\n tabMemberNames.push(memberName);\r\n }\r\n\r\n lines.push(`/** Tab constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${tabsEnumName} {`);\r\n for (let i = 0; i < namedTabs.length; i++) {\r\n const tab = namedTabs[i]!;\r\n if (tab.label) {\r\n lines.push(` /** ${tab.label} */`);\r\n }\r\n lines.push(` ${tabMemberNames[i]} = '${tab.name}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n\r\n // 4c. Section const enums (one per tab, using disambiguated tab member names)\r\n for (let i = 0; i < namedTabs.length; i++) {\r\n const tab = namedTabs[i]!;\r\n const namedSections = tab.sections.filter((s) => s.name);\r\n if (namedSections.length === 0) continue;\r\n\r\n const tabMemberName = tabMemberNames[i]!;\r\n const sectionsEnumName = `${baseName}Form${tabMemberName}Sections`;\r\n lines.push(`/** Section constants for tab \"${tab.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${sectionsEnumName} {`);\r\n\r\n // Disambiguate section member names within a tab\r\n const usedSectionMembers = new Set<string>();\r\n for (const section of namedSections) {\r\n if (section.label) {\r\n lines.push(` /** ${section.label} */`);\r\n }\r\n let sectionMember = toSafeFormName(section.name) || toPascalCase(section.name);\r\n const originalSectionMember = sectionMember;\r\n let sCounter = 2;\r\n while (usedSectionMembers.has(sectionMember)) {\r\n sectionMember = `${originalSectionMember}${sCounter}`;\r\n sCounter++;\r\n }\r\n usedSectionMembers.add(sectionMember);\r\n lines.push(` ${sectionMember} = '${section.name}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n }\r\n }\r\n\r\n // 4d. Subgrid/QuickView const enum (all special controls with stable IDs)\r\n const specialControls = form.allSpecialControls || [];\r\n const subgrids = specialControls.filter((sc) => sc.controlType === 'subgrid' || sc.controlType === 'editablegrid');\r\n const quickViews = specialControls.filter((sc) => sc.controlType === 'quickview');\r\n\r\n if (subgrids.length > 0) {\r\n const subgridsEnumName = `${baseName}FormSubgrids`;\r\n lines.push(`/** Subgrid constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${subgridsEnumName} {`);\r\n const usedMembers = new Set<string>();\r\n for (const sg of subgrids) {\r\n let member = toSafeFormName(sg.id) || toPascalCase(sg.id);\r\n const original = member;\r\n let counter = 2;\r\n while (usedMembers.has(member)) {\r\n member = `${original}${counter}`;\r\n counter++;\r\n }\r\n usedMembers.add(member);\r\n const label = sg.targetEntityType ? `Subgrid: ${sg.targetEntityType}` : `Subgrid`;\r\n lines.push(` /** ${label} */`);\r\n lines.push(` ${member} = '${sg.id}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n }\r\n\r\n if (quickViews.length > 0) {\r\n const qvEnumName = `${baseName}FormQuickViews`;\r\n lines.push(`/** Quick View constants for \"${form.name}\" (compile-time only, zero runtime) */`);\r\n lines.push(`export const enum ${qvEnumName} {`);\r\n const usedMembers = new Set<string>();\r\n for (const qv of quickViews) {\r\n let member = toSafeFormName(qv.id) || toPascalCase(qv.id);\r\n const original = member;\r\n let counter = 2;\r\n while (usedMembers.has(member)) {\r\n member = `${original}${counter}`;\r\n counter++;\r\n }\r\n usedMembers.add(member);\r\n lines.push(` /** Quick View */`);\r\n lines.push(` ${member} = '${qv.id}',`);\r\n }\r\n lines.push('}');\r\n lines.push('');\r\n }\r\n\r\n // 5. Form Interface: generic getAttribute/getControl with compile-time validation\r\n lines.push(`/** ${form.name} */`);\r\n lines.push(`export interface ${interfaceName} extends Omit<Xrm.FormContext, 'getAttribute' | 'getControl'> {`);\r\n lines.push(` /** Typisierter Feldzugriff: nur Felder die auf diesem Formular existieren */`);\r\n lines.push(` getAttribute<K extends ${fieldsTypeName}>(name: K): ${attrMapName}[K];`);\r\n lines.push(' getAttribute(index: number): Xrm.Attributes.Attribute;');\r\n lines.push(' getAttribute(): Xrm.Attributes.Attribute[];');\r\n lines.push('');\r\n lines.push(` /** Typisierter Control-Zugriff: nur Controls die auf diesem Formular existieren */`);\r\n lines.push(` getControl<K extends ${fieldsTypeName}>(name: K): ${ctrlMapName}[K];`);\r\n\r\n // Typed getControl overloads for special controls (subgrids, quick views, etc.)\r\n for (const sc of specialControls) {\r\n const xrmType = specialControlToXrmType(sc.controlType);\r\n if (xrmType) {\r\n lines.push(` getControl(name: \"${sc.id}\"): ${xrmType};`);\r\n }\r\n }\r\n\r\n lines.push(' getControl(index: number): Xrm.Controls.Control;');\r\n lines.push(' getControl(): Xrm.Controls.Control[];');\r\n\r\n // 6. Typed ui.tabs for compile-time tab name validation\r\n if (form.tabs.length > 0) {\r\n lines.push('');\r\n lines.push(' /** Typisierter Tab-Zugriff */');\r\n lines.push(' ui: {');\r\n lines.push(' tabs: {');\r\n for (const tab of form.tabs) {\r\n if (tab.name) {\r\n const sectionNames = tab.sections.filter((s) => s.name).map((s) => s.name);\r\n if (sectionNames.length > 0) {\r\n // Tab with typed sections\r\n lines.push(` get(name: \"${tab.name}\"): Xrm.Controls.Tab & {`);\r\n lines.push(' sections: {');\r\n for (const sectionName of sectionNames) {\r\n lines.push(` get(name: \"${sectionName}\"): Xrm.Controls.Section;`);\r\n }\r\n lines.push(' get(name: string): Xrm.Controls.Section;');\r\n lines.push(' };');\r\n lines.push(' };');\r\n } else {\r\n lines.push(` get(name: \"${tab.name}\"): Xrm.Controls.Tab;`);\r\n }\r\n }\r\n }\r\n lines.push(' get(name: string): Xrm.Controls.Tab;');\r\n lines.push(' };');\r\n lines.push(' } & Xrm.Ui;');\r\n }\r\n\r\n lines.push('}');\r\n\r\n // 7. MockValues type for @xrmforge/testing\r\n const mockValuesName = `${interfaceName}MockValues`;\r\n lines.push('');\r\n lines.push(`/** Mock value types for \"${form.name}\" form (used with @xrmforge/testing) */`);\r\n lines.push(`export type ${mockValuesName} = {`);\r\n for (const field of fields) {\r\n const mockType = getFormMockValueType(field.attributeType);\r\n lines.push(` ${field.logicalName}?: ${mockType};`);\r\n }\r\n lines.push('};');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate form interfaces for all forms of an entity.\r\n *\r\n * @param forms - Parsed forms (from FormXml parser)\r\n * @param entityLogicalName - Entity logical name\r\n * @param attributes - All attributes of the entity (for type resolution)\r\n * @param options - Generator options\r\n * @returns Array of { formName, interfaceName, content }\r\n */\r\nexport function generateEntityForms(\r\n forms: ParsedForm[],\r\n entityLogicalName: string,\r\n attributes: AttributeMetadata[],\r\n options: FormGeneratorOptions = {},\r\n): Array<{ formName: string; interfaceName: string; content: string }> {\r\n // Build attribute lookup map\r\n const attributeMap = new Map<string, AttributeMetadata>();\r\n for (const attr of attributes) {\r\n attributeMap.set(attr.LogicalName, attr);\r\n }\r\n\r\n const entityPascal = toPascalCase(entityLogicalName);\r\n\r\n // Pre-compute base names for all valid forms\r\n const validForms = forms.filter((f) => f.allControls.length > 0);\r\n const baseNames = validForms.map((form) => {\r\n const safeFormName = toSafeFormName(form.name);\r\n return buildFormBaseName(entityPascal, safeFormName);\r\n });\r\n\r\n // Count occurrences to detect duplicates\r\n const baseNameCounts = new Map<string, number>();\r\n for (const name of baseNames) {\r\n baseNameCounts.set(name, (baseNameCounts.get(name) || 0) + 1);\r\n }\r\n\r\n // Disambiguate duplicate base names with numeric suffix\r\n const baseNameCounters = new Map<string, number>();\r\n const results: Array<{ formName: string; interfaceName: string; content: string }> = [];\r\n\r\n for (let i = 0; i < validForms.length; i++) {\r\n const form = validForms[i]!;\r\n let baseName = baseNames[i]!;\r\n\r\n if (baseNameCounts.get(baseName)! > 1) {\r\n const counter = (baseNameCounters.get(baseName) || 0) + 1;\r\n baseNameCounters.set(baseName, counter);\r\n if (counter > 1) {\r\n baseName = `${baseName}${counter}`;\r\n }\r\n }\r\n\r\n const interfaceName = `${baseName}Form`;\r\n const content = generateFormInterface(form, entityLogicalName, attributeMap, options, baseName);\r\n results.push({ formName: form.name, interfaceName, content });\r\n }\r\n\r\n return results;\r\n}\r\n","/**\r\n * @xrmforge/typegen - Entity Fields Enum Generator\r\n *\r\n * Generates a const enum with ALL entity fields for use with Xrm.WebApi.\r\n * Unlike form-specific Fields enums, this contains every readable attribute.\r\n *\r\n * Output pattern:\r\n * ```typescript\r\n * declare namespace XrmForge.Entities {\r\n * const enum AccountFields {\r\n * /** Account Name | Firmenname *\\/\r\n * Name = 'name',\r\n * /** Main Phone | Haupttelefon *\\/\r\n * Telephone1 = 'telephone1',\r\n * }\r\n * }\r\n * ```\r\n */\r\n\r\nimport type { EntityTypeInfo } from '../metadata/types.js';\r\nimport {\r\n toPascalCase,\r\n shouldIncludeInEntityInterface,\r\n isLookupType,\r\n toLookupValueProperty,\r\n} from './type-mapping.js';\r\nimport {\r\n formatDualLabel,\r\n getPrimaryLabel,\r\n transliterateUmlauts,\r\n type LabelConfig,\r\n DEFAULT_LABEL_CONFIG,\r\n} from './label-utils.js';\r\n\r\n/** Options for entity fields enum generation */\r\nexport interface EntityFieldsGeneratorOptions {\r\n /** Label configuration for dual-language JSDoc comments */\r\n labelConfig?: LabelConfig;\r\n}\r\n\r\n/** Convert a label to a PascalCase enum member name */\r\nfunction labelToPascalMember(label: string): string {\r\n if (!label) return '';\r\n const transliterated = transliterateUmlauts(label);\r\n const cleaned = transliterated.replace(/[^a-zA-Z0-9\\s_]/g, '');\r\n const parts = cleaned.split(/[\\s_]+/).filter((p) => p.length > 0);\r\n if (parts.length === 0) return '';\r\n const pascal = parts.map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join('');\r\n if (/^\\d/.test(pascal)) return `_${pascal}`;\r\n return pascal;\r\n}\r\n\r\n/**\r\n * Generate a const enum with all entity fields for Web API usage.\r\n * Includes ALL readable fields (not form-specific).\r\n *\r\n * @param info - Complete entity metadata\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string\r\n */\r\nexport function generateEntityFieldsEnum(\r\n info: EntityTypeInfo,\r\n options: EntityFieldsGeneratorOptions = {},\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityName = toPascalCase(info.entity.LogicalName);\r\n const enumName = `${entityName}Fields`;\r\n\r\n // Filter and sort attributes\r\n const includedAttrs = info.attributes\r\n .filter(shouldIncludeInEntityInterface)\r\n .sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));\r\n\r\n const lines: string[] = [];\r\n lines.push(`/** All fields of ${entityName} (for Web API $select queries) */`);\r\n lines.push(`export const enum ${enumName} {`);\r\n\r\n const usedNames = new Set<string>();\r\n\r\n for (const attr of includedAttrs) {\r\n const isLookup = isLookupType(attr.AttributeType);\r\n const propertyName = isLookup ? toLookupValueProperty(attr.LogicalName) : attr.LogicalName;\r\n\r\n // Build enum member name from label\r\n const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);\r\n let memberName = labelToPascalMember(primaryLabel);\r\n if (!memberName) {\r\n memberName = toPascalCase(attr.LogicalName);\r\n }\r\n\r\n // Disambiguate\r\n const originalName = memberName;\r\n let counter = 2;\r\n while (usedNames.has(memberName)) {\r\n memberName = `${originalName}${counter}`;\r\n counter++;\r\n }\r\n usedNames.add(memberName);\r\n\r\n // Dual-language JSDoc\r\n const dualLabel = formatDualLabel(attr.DisplayName, labelConfig);\r\n if (dualLabel) {\r\n lines.push(` /** ${dualLabel} */`);\r\n }\r\n lines.push(` ${memberName} = '${propertyName}',`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate a const enum with navigation property names for all lookup fields.\r\n * Used with parseLookup() and $expand queries.\r\n *\r\n * Unlike EntityFields (which uses _fieldname_value format), this enum\r\n * contains the plain LogicalName (= navigation property name for non-polymorphic lookups).\r\n *\r\n * @param info - Complete entity metadata\r\n * @param options - Generator options\r\n * @returns TypeScript declaration string\r\n *\r\n * @example\r\n * ```typescript\r\n * // Generated:\r\n * const enum AccountNavigationProperties {\r\n * Country = 'markant_address1_countryid',\r\n * PrimaryContact = 'primarycontactid',\r\n * }\r\n *\r\n * // Usage:\r\n * parseLookup(response, AccountNav.Country);\r\n * ```\r\n */\r\nexport function generateEntityNavigationProperties(\r\n info: EntityTypeInfo,\r\n options: EntityFieldsGeneratorOptions = {},\r\n): string {\r\n const labelConfig = options.labelConfig || DEFAULT_LABEL_CONFIG;\r\n const entityName = toPascalCase(info.entity.LogicalName);\r\n const enumName = `${entityName}NavigationProperties`;\r\n\r\n // Filter to lookup attributes only\r\n const lookupAttrs = info.attributes\r\n .filter(shouldIncludeInEntityInterface)\r\n .filter((a) => isLookupType(a.AttributeType))\r\n .sort((a, b) => a.LogicalName.localeCompare(b.LogicalName));\r\n\r\n if (lookupAttrs.length === 0) return '';\r\n\r\n const lines: string[] = [];\r\n lines.push(`/** Navigation properties of ${entityName} (for parseLookup and $expand) */`);\r\n lines.push(`export const enum ${enumName} {`);\r\n\r\n const usedNames = new Set<string>();\r\n\r\n for (const attr of lookupAttrs) {\r\n // Build enum member name from label\r\n const primaryLabel = getPrimaryLabel(attr.DisplayName, labelConfig);\r\n let memberName = labelToPascalMember(primaryLabel);\r\n if (!memberName) {\r\n memberName = toPascalCase(attr.LogicalName);\r\n }\r\n\r\n // Disambiguate\r\n const originalName = memberName;\r\n let counter = 2;\r\n while (usedNames.has(memberName)) {\r\n memberName = `${originalName}${counter}`;\r\n counter++;\r\n }\r\n usedNames.add(memberName);\r\n\r\n // Dual-language JSDoc\r\n const dualLabel = formatDualLabel(attr.DisplayName, labelConfig);\r\n if (dualLabel) {\r\n lines.push(` /** ${dualLabel} */`);\r\n }\r\n // Value = LogicalName (NOT _value format)\r\n lines.push(` ${memberName} = '${attr.LogicalName}',`);\r\n }\r\n\r\n lines.push('}');\r\n lines.push('');\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\n * @xrmforge/typegen - Entity Names Enum Generator\n *\n * Generates a single const enum with all entity logical names.\n * Eliminates raw strings in Xrm.WebApi calls.\n *\n * Output pattern:\n * ```typescript\n * declare namespace XrmForge {\n * const enum EntityNames {\n * Account = 'account',\n * Contact = 'contact',\n * Lead = 'lead',\n * }\n * }\n * ```\n */\n\nimport { toPascalCase } from './type-mapping.js';\n\nexport interface EntityNamesGeneratorOptions {\n // Options reserved for future use\n}\n\n/**\n * Generate a const enum mapping entity PascalCase names to logical names.\n *\n * @param entityNames - Array of entity logical names\n * @param options - Generator options\n * @returns TypeScript declaration string\n */\nexport function generateEntityNamesEnum(\n entityNames: string[],\n _options: EntityNamesGeneratorOptions = {},\n): string {\n const sorted = [...entityNames].sort();\n\n const lines: string[] = [];\n lines.push('/** Entity logical names for Xrm.WebApi calls (compile-time only, zero runtime) */');\n lines.push('export const enum EntityNames {');\n\n for (const name of sorted) {\n const pascal = toPascalCase(name);\n lines.push(` ${pascal} = '${name}',`);\n }\n\n lines.push('}');\n lines.push('');\n\n return lines.join('\\n');\n}\n","/**\r\n * @xrmforge/typegen - Custom API Metadata Types\r\n *\r\n * TypeScript interfaces for Dataverse Custom API definitions.\r\n * These types model the JSON structures from the customapi,\r\n * customapirequestparameter, and customapiresponseproperty tables.\r\n *\r\n * Used by the action-generator to produce typed Action/Function executors.\r\n */\r\n\r\n// ─── Custom API Parameter Type ──────────────────────────────────────────────\r\n\r\n/**\r\n * Custom API parameter type (picklist values from customapirequestparameter.type).\r\n *\r\n * Maps to TypeScript types and OData metadata for getMetadata().\r\n */\r\nexport const enum CustomApiParameterType {\r\n Boolean = 0,\r\n DateTime = 1,\r\n Decimal = 2,\r\n Entity = 3,\r\n EntityCollection = 4,\r\n EntityReference = 5,\r\n Float = 6,\r\n Integer = 7,\r\n Money = 8,\r\n Picklist = 9,\r\n String = 10,\r\n StringArray = 11,\r\n Guid = 12,\r\n}\r\n\r\n/**\r\n * Custom API binding type (picklist values from customapi.bindingtype).\r\n */\r\nexport const enum CustomApiBindingType {\r\n /** Nicht an eine Entity gebunden (global aufrufbar) */\r\n Global = 0,\r\n /** An einen einzelnen Entity-Datensatz gebunden */\r\n Entity = 1,\r\n /** An eine Entity-Collection gebunden */\r\n EntityCollection = 2,\r\n}\r\n\r\n// ─── Metadata Interfaces ────────────────────────────────────────────────────\r\n\r\n/** Custom API definition (from the customapi table) */\r\nexport interface CustomApiMetadata {\r\n /** Unique message name (e.g. \"markant_NormalizePhone\") */\r\n uniquename: string;\r\n /** 0=Global, 1=Entity, 2=EntityCollection */\r\n bindingtype: CustomApiBindingType;\r\n /** true = Function (GET), false = Action (POST) */\r\n isfunction: boolean;\r\n /** Bound entity logical name (null for unbound) */\r\n boundentitylogicalname: string | null;\r\n /** Display name for documentation */\r\n displayname?: string;\r\n /** Description for JSDoc */\r\n description?: string;\r\n}\r\n\r\n/** Custom API request parameter (from the customapirequestparameter table) */\r\nexport interface CustomApiRequestParameter {\r\n /** Parameter name (e.g. \"Input\", \"TargetId\") */\r\n uniquename: string;\r\n /** Parameter type (0-12, see CustomApiParameterType) */\r\n type: CustomApiParameterType;\r\n /** Whether the parameter is optional */\r\n isoptional: boolean;\r\n /** Entity logical name (for Entity/EntityReference types) */\r\n logicalentityname?: string | null;\r\n /** Description for JSDoc */\r\n description?: string;\r\n}\r\n\r\n/** Custom API response property (from the customapiresponseproperty table) */\r\nexport interface CustomApiResponseProperty {\r\n /** Property name (e.g. \"Normalized\", \"IsValid\") */\r\n uniquename: string;\r\n /** Property type (0-12, see CustomApiParameterType) */\r\n type: CustomApiParameterType;\r\n /** Entity logical name (for Entity/EntityReference types) */\r\n logicalentityname?: string | null;\r\n /** Description for JSDoc */\r\n description?: string;\r\n}\r\n\r\n/** Complete Custom API definition with parameters and response */\r\nexport interface CustomApiTypeInfo {\r\n api: CustomApiMetadata;\r\n requestParameters: CustomApiRequestParameter[];\r\n responseProperties: CustomApiResponseProperty[];\r\n}\r\n\r\n// ─── Type Mapping ───────────────────────────────────────────────────────────\r\n\r\n/** Mapped type information for code generation */\r\nexport interface MappedParameterType {\r\n /** TypeScript type string (e.g. \"string\", \"number\", \"boolean\") */\r\n tsType: string;\r\n /** OData type name for getMetadata() (e.g. \"Edm.String\", \"mscrm.quote\") */\r\n typeName: string;\r\n /** structuralProperty value for getMetadata() */\r\n structuralProperty: number;\r\n}\r\n\r\n/**\r\n * Map a Custom API parameter type (0-12) to TypeScript + OData metadata.\r\n *\r\n * @param type - Custom API parameter type value\r\n * @param entityName - Entity logical name (for Entity/EntityReference types)\r\n * @returns TypeScript type, OData typeName, and structuralProperty\r\n */\r\nexport function mapCustomApiParameterType(\r\n type: CustomApiParameterType,\r\n entityName?: string | null,\r\n): MappedParameterType {\r\n switch (type) {\r\n case CustomApiParameterType.Boolean:\r\n return { tsType: 'boolean', typeName: 'Edm.Boolean', structuralProperty: 1 };\r\n case CustomApiParameterType.DateTime:\r\n return { tsType: 'string', typeName: 'Edm.DateTimeOffset', structuralProperty: 1 };\r\n case CustomApiParameterType.Decimal:\r\n return { tsType: 'number', typeName: 'Edm.Decimal', structuralProperty: 1 };\r\n case CustomApiParameterType.Entity:\r\n return {\r\n tsType: 'Record<string, unknown>',\r\n typeName: `mscrm.${entityName || 'crmbaseentity'}`,\r\n structuralProperty: 5,\r\n };\r\n case CustomApiParameterType.EntityCollection:\r\n return {\r\n tsType: 'Array<Record<string, unknown>>',\r\n typeName: 'Collection(mscrm.crmbaseentity)',\r\n structuralProperty: 4,\r\n };\r\n case CustomApiParameterType.EntityReference:\r\n return {\r\n tsType: '{ id: string; entityType: string; name?: string }',\r\n typeName: `mscrm.${entityName || 'crmbaseentity'}`,\r\n structuralProperty: 5,\r\n };\r\n case CustomApiParameterType.Float:\r\n return { tsType: 'number', typeName: 'Edm.Double', structuralProperty: 1 };\r\n case CustomApiParameterType.Integer:\r\n return { tsType: 'number', typeName: 'Edm.Int32', structuralProperty: 1 };\r\n case CustomApiParameterType.Money:\r\n return { tsType: 'number', typeName: 'Edm.Decimal', structuralProperty: 1 };\r\n case CustomApiParameterType.Picklist:\r\n return { tsType: 'number', typeName: 'Edm.Int32', structuralProperty: 1 };\r\n case CustomApiParameterType.String:\r\n return { tsType: 'string', typeName: 'Edm.String', structuralProperty: 1 };\r\n case CustomApiParameterType.StringArray:\r\n return { tsType: 'string[]', typeName: 'Collection(Edm.String)', structuralProperty: 4 };\r\n case CustomApiParameterType.Guid:\r\n return { tsType: 'string', typeName: 'Edm.Guid', structuralProperty: 1 };\r\n default:\r\n return { tsType: 'unknown', typeName: 'Edm.String', structuralProperty: 0 };\r\n }\r\n}\r\n","/**\r\n * @xrmforge/typegen - Action/Function Generator\r\n *\r\n * Generates TypeScript files for type-safe Custom API execution:\r\n * - .d.ts: Parameter/Response interfaces and executor types\r\n * - .ts: Runtime modules that import factory functions from @xrmforge/helpers\r\n *\r\n * Input: CustomApiTypeInfo[] (from fixture JSON or live Dataverse query)\r\n * Output: Grouped by entity (bound) or \"global\" (unbound)\r\n *\r\n * @example Generated output for markant_NormalizePhone (unbound action):\r\n * ```typescript\r\n * // global.d.ts\r\n * declare namespace XrmForge.Actions {\r\n * interface NormalizePhoneParams { Input: string; AllowSuspicious?: boolean; }\r\n * interface NormalizePhoneResult { Normalized: string; Status: number; Message: string; }\r\n * }\r\n *\r\n * // global.ts\r\n * import { createUnboundAction } from '@xrmforge/helpers';\r\n * export const NormalizePhone = createUnboundAction<...>('markant_NormalizePhone', { ... });\r\n * ```\r\n */\r\n\r\nimport type {\r\n CustomApiTypeInfo,\r\n CustomApiRequestParameter,\r\n CustomApiResponseProperty,\r\n} from '../metadata/custom-api-types.js';\r\nimport { mapCustomApiParameterType, CustomApiBindingType } from '../metadata/custom-api-types.js';\r\nimport { toPascalCase } from './type-mapping.js';\r\n\r\n// ─── Options ────────────────────────────────────────────────────────────────\r\n\r\n/** Options for action/function generation */\r\nexport interface ActionGeneratorOptions {\r\n /** Import path for @xrmforge/helpers (default: \"@xrmforge/helpers\") */\r\n importPath?: string;\r\n}\r\n\r\n// ─── Naming ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Derive a clean PascalCase name from the Custom API uniquename.\r\n * Removes common prefixes like \"markant_\", \"dev1_\" etc.\r\n *\r\n * Examples:\r\n * - \"markant_NormalizePhone\" -> \"NormalizePhone\"\r\n * - \"markant_winquote\" -> \"WinQuote\"\r\n * - \"WhoAmI\" -> \"WhoAmI\"\r\n */\r\nfunction deriveActionName(uniquename: string): string {\r\n // Entferne Publisher-Prefix (alles vor und inklusive dem ersten Unterstrich)\r\n const withoutPrefix = uniquename.includes('_')\r\n ? uniquename.substring(uniquename.indexOf('_') + 1)\r\n : uniquename;\r\n return toPascalCase(withoutPrefix);\r\n}\r\n\r\n// ─── Interface Generation ───────────────────────────────────────────────────\r\n\r\nfunction generateParamsInterface(\r\n name: string,\r\n params: CustomApiRequestParameter[],\r\n): string {\r\n if (params.length === 0) return '';\r\n\r\n const lines: string[] = [];\r\n lines.push(`export interface ${name}Params {`);\r\n\r\n for (const param of params) {\r\n const mapped = mapCustomApiParameterType(param.type, param.logicalentityname);\r\n const optional = param.isoptional ? '?' : '';\r\n if (param.description) {\r\n lines.push(` /** ${param.description} */`);\r\n }\r\n lines.push(` ${param.uniquename}${optional}: ${mapped.tsType};`);\r\n }\r\n\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n\r\nfunction generateResultInterface(\r\n name: string,\r\n props: CustomApiResponseProperty[],\r\n): string {\r\n if (props.length === 0) return '';\r\n\r\n const lines: string[] = [];\r\n lines.push(`export interface ${name}Result {`);\r\n\r\n for (const prop of props) {\r\n const mapped = mapCustomApiParameterType(prop.type, prop.logicalentityname);\r\n if (prop.description) {\r\n lines.push(` /** ${prop.description} */`);\r\n }\r\n lines.push(` ${prop.uniquename}: ${mapped.tsType};`);\r\n }\r\n\r\n lines.push('}');\r\n return lines.join('\\n');\r\n}\r\n\r\n// ─── Declaration (.d.ts) Generation ─────────────────────────────────────────\r\n\r\n/**\r\n * Generate declarations (interfaces only) for a group of Custom APIs.\r\n * Output is flat ES module style with `export interface`.\r\n *\r\n * @param apis - Custom APIs to generate (all in the same group: same entity or all global)\r\n * @param isFunction - true for functions, false for actions\r\n * @param _entityName - Entity name for bound APIs, undefined for global (reserved for future use)\r\n * @param _options - Generator options (reserved for future use)\r\n */\r\nexport function generateActionDeclarations(\r\n apis: CustomApiTypeInfo[],\r\n _isFunction: boolean,\r\n _entityName?: string,\r\n _options: ActionGeneratorOptions = {},\r\n): string {\r\n const lines: string[] = [];\r\n lines.push('// Auto-generated by @xrmforge/typegen - DO NOT EDIT');\r\n lines.push('');\r\n\r\n for (const apiInfo of apis) {\r\n const name = deriveActionName(apiInfo.api.uniquename);\r\n const hasParams = apiInfo.requestParameters.length > 0;\r\n const hasResult = apiInfo.responseProperties.length > 0;\r\n\r\n // JSDoc\r\n const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;\r\n lines.push(`/** ${description} (${apiInfo.api.uniquename}) */`);\r\n\r\n // Params interface\r\n if (hasParams) {\r\n lines.push(generateParamsInterface(name, apiInfo.requestParameters));\r\n lines.push('');\r\n }\r\n\r\n // Result interface\r\n if (hasResult) {\r\n lines.push(generateResultInterface(name, apiInfo.responseProperties));\r\n lines.push('');\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n// ─── Runtime Module (.ts) Generation ────────────────────────────────────────\r\n\r\n/**\r\n * Generate a single .ts file with both interfaces and runtime executors for a group of Custom APIs.\r\n * Combines what was previously split into .d.ts (interfaces) and .ts (executors).\r\n *\r\n * @param apis - Custom APIs to generate\r\n * @param isFunction - true for functions, false for actions\r\n * @param options - Generator options\r\n */\r\nexport function generateActionModule(\r\n apis: CustomApiTypeInfo[],\r\n isFunction: boolean,\r\n options: ActionGeneratorOptions = {},\r\n): string {\r\n const importPath = options.importPath || '@xrmforge/helpers';\r\n const lines: string[] = [];\r\n\r\n lines.push('// Auto-generated by @xrmforge/typegen - DO NOT EDIT');\r\n lines.push('');\r\n\r\n // Collect needed factory imports\r\n const imports = new Set<string>();\r\n for (const apiInfo of apis) {\r\n if (apiInfo.api.bindingtype === CustomApiBindingType.Global) {\r\n if (isFunction) {\r\n imports.add('createUnboundFunction');\r\n } else {\r\n imports.add('createUnboundAction');\r\n }\r\n } else {\r\n if (isFunction) {\r\n imports.add('createBoundFunction');\r\n } else {\r\n imports.add('createBoundAction');\r\n }\r\n }\r\n }\r\n\r\n lines.push(`import { ${[...imports].sort().join(', ')} } from '${importPath}';`);\r\n lines.push('');\r\n\r\n for (const apiInfo of apis) {\r\n const name = deriveActionName(apiInfo.api.uniquename);\r\n const hasParams = apiInfo.requestParameters.length > 0;\r\n const hasResult = apiInfo.responseProperties.length > 0;\r\n\r\n // JSDoc\r\n const description = apiInfo.api.description || apiInfo.api.displayname || apiInfo.api.uniquename;\r\n lines.push(`/** ${description} (${apiInfo.api.uniquename}) */`);\r\n\r\n // Params interface (inline, exported)\r\n if (hasParams) {\r\n lines.push(generateParamsInterface(name, apiInfo.requestParameters));\r\n lines.push('');\r\n }\r\n\r\n // Result interface (inline, exported)\r\n if (hasResult) {\r\n lines.push(generateResultInterface(name, apiInfo.responseProperties));\r\n lines.push('');\r\n }\r\n\r\n // Executor export with type parameters for Params and Result types\r\n const typeArgs = buildTypeArgs(name, hasParams, hasResult);\r\n\r\n if (apiInfo.api.bindingtype === CustomApiBindingType.Global) {\r\n // Unbound\r\n if (isFunction) {\r\n const funcTypeArg = hasResult ? `<${name}Result>` : '<unknown>';\r\n lines.push(`export const ${name} = createUnboundFunction${funcTypeArg}('${apiInfo.api.uniquename}');`);\r\n } else if (hasParams) {\r\n const paramMeta = generateParameterMetaMap(apiInfo.requestParameters);\r\n lines.push(`export const ${name} = createUnboundAction${typeArgs}('${apiInfo.api.uniquename}', ${paramMeta});`);\r\n } else if (hasResult) {\r\n lines.push(`export const ${name} = createUnboundAction<${name}Result>('${apiInfo.api.uniquename}');`);\r\n } else {\r\n lines.push(`export const ${name} = createUnboundAction('${apiInfo.api.uniquename}');`);\r\n }\r\n } else {\r\n // Bound\r\n const entity = apiInfo.api.boundentitylogicalname!;\r\n if (isFunction) {\r\n const funcTypeArg = hasResult ? `<${name}Result>` : '<unknown>';\r\n lines.push(`export const ${name} = createBoundFunction${funcTypeArg}('${apiInfo.api.uniquename}', '${entity}');`);\r\n } else if (hasParams) {\r\n const paramMeta = generateParameterMetaMap(apiInfo.requestParameters);\r\n lines.push(`export const ${name} = createBoundAction${typeArgs}('${apiInfo.api.uniquename}', '${entity}', ${paramMeta});`);\r\n } else if (hasResult) {\r\n lines.push(`export const ${name} = createBoundAction<${name}Result>('${apiInfo.api.uniquename}', '${entity}');`);\r\n } else {\r\n lines.push(`export const ${name} = createBoundAction('${apiInfo.api.uniquename}', '${entity}');`);\r\n }\r\n }\r\n\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n\r\n// ─── Type Argument Builder ──────────────────────────────────────────────────\r\n\r\n/**\r\n * Build the generic type argument string for factory function calls.\r\n *\r\n * For actions with params: `<NameParams>` or `<NameParams, NameResult>`\r\n * For actions without params but with result: `<NameResult>`\r\n * For actions without params or result: empty string (no generics)\r\n */\r\nfunction buildTypeArgs(name: string, hasParams: boolean, hasResult: boolean): string {\r\n if (hasParams && hasResult) {\r\n return `<${name}Params, ${name}Result>`;\r\n }\r\n if (hasParams) {\r\n return `<${name}Params>`;\r\n }\r\n if (hasResult) {\r\n return `<${name}Result>`;\r\n }\r\n return '';\r\n}\r\n\r\n// ─── Parameter Metadata Map ─────────────────────────────────────────────────\r\n\r\nfunction generateParameterMetaMap(params: CustomApiRequestParameter[]): string {\r\n const entries: string[] = [];\r\n for (const param of params) {\r\n const mapped = mapCustomApiParameterType(param.type, param.logicalentityname);\r\n entries.push(\r\n ` ${param.uniquename}: { typeName: '${mapped.typeName}', structuralProperty: ${mapped.structuralProperty} }`,\r\n );\r\n }\r\n return `{\\n${entries.join(',\\n')},\\n}`;\r\n}\r\n\r\n// ─── Grouping Helper ────────────────────────────────────────────────────────\r\n\r\n/** Group result: key is entity logical name or \"global\" for unbound */\r\nexport interface GroupedCustomApis {\r\n actions: Map<string, CustomApiTypeInfo[]>;\r\n functions: Map<string, CustomApiTypeInfo[]>;\r\n}\r\n\r\n/**\r\n * Group Custom APIs by entity (bound) or \"global\" (unbound),\r\n * and separate actions from functions.\r\n */\r\nexport function groupCustomApis(apis: CustomApiTypeInfo[]): GroupedCustomApis {\r\n const actions = new Map<string, CustomApiTypeInfo[]>();\r\n const functions = new Map<string, CustomApiTypeInfo[]>();\r\n\r\n for (const api of apis) {\r\n const target = api.api.isfunction ? functions : actions;\r\n const key = api.api.bindingtype === CustomApiBindingType.Global\r\n ? 'global'\r\n : (api.api.boundentitylogicalname || 'global');\r\n\r\n if (!target.has(key)) {\r\n target.set(key, []);\r\n }\r\n target.get(key)!.push(api);\r\n }\r\n\r\n return { actions, functions };\r\n}\r\n","/**\r\n * @xrmforge/typegen - File Writer\r\n *\r\n * Writes generated .ts files to disk with proper directory structure:\r\n *\r\n * outputDir/\r\n * entities/\r\n * account.ts\r\n * contact.ts\r\n * optionsets/\r\n * account.ts (all OptionSets for one entity)\r\n * forms/\r\n * account.ts (all form interfaces for one entity)\r\n * fields/\r\n * account.ts (FieldsEnum + NavigationProperties)\r\n * index.ts (barrel re-export file)\r\n */\r\n\r\nimport { mkdir, writeFile, readFile, unlink } from 'node:fs/promises';\r\nimport { join, dirname } from 'node:path';\r\nimport type { GeneratedFile } from './types.js';\r\n\r\n/**\r\n * Write a generated file to disk, creating directories as needed.\r\n *\r\n * @param outputDir - Base output directory\r\n * @param file - Generated file to write\r\n * @returns true if file was written (content changed), false if unchanged\r\n */\r\nexport async function writeGeneratedFile(outputDir: string, file: GeneratedFile): Promise<boolean> {\r\n const absolutePath = join(outputDir, file.relativePath);\r\n\r\n // Check if file already exists with same content (avoid unnecessary writes)\r\n try {\r\n const existing = await readFile(absolutePath, 'utf-8');\r\n if (existing === file.content) {\r\n return false; // No change\r\n }\r\n } catch {\r\n // File doesn't exist yet, proceed with write\r\n }\r\n\r\n // Ensure directory exists\r\n await mkdir(dirname(absolutePath), { recursive: true });\r\n\r\n // Write file\r\n await writeFile(absolutePath, file.content, 'utf-8');\r\n\r\n return true;\r\n}\r\n\r\n/** Result of writing a batch of files */\r\nexport interface WriteResult {\r\n /** Number of files successfully written (content changed) */\r\n written: number;\r\n /** Number of files unchanged (already up to date) */\r\n unchanged: number;\r\n /** Warnings for files that could not be written (non-fatal) */\r\n warnings: string[];\r\n}\r\n\r\n/**\r\n * Write all generated files to disk.\r\n * A single file write failure does NOT abort the entire batch.\r\n * Errors are collected as warnings so the remaining files are still written.\r\n *\r\n * @param outputDir - Base output directory\r\n * @param files - Array of generated files\r\n * @returns WriteResult with counts and warnings\r\n */\r\nexport async function writeAllFiles(outputDir: string, files: GeneratedFile[]): Promise<WriteResult> {\r\n const result: WriteResult = { written: 0, unchanged: 0, warnings: [] };\r\n\r\n for (const file of files) {\r\n try {\r\n const changed = await writeGeneratedFile(outputDir, file);\r\n if (changed) {\r\n result.written++;\r\n } else {\r\n result.unchanged++;\r\n }\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n result.warnings.push(`Konnte ${file.relativePath} nicht schreiben: ${message}`);\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Delete orphaned .d.ts files for entities that were removed from Dataverse.\r\n * Removes entity, optionset, and form files for the given entity names.\r\n *\r\n * @param outputDir - Base output directory\r\n * @param deletedEntityNames - Entity logical names whose files should be removed\r\n * @returns Number of files deleted\r\n */\r\nexport async function deleteOrphanedFiles(outputDir: string, deletedEntityNames: string[]): Promise<number> {\r\n let deleted = 0;\r\n const subdirs = ['entities', 'optionsets', 'forms', 'fields'];\r\n\r\n for (const entityName of deletedEntityNames) {\r\n for (const subdir of subdirs) {\r\n const filePath = join(outputDir, subdir, `${entityName}.ts`);\r\n try {\r\n await unlink(filePath);\r\n deleted++;\r\n } catch (error: unknown) {\r\n // File might not exist (entity had no forms/optionsets), that's fine\r\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\r\n throw error;\r\n }\r\n }\r\n }\r\n }\r\n\r\n return deleted;\r\n}\r\n\r\n/** File header for generated files */\r\nconst GENERATED_HEADER = `// ──────────────────────────────────────────────────────────────────────────────\r\n// This file was generated by @xrmforge/typegen. Do not edit manually.\r\n// Re-run 'xrmforge generate' to update.\r\n// ──────────────────────────────────────────────────────────────────────────────\r\n\r\n`;\r\n\r\n/**\r\n * Wrap generated content with the standard header.\r\n */\r\nexport function addGeneratedHeader(content: string): string {\r\n return GENERATED_HEADER + content;\r\n}\r\n\r\n/**\r\n * Convert a relative .ts path to an ESM import specifier with .js extension.\r\n * TypeScript ESM convention: import specifiers use .js, resolved to .ts at compile time.\r\n *\r\n * @example toImportSpecifier('entities/account.ts') => './entities/account.js'\r\n */\r\nfunction toImportSpecifier(relativePath: string): string {\r\n const withoutExt = relativePath.replace(/\\.ts$/, '');\r\n return `./${withoutExt}.js`;\r\n}\r\n\r\n/**\r\n * Generate a barrel index.ts that re-exports all generated files.\r\n *\r\n * @param files - All generated files\r\n * @returns Content for index.ts\r\n */\r\nexport function generateBarrelIndex(files: GeneratedFile[]): string {\r\n const lines: string[] = [GENERATED_HEADER];\r\n\r\n // Group by type for organized output\r\n const entities = files.filter((f) => f.type === 'entity');\r\n const optionsets = files.filter((f) => f.type === 'optionset');\r\n const forms = files.filter((f) => f.type === 'form');\r\n const fields = files.filter((f) => f.type === 'fields');\r\n const actions = files.filter((f) => f.type === 'action');\r\n\r\n if (entities.length > 0) {\r\n lines.push('// Entity Interfaces');\r\n for (const f of entities) {\r\n lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (optionsets.length > 0) {\r\n lines.push('// OptionSet Enums - import directly from individual files to avoid name conflicts:');\r\n for (const f of optionsets) {\r\n lines.push(`// import { ... } from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (forms.length > 0) {\r\n lines.push('// Form Interfaces');\r\n for (const f of forms) {\r\n lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (fields.length > 0) {\r\n lines.push('// Entity Fields & Navigation Properties - import directly from individual files to avoid name conflicts:');\r\n for (const f of fields) {\r\n lines.push(`// import { ... } from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (actions.length > 0) {\r\n lines.push('// Custom API Actions & Functions');\r\n for (const f of actions) {\r\n lines.push(`export * from '${toImportSpecifier(f.relativePath)}';`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n');\r\n}\r\n","/**\r\n * @xrmforge/typegen - Type Generation Orchestrator\r\n *\r\n * Coordinates the full type generation pipeline:\r\n * 1. Fetch metadata for requested entities (via MetadataClient)\r\n * 2. Generate entity interfaces, OptionSet enums, form interfaces, fields enums\r\n * 3. Write .ts files to disk\r\n *\r\n * This is the main entry point that ties all components together.\r\n */\r\n\r\nimport type { TokenCredential } from '@azure/identity';\r\nimport { MetadataClient } from '../metadata/client.js';\r\nimport { MetadataCache } from '../metadata/cache.js';\r\nimport { ChangeDetector } from '../metadata/change-detector.js';\r\nimport { DataverseHttpClient } from '../http/client.js';\r\nimport { ErrorCode } from '../errors.js';\r\nimport { createLogger, type Logger } from '../logger.js';\r\nimport type { EntityTypeInfo, OptionSetMetadata } from '../metadata/types.js';\r\nimport { generateEntityInterface } from '../generators/entity-generator.js';\r\nimport { generateEntityOptionSets } from '../generators/optionset-generator.js';\r\nimport { generateEntityForms } from '../generators/form-generator.js';\r\nimport { generateActionModule, groupCustomApis } from '../generators/action-generator.js';\r\nimport { generateEntityFieldsEnum, generateEntityNavigationProperties } from '../generators/entity-fields-generator.js';\r\nimport { generateEntityNamesEnum } from '../generators/entity-names-generator.js';\r\nimport { generateActivityPartyInterface } from '../generators/activity-party.js';\r\nimport { isPartyListType } from '../generators/type-mapping.js';\r\nimport { addGeneratedHeader, writeAllFiles, generateBarrelIndex, deleteOrphanedFiles } from './file-writer.js';\r\nimport type {\r\n GenerateConfig,\r\n GenerationResult,\r\n EntityGenerationResult,\r\n GeneratedFile,\r\n CacheStats,\r\n} from './types.js';\r\n\r\n/**\r\n * Main orchestrator for type generation.\r\n *\r\n * Usage:\r\n * ```typescript\r\n * const orchestrator = new TypeGenerationOrchestrator(credential, {\r\n * environmentUrl: 'https://myorg.crm4.dynamics.com',\r\n * entities: ['account', 'contact'],\r\n * outputDir: './generated',\r\n * labelConfig: { primaryLanguage: 1033, secondaryLanguage: 1031 },\r\n * });\r\n * const result = await orchestrator.generate();\r\n * ```\r\n */\r\nexport class TypeGenerationOrchestrator {\r\n private readonly config: Required<GenerateConfig>;\r\n private readonly credential: TokenCredential;\r\n private readonly logger: Logger;\r\n\r\n constructor(credential: TokenCredential, config: GenerateConfig, logger?: Logger) {\r\n this.credential = credential;\r\n this.logger = logger ?? createLogger('orchestrator');\r\n\r\n // Apply defaults\r\n this.config = {\r\n environmentUrl: config.environmentUrl,\r\n entities: [...config.entities],\r\n solutionNames: config.solutionNames ?? [],\r\n outputDir: config.outputDir,\r\n labelConfig: config.labelConfig,\r\n generateEntities: config.generateEntities ?? true,\r\n generateForms: config.generateForms ?? true,\r\n generateOptionSets: config.generateOptionSets ?? true,\r\n generateActions: config.generateActions ?? false,\r\n actionsFilter: config.actionsFilter ?? '',\r\n useCache: config.useCache ?? false,\r\n cacheDir: config.cacheDir ?? '.xrmforge/cache',\r\n namespacePrefix: config.namespacePrefix ?? 'XrmForge',\r\n };\r\n }\r\n\r\n /**\r\n * Run the full type generation pipeline.\r\n *\r\n * @param options - Optional parameters\r\n * @param options.signal - AbortSignal to cancel the generation process.\r\n * When aborted, entities that have not yet started processing are skipped.\r\n * Entities already in progress may still complete or fail with an abort error.\r\n */\r\n async generate(options?: { signal?: AbortSignal }): Promise<GenerationResult> {\r\n const signal = options?.signal;\r\n const startTime = Date.now();\r\n const allFiles: GeneratedFile[] = [];\r\n const entityResults: EntityGenerationResult[] = [];\r\n\r\n // Check abort before starting\r\n if (signal?.aborted) {\r\n return { entities: [], totalFiles: 0, totalWarnings: 0, durationMs: 0 };\r\n }\r\n\r\n this.logger.info('Starting type generation', {\r\n entities: this.config.entities,\r\n outputDir: this.config.outputDir,\r\n useCache: this.config.useCache,\r\n });\r\n\r\n // 1. Create HTTP client and metadata client\r\n const httpClient = new DataverseHttpClient({\r\n environmentUrl: this.config.environmentUrl,\r\n credential: this.credential,\r\n });\r\n\r\n const metadataClient = new MetadataClient(httpClient);\r\n\r\n // 1b. Resolve solution entities if solutionNames is set\r\n if (this.config.solutionNames && this.config.solutionNames.length > 0) {\r\n this.logger.info(`Resolving entities from ${this.config.solutionNames.length} solution(s): ${this.config.solutionNames.join(', ')}`);\r\n const solutionEntityNames = await metadataClient.getEntityNamesForSolutions(this.config.solutionNames);\r\n this.logger.info(`Found ${solutionEntityNames.length} entities in solution(s)`);\r\n // Merge solution entities with manually specified entities, deduplicate\r\n const merged = new Set([...this.config.entities, ...solutionEntityNames]);\r\n this.config.entities = [...merged].sort();\r\n }\r\n\r\n if (this.config.entities.length === 0) {\r\n this.logger.warn('No entities to process. Check --entities or --solutions.');\r\n return {\r\n entities: [],\r\n totalFiles: 0,\r\n totalWarnings: 1,\r\n durationMs: Date.now() - startTime,\r\n };\r\n }\r\n\r\n // 2. Determine cache strategy\r\n let cacheStats: CacheStats | undefined;\r\n const entitiesToFetch = new Set<string>(this.config.entities);\r\n const cachedEntityInfos: Record<string, EntityTypeInfo> = {};\r\n let cache: MetadataCache | undefined;\r\n let newVersionStamp: string | null = null;\r\n const deletedEntityNames: string[] = [];\r\n\r\n if (this.config.useCache) {\r\n const cacheResult = await this.resolveCache(httpClient, entitiesToFetch);\r\n cache = cacheResult.cache;\r\n newVersionStamp = cacheResult.newVersionStamp;\r\n cacheStats = cacheResult.stats;\r\n\r\n // Move cached entities out of the fetch set\r\n for (const [name, info] of Object.entries(cacheResult.cachedEntities)) {\r\n cachedEntityInfos[name] = info;\r\n entitiesToFetch.delete(name);\r\n }\r\n deletedEntityNames.push(...cacheResult.deletedEntityNames);\r\n }\r\n\r\n // 3. Fetch metadata for entities that need fetching (parallel, R7-07)\r\n const fetchList = [...entitiesToFetch];\r\n const failedEntities = new Map<string, string>(); // entityName -> errorMsg\r\n if (fetchList.length > 0) {\r\n this.logger.info(`Fetching ${fetchList.length} entities from Dataverse`);\r\n\r\n const settled = await Promise.allSettled(\r\n fetchList.map((entityName) => {\r\n if (signal?.aborted) {\r\n return Promise.reject(new Error('Generation aborted'));\r\n }\r\n return metadataClient.getEntityTypeInfo(entityName).then((info) => {\r\n this.logger.info(`Fetched entity: ${entityName}`);\r\n return { entityName, info };\r\n });\r\n }),\r\n );\r\n\r\n for (let i = 0; i < settled.length; i++) {\r\n const outcome = settled[i]!;\r\n const entityName = fetchList[i]!;\r\n\r\n if (outcome.status === 'fulfilled') {\r\n cachedEntityInfos[outcome.value.entityName] = outcome.value.info;\r\n } else {\r\n const errorMsg = outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason);\r\n this.logger.error(`Failed to fetch entity: ${entityName}`, { error: outcome.reason });\r\n failedEntities.set(entityName, errorMsg);\r\n }\r\n }\r\n }\r\n\r\n // 3b. Generate ActivityParty interface if any entity has PartyList attributes\r\n if (this.config.generateEntities) {\r\n const hasPartyList = Object.values(cachedEntityInfos).some((info) =>\r\n info.attributes.some((a) => isPartyListType(a.AttributeType)),\r\n );\r\n if (hasPartyList) {\r\n const activityPartyContent = generateActivityPartyInterface();\r\n allFiles.push({\r\n relativePath: 'entities/_activity-party.ts',\r\n content: addGeneratedHeader(activityPartyContent),\r\n type: 'entity',\r\n });\r\n }\r\n }\r\n\r\n // 3c. Generate types for all entities in order (cached + freshly fetched)\r\n this.logger.info(`Generating types for ${this.config.entities.length - failedEntities.size} entities`);\r\n\r\n for (const entityName of this.config.entities) {\r\n if (signal?.aborted) break;\r\n\r\n // Check if fetch failed for this entity\r\n if (failedEntities.has(entityName)) {\r\n entityResults.push({\r\n entityLogicalName: entityName,\r\n files: [],\r\n warnings: [`Failed to process: ${failedEntities.get(entityName)}`],\r\n });\r\n continue;\r\n }\r\n\r\n const entityInfo = cachedEntityInfos[entityName];\r\n if (!entityInfo) continue; // Should not happen, but safety guard\r\n\r\n const result = this.generateEntityFiles(entityName, entityInfo);\r\n this.logger.info(`Generated entity: ${entityName} (${result.files.length} files)`);\r\n entityResults.push(result);\r\n allFiles.push(...result.files);\r\n }\r\n\r\n // 3d. Generate Custom API Action/Function executors\r\n if (this.config.generateActions && !signal?.aborted) {\r\n const actionFiles = await this.generateActions(metadataClient);\r\n allFiles.push(...actionFiles);\r\n }\r\n\r\n // 3e. Generate EntityNames enum (all entities in one file)\r\n if (this.config.entities.length > 0) {\r\n const entityNamesContent = generateEntityNamesEnum(this.config.entities);\r\n allFiles.push({\r\n relativePath: 'entity-names.ts',\r\n content: addGeneratedHeader(entityNamesContent),\r\n type: 'entity',\r\n });\r\n }\r\n\r\n // 4. Write barrel index\r\n if (allFiles.length > 0) {\r\n const indexContent = generateBarrelIndex(allFiles);\r\n const indexFile: GeneratedFile = {\r\n relativePath: 'index.ts',\r\n content: indexContent,\r\n type: 'entity',\r\n };\r\n allFiles.push(indexFile);\r\n }\r\n\r\n // 5. Write all files to disk (non-fatal: file write errors become warnings)\r\n const writeResult = await writeAllFiles(this.config.outputDir, allFiles);\r\n\r\n // 5b. Delete orphaned .d.ts files for deleted entities\r\n if (deletedEntityNames.length > 0) {\r\n const deleted = await deleteOrphanedFiles(this.config.outputDir, deletedEntityNames);\r\n if (deleted > 0) {\r\n this.logger.info(`Deleted ${deleted} orphaned files for removed entities`);\r\n }\r\n }\r\n\r\n // 6. Update cache with new data\r\n if (this.config.useCache && cache) {\r\n await this.updateCache(cache, cachedEntityInfos, deletedEntityNames, newVersionStamp);\r\n }\r\n\r\n const durationMs = Date.now() - startTime;\r\n const entityWarnings = entityResults.reduce((sum, r) => sum + r.warnings.length, 0);\r\n const totalWarnings = entityWarnings + writeResult.warnings.length;\r\n\r\n for (const w of writeResult.warnings) {\r\n this.logger.warn(w);\r\n }\r\n\r\n this.logger.info('Type generation complete', {\r\n entities: entityResults.length,\r\n filesWritten: writeResult.written,\r\n filesUnchanged: writeResult.unchanged,\r\n totalFiles: allFiles.length,\r\n totalWarnings,\r\n durationMs,\r\n cacheUsed: cacheStats?.cacheUsed ?? false,\r\n });\r\n\r\n return {\r\n entities: entityResults,\r\n totalFiles: allFiles.length,\r\n totalWarnings,\r\n durationMs,\r\n cacheStats,\r\n };\r\n }\r\n\r\n /**\r\n * Resolve the cache: load existing cache, detect changes, determine which\r\n * entities need to be fetched vs. can be served from cache.\r\n *\r\n * On any failure (corrupt cache, expired stamp), falls back to full refresh.\r\n */\r\n private async resolveCache(\r\n httpClient: DataverseHttpClient,\r\n requestedEntities: Set<string>,\r\n ): Promise<{\r\n cache: MetadataCache;\r\n cachedEntities: Record<string, EntityTypeInfo>;\r\n deletedEntityNames: string[];\r\n newVersionStamp: string | null;\r\n stats: CacheStats;\r\n }> {\r\n const cache = new MetadataCache(this.config.cacheDir);\r\n const changeDetector = new ChangeDetector(httpClient);\r\n\r\n // Try to load existing cache\r\n let cacheData;\r\n try {\r\n cacheData = await cache.load(this.config.environmentUrl);\r\n } catch {\r\n this.logger.warn('Failed to load metadata cache, performing full refresh');\r\n cacheData = null;\r\n }\r\n\r\n if (!cacheData || !cacheData.manifest.serverVersionStamp) {\r\n // No cache or no stamp: full refresh, but get initial stamp for next run\r\n this.logger.info('No valid cache found, performing full metadata refresh');\r\n const stamp = await changeDetector.getInitialVersionStamp();\r\n return {\r\n cache,\r\n cachedEntities: {},\r\n deletedEntityNames: [],\r\n newVersionStamp: stamp,\r\n stats: {\r\n cacheUsed: true,\r\n fullRefresh: true,\r\n entitiesFromCache: 0,\r\n entitiesFetched: requestedEntities.size,\r\n entitiesDeleted: 0,\r\n },\r\n };\r\n }\r\n\r\n // Cache exists with stamp: try delta detection\r\n let changeResult;\r\n try {\r\n changeResult = await changeDetector.detectChanges(cacheData.manifest.serverVersionStamp);\r\n } catch (error: unknown) {\r\n // Expired stamp or other error: fall back to full refresh\r\n const isExpired = error instanceof Error &&\r\n error.message.includes(ErrorCode.META_VERSION_STAMP_EXPIRED);\r\n if (isExpired) {\r\n this.logger.warn('Cache version stamp expired (>90 days), performing full refresh');\r\n } else {\r\n this.logger.warn('Change detection failed, performing full refresh', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n const stamp = await changeDetector.getInitialVersionStamp();\r\n return {\r\n cache,\r\n cachedEntities: {},\r\n deletedEntityNames: [],\r\n newVersionStamp: stamp,\r\n stats: {\r\n cacheUsed: true,\r\n fullRefresh: true,\r\n entitiesFromCache: 0,\r\n entitiesFetched: requestedEntities.size,\r\n entitiesDeleted: 0,\r\n },\r\n };\r\n }\r\n\r\n // Delta detection succeeded: determine which entities to fetch\r\n const changedSet = new Set(changeResult.changedEntityNames);\r\n const cachedEntities: Record<string, EntityTypeInfo> = {};\r\n let entitiesFromCache = 0;\r\n let entitiesFetched = 0;\r\n\r\n for (const entityName of requestedEntities) {\r\n if (changedSet.has(entityName) || !cacheData.entityTypeInfos[entityName]) {\r\n // Entity changed or not in cache: needs fresh fetch\r\n entitiesFetched++;\r\n } else {\r\n // Entity unchanged and in cache: use cached version\r\n cachedEntities[entityName] = cacheData.entityTypeInfos[entityName]!;\r\n entitiesFromCache++;\r\n }\r\n }\r\n\r\n // Deleted entities that were in our entity list\r\n const deletedEntityNames = changeResult.deletedEntityNames.filter(\r\n (name) => requestedEntities.has(name),\r\n );\r\n\r\n this.logger.info(`Cache delta: ${entitiesFromCache} from cache, ${entitiesFetched} to fetch, ${deletedEntityNames.length} deleted`);\r\n\r\n return {\r\n cache,\r\n cachedEntities,\r\n deletedEntityNames,\r\n newVersionStamp: changeResult.newVersionStamp,\r\n stats: {\r\n cacheUsed: true,\r\n fullRefresh: false,\r\n entitiesFromCache,\r\n entitiesFetched,\r\n entitiesDeleted: deletedEntityNames.length,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Update the metadata cache after a successful generation run.\r\n */\r\n private async updateCache(\r\n cache: MetadataCache,\r\n entityTypeInfos: Record<string, EntityTypeInfo>,\r\n deletedEntityNames: string[],\r\n newVersionStamp: string | null,\r\n ): Promise<void> {\r\n try {\r\n if (deletedEntityNames.length > 0) {\r\n await cache.removeEntities(this.config.environmentUrl, deletedEntityNames, newVersionStamp);\r\n }\r\n await cache.save(this.config.environmentUrl, entityTypeInfos, newVersionStamp);\r\n this.logger.info('Metadata cache updated');\r\n } catch (error: unknown) {\r\n // Cache update failure is non-fatal\r\n this.logger.warn('Failed to update metadata cache', {\r\n error: error instanceof Error ? error.message : String(error),\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Generate all output files for a single entity from its metadata.\r\n */\r\n private generateEntityFiles(\r\n entityName: string,\r\n entityInfo: EntityTypeInfo,\r\n ): EntityGenerationResult {\r\n const warnings: string[] = [];\r\n const files: GeneratedFile[] = [];\r\n\r\n // Generate entity interface\r\n if (this.config.generateEntities) {\r\n const entityContent = generateEntityInterface(entityInfo, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n files.push({\r\n relativePath: `entities/${entityName}.ts`,\r\n content: addGeneratedHeader(entityContent),\r\n type: 'entity',\r\n });\r\n }\r\n\r\n // Generate OptionSet enums\r\n if (this.config.generateOptionSets) {\r\n const picklistAttrs = this.getPicklistAttributes(entityInfo);\r\n\r\n if (picklistAttrs.length > 0) {\r\n const optionSets = generateEntityOptionSets(picklistAttrs, entityName, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n\r\n if (optionSets.length > 0) {\r\n // Combine all OptionSets for one entity into a single file\r\n const combinedContent = optionSets.map((os) => os.content).join('\\n');\r\n files.push({\r\n relativePath: `optionsets/${entityName}.ts`,\r\n content: addGeneratedHeader(combinedContent),\r\n type: 'optionset',\r\n });\r\n }\r\n } else {\r\n warnings.push(`No OptionSet attributes found for ${entityName}`);\r\n }\r\n }\r\n\r\n // Generate form interfaces\r\n if (this.config.generateForms) {\r\n if (entityInfo.forms.length > 0) {\r\n const formResults = generateEntityForms(\r\n entityInfo.forms,\r\n entityName,\r\n entityInfo.attributes,\r\n {\r\n labelConfig: this.config.labelConfig,\r\n },\r\n );\r\n\r\n if (formResults.length > 0) {\r\n // Combine all forms for one entity into a single file\r\n const combinedContent = formResults.map((f) => f.content).join('\\n');\r\n files.push({\r\n relativePath: `forms/${entityName}.ts`,\r\n content: addGeneratedHeader(combinedContent),\r\n type: 'form',\r\n });\r\n }\r\n } else {\r\n warnings.push(`No forms found for ${entityName}`);\r\n }\r\n }\r\n\r\n // Generate entity fields enum and navigation properties (R4-03)\r\n if (this.config.generateEntities) {\r\n const fieldsEnumContent = generateEntityFieldsEnum(entityInfo, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n const navPropsContent = generateEntityNavigationProperties(entityInfo, {\r\n labelConfig: this.config.labelConfig,\r\n });\r\n\r\n // Combine both outputs into a single file (navProps may be empty if no lookups)\r\n const combinedFieldsContent = navPropsContent\r\n ? `${fieldsEnumContent}\\n${navPropsContent}`\r\n : fieldsEnumContent;\r\n\r\n files.push({\r\n relativePath: `fields/${entityName}.ts`,\r\n content: addGeneratedHeader(combinedFieldsContent),\r\n type: 'fields',\r\n });\r\n }\r\n\r\n return { entityLogicalName: entityName, files, warnings };\r\n }\r\n\r\n /**\r\n * Generate Custom API Action/Function executor files.\r\n */\r\n private async generateActions(metadataClient: MetadataClient): Promise<GeneratedFile[]> {\r\n const files: GeneratedFile[] = [];\r\n\r\n this.logger.info('Fetching Custom APIs...');\r\n let customApis = await metadataClient.getCustomApis();\r\n\r\n // Apply prefix filter if configured\r\n if (this.config.actionsFilter) {\r\n const prefix = this.config.actionsFilter.toLowerCase();\r\n const before = customApis.length;\r\n customApis = customApis.filter((api) => api.api.uniquename.toLowerCase().startsWith(prefix));\r\n this.logger.info(`Filtered Custom APIs by prefix \"${this.config.actionsFilter}\": ${before} -> ${customApis.length}`);\r\n }\r\n\r\n if (customApis.length > 0) {\r\n const importPath = '@xrmforge/helpers';\r\n const grouped = groupCustomApis(customApis);\r\n\r\n for (const [key, apis] of grouped.actions) {\r\n const module = generateActionModule(apis, false, { importPath });\r\n\r\n files.push({\r\n relativePath: `actions/${key}.ts`,\r\n content: addGeneratedHeader(module),\r\n type: 'action',\r\n });\r\n }\r\n\r\n for (const [key, apis] of grouped.functions) {\r\n const module = generateActionModule(apis, true, { importPath });\r\n\r\n files.push({\r\n relativePath: `functions/${key}.ts`,\r\n content: addGeneratedHeader(module),\r\n type: 'action',\r\n });\r\n }\r\n\r\n this.logger.info(`Generated ${grouped.actions.size} action groups, ${grouped.functions.size} function groups`);\r\n } else {\r\n this.logger.info('No Custom APIs found');\r\n }\r\n\r\n return files;\r\n }\r\n\r\n /**\r\n * Extract picklist attributes with their OptionSet metadata.\r\n * Maps the raw EntityTypeInfo data to the format expected by the OptionSet generator.\r\n */\r\n private getPicklistAttributes(\r\n entityInfo: EntityTypeInfo,\r\n ): Array<{ SchemaName: string; OptionSet: OptionSetMetadata | null; GlobalOptionSet: OptionSetMetadata | null }> {\r\n const result: Array<{ SchemaName: string; OptionSet: OptionSetMetadata | null; GlobalOptionSet: OptionSetMetadata | null }> = [];\r\n\r\n for (const attr of entityInfo.picklistAttributes) {\r\n result.push({\r\n SchemaName: attr.SchemaName,\r\n OptionSet: attr.OptionSet ?? null,\r\n GlobalOptionSet: attr.GlobalOptionSet ?? null,\r\n });\r\n }\r\n\r\n // Also include State and Status attributes\r\n for (const attr of entityInfo.stateAttributes) {\r\n result.push({\r\n SchemaName: attr.SchemaName,\r\n OptionSet: attr.OptionSet ?? null,\r\n GlobalOptionSet: null,\r\n });\r\n }\r\n for (const attr of entityInfo.statusAttributes) {\r\n result.push({\r\n SchemaName: attr.SchemaName,\r\n OptionSet: attr.OptionSet ?? null,\r\n GlobalOptionSet: null,\r\n });\r\n }\r\n\r\n return result;\r\n }\r\n}\r\n"],"mappings":"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","names":["ErrorCode","LogLevel","log","path","resolve","log","log","path","log","log","path","log","labelToPascalMember","join"]}