UNPKG

dataverse-type-gen

Version:

TypeScript type generator for Dataverse entities and metadata

1,467 lines (1,458 loc) 180 kB
#!/usr/bin/env node import { processEntityMetadata } from '../chunk-IF2SU3NK.mjs'; import { Command } from 'commander'; import process2 from 'process'; import os from 'os'; import tty from 'tty'; import { DefaultAzureCredential } from '@azure/identity'; import crypto from 'crypto'; import { promises } from 'fs'; import { join, resolve, dirname, relative } from 'path'; import { Project, ModuleKind, ScriptTarget } from 'ts-morph'; import { createInterface } from 'readline'; // node_modules/.pnpm/chalk@5.4.1/node_modules/chalk/source/vendor/ansi-styles/index.js var ANSI_BACKGROUND_OFFSET = 10; var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`; var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`; var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`; var styles = { modifier: { reset: [0, 0], // 21 isn't widely supported and 22 does the same thing bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], // Bright color blackBright: [90, 39], gray: [90, 39], // Alias of `blackBright` grey: [90, 39], // Alias of `blackBright` redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], // Bright color bgBlackBright: [100, 49], bgGray: [100, 49], // Alias of `bgBlackBright` bgGrey: [100, 49], // Alias of `bgBlackBright` bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } }; Object.keys(styles.modifier); var foregroundColorNames = Object.keys(styles.color); var backgroundColorNames = Object.keys(styles.bgColor); [...foregroundColorNames, ...backgroundColorNames]; function assembleStyles() { const codes = /* @__PURE__ */ new Map(); for (const [groupName, group] of Object.entries(styles)) { for (const [styleName, style] of Object.entries(group)) { styles[styleName] = { open: `\x1B[${style[0]}m`, close: `\x1B[${style[1]}m` }; group[styleName] = styles[styleName]; codes.set(style[0], style[1]); } Object.defineProperty(styles, groupName, { value: group, enumerable: false }); } Object.defineProperty(styles, "codes", { value: codes, enumerable: false }); styles.color.close = "\x1B[39m"; styles.bgColor.close = "\x1B[49m"; styles.color.ansi = wrapAnsi16(); styles.color.ansi256 = wrapAnsi256(); styles.color.ansi16m = wrapAnsi16m(); styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); Object.defineProperties(styles, { rgbToAnsi256: { value(red, green, blue) { if (red === green && green === blue) { if (red < 8) { return 16; } if (red > 248) { return 231; } return Math.round((red - 8) / 247 * 24) + 232; } return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5); }, enumerable: false }, hexToRgb: { value(hex) { const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16)); if (!matches) { return [0, 0, 0]; } let [colorString] = matches; if (colorString.length === 3) { colorString = [...colorString].map((character) => character + character).join(""); } const integer = Number.parseInt(colorString, 16); return [ /* eslint-disable no-bitwise */ integer >> 16 & 255, integer >> 8 & 255, integer & 255 /* eslint-enable no-bitwise */ ]; }, enumerable: false }, hexToAnsi256: { value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)), enumerable: false }, ansi256ToAnsi: { value(code) { if (code < 8) { return 30 + code; } if (code < 16) { return 90 + (code - 8); } let red; let green; let blue; if (code >= 232) { red = ((code - 232) * 10 + 8) / 255; green = red; blue = red; } else { code -= 16; const remainder = code % 36; red = Math.floor(code / 36) / 5; green = Math.floor(remainder / 6) / 5; blue = remainder % 6 / 5; } const value = Math.max(red, green, blue) * 2; if (value === 0) { return 30; } let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red)); if (value === 2) { result += 60; } return result; }, enumerable: false }, rgbToAnsi: { value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), enumerable: false }, hexToAnsi: { value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), enumerable: false } }); return styles; } var ansiStyles = assembleStyles(); var ansi_styles_default = ansiStyles; function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) { const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--"; const position = argv.indexOf(prefix + flag); const terminatorPosition = argv.indexOf("--"); return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); } var { env } = process2; var flagForceColor; if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) { flagForceColor = 0; } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) { flagForceColor = 1; } function envForceColor() { if ("FORCE_COLOR" in env) { if (env.FORCE_COLOR === "true") { return 1; } if (env.FORCE_COLOR === "false") { return 0; } return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); } } function translateLevel(level) { if (level === 0) { return false; } return { level, hasBasic: true, has256: level >= 2, has16m: level >= 3 }; } function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) { const noFlagForceColor = envForceColor(); if (noFlagForceColor !== void 0) { flagForceColor = noFlagForceColor; } const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; if (forceColor === 0) { return 0; } if (sniffFlags) { if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) { return 3; } if (hasFlag("color=256")) { return 2; } } if ("TF_BUILD" in env && "AGENT_NAME" in env) { return 1; } if (haveStream && !streamIsTTY && forceColor === void 0) { return 0; } const min = forceColor || 0; if (env.TERM === "dumb") { return min; } if (process2.platform === "win32") { const osRelease = os.release().split("."); if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) { return Number(osRelease[2]) >= 14931 ? 3 : 2; } return 1; } if ("CI" in env) { if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => key in env)) { return 3; } if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") { return 1; } return min; } if ("TEAMCITY_VERSION" in env) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; } if (env.COLORTERM === "truecolor") { return 3; } if (env.TERM === "xterm-kitty") { return 3; } if ("TERM_PROGRAM" in env) { const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); switch (env.TERM_PROGRAM) { case "iTerm.app": { return version >= 3 ? 3 : 2; } case "Apple_Terminal": { return 2; } } } if (/-256(color)?$/i.test(env.TERM)) { return 2; } if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { return 1; } if ("COLORTERM" in env) { return 1; } return min; } function createSupportsColor(stream, options = {}) { const level = _supportsColor(stream, { streamIsTTY: stream && stream.isTTY, ...options }); return translateLevel(level); } var supportsColor = { stdout: createSupportsColor({ isTTY: tty.isatty(1) }), stderr: createSupportsColor({ isTTY: tty.isatty(2) }) }; var supports_color_default = supportsColor; // node_modules/.pnpm/chalk@5.4.1/node_modules/chalk/source/utilities.js function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); if (index === -1) { return string; } const substringLength = substring.length; let endIndex = 0; let returnValue = ""; do { returnValue += string.slice(endIndex, index) + substring + replacer; endIndex = index + substringLength; index = string.indexOf(substring, endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { let endIndex = 0; let returnValue = ""; do { const gotCR = string[index - 1] === "\r"; returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix; endIndex = index + 1; index = string.indexOf("\n", endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } // node_modules/.pnpm/chalk@5.4.1/node_modules/chalk/source/index.js var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default; var GENERATOR = Symbol("GENERATOR"); var STYLER = Symbol("STYLER"); var IS_EMPTY = Symbol("IS_EMPTY"); var levelMapping = [ "ansi", "ansi", "ansi256", "ansi16m" ]; var styles2 = /* @__PURE__ */ Object.create(null); var applyOptions = (object, options = {}) => { if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { throw new Error("The `level` option should be an integer from 0 to 3"); } const colorLevel = stdoutColor ? stdoutColor.level : 0; object.level = options.level === void 0 ? colorLevel : options.level; }; var chalkFactory = (options) => { const chalk2 = (...strings) => strings.join(" "); applyOptions(chalk2, options); Object.setPrototypeOf(chalk2, createChalk.prototype); return chalk2; }; function createChalk(options) { return chalkFactory(options); } Object.setPrototypeOf(createChalk.prototype, Function.prototype); for (const [styleName, style] of Object.entries(ansi_styles_default)) { styles2[styleName] = { get() { const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]); Object.defineProperty(this, styleName, { value: builder }); return builder; } }; } styles2.visible = { get() { const builder = createBuilder(this, this[STYLER], true); Object.defineProperty(this, "visible", { value: builder }); return builder; } }; var getModelAnsi = (model, level, type, ...arguments_) => { if (model === "rgb") { if (level === "ansi16m") { return ansi_styles_default[type].ansi16m(...arguments_); } if (level === "ansi256") { return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_)); } return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_)); } if (model === "hex") { return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_)); } return ansi_styles_default[type][model](...arguments_); }; var usedModels = ["rgb", "hex", "ansi256"]; for (const model of usedModels) { styles2[model] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; const bgModel = "bg" + model[0].toUpperCase() + model.slice(1); styles2[bgModel] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; } var proto = Object.defineProperties(() => { }, { ...styles2, level: { enumerable: true, get() { return this[GENERATOR].level; }, set(level) { this[GENERATOR].level = level; } } }); var createStyler = (open, close, parent) => { let openAll; let closeAll; if (parent === void 0) { openAll = open; closeAll = close; } else { openAll = parent.openAll + open; closeAll = close + parent.closeAll; } return { open, close, openAll, closeAll, parent }; }; var createBuilder = (self, _styler, _isEmpty) => { const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" ")); Object.setPrototypeOf(builder, proto); builder[GENERATOR] = self; builder[STYLER] = _styler; builder[IS_EMPTY] = _isEmpty; return builder; }; var applyStyle = (self, string) => { if (self.level <= 0 || !string) { return self[IS_EMPTY] ? "" : string; } let styler = self[STYLER]; if (styler === void 0) { return string; } const { openAll, closeAll } = styler; if (string.includes("\x1B")) { while (styler !== void 0) { string = stringReplaceAll(string, styler.close, styler.open); styler = styler.parent; } } const lfIndex = string.indexOf("\n"); if (lfIndex !== -1) { string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); } return openAll + string + closeAll; }; Object.defineProperties(createChalk.prototype, styles2); var chalk = createChalk(); createChalk({ level: stderrColor ? stderrColor.level : 0 }); var source_default = chalk; // src/error-logger.ts async function advancedLog(response, requestUrl, requestMethod, requestBody) { const timestamp = (/* @__PURE__ */ new Date()).toISOString(); const headers = {}; response.headers.forEach((value, key) => { headers[key] = value; }); const requestId = headers["request-id"] || headers["x-ms-request-id"] || headers["activityid"]; let errorDetails = { status: response.status, statusText: response.statusText, url: requestUrl || response.url, method: requestMethod || "GET", headers, timestamp, requestId }; try { const responseClone = response.clone(); const responseText = await responseClone.text(); errorDetails.body = responseText; if (responseText) { try { const errorResponse = JSON.parse(responseText); if (errorResponse.error) { errorDetails.error = errorResponse.error; } } catch (parseError) { console.warn("Failed to parse error response as JSON:", parseError); } } } catch (textError) { console.warn("Failed to read response body:", textError); } console.error("\u{1F6A8} Dataverse API Error Details:"); console.error("\u2501".repeat(60)); console.error(`\u23F0 Timestamp: ${errorDetails.timestamp}`); console.error(`\u{1F310} URL: ${errorDetails.url}`); console.error(`\u{1F4CB} Method: ${errorDetails.method}`); console.error(`\u{1F4CA} Status: ${errorDetails.status} ${errorDetails.statusText}`); if (errorDetails.requestId) { console.error(`\u{1F194} Request ID: ${errorDetails.requestId}`); } if (errorDetails.error) { console.error("\u274C Error Details:"); console.error(` Code: ${errorDetails.error.code}`); console.error(` Message: ${errorDetails.error.message}`); if (errorDetails.error["@Microsoft.PowerApps.CDS.ErrorDetails.OperationStatus"]) { console.error(` Operation Status: ${errorDetails.error["@Microsoft.PowerApps.CDS.ErrorDetails.OperationStatus"]}`); } if (errorDetails.error["@Microsoft.PowerApps.CDS.ErrorDetails.SubErrorCode"]) { console.error(` Sub Error Code: ${errorDetails.error["@Microsoft.PowerApps.CDS.ErrorDetails.SubErrorCode"]}`); } if (errorDetails.error["@Microsoft.PowerApps.CDS.HelpLink"]) { console.error(` Help Link: ${errorDetails.error["@Microsoft.PowerApps.CDS.HelpLink"]}`); } if (errorDetails.error["@Microsoft.PowerApps.CDS.TraceText"]) { console.error(` Trace Text:`); console.error(` ${errorDetails.error["@Microsoft.PowerApps.CDS.TraceText"]}`); } if (errorDetails.error["@Microsoft.PowerApps.CDS.InnerError.Message"]) { console.error(` Inner Error: ${errorDetails.error["@Microsoft.PowerApps.CDS.InnerError.Message"]}`); } } else if (errorDetails.body) { console.error("\u{1F4C4} Raw Response Body:"); console.error(errorDetails.body); } console.error("\u{1F4CB} Request Headers:"); Object.entries(headers).forEach(([key, value]) => { if (key.toLowerCase().includes("authorization") || key.toLowerCase().includes("cookie")) { console.error(` ${key}: [REDACTED]`); } else { console.error(` ${key}: ${value}`); } }); if (requestBody) { console.error("\u{1F4E4} Request Body:"); console.error(requestBody); } console.error("\u2501".repeat(60)); return errorDetails; } function getStatusCodeDescription(status) { const statusDescriptions = { 400: "Bad Request - Invalid request syntax or unsupported query parameters", 401: "Unauthorized - Authentication failed or insufficient permissions", 403: "Forbidden - Access denied or insufficient privileges", 404: "Not Found - Resource does not exist", 405: "Method Not Allowed - HTTP method not supported for this resource", 412: "Precondition Failed - Concurrency or duplicate record issues", 413: "Payload Too Large - Request size exceeds limits", 429: "Too Many Requests - API rate limits exceeded", 501: "Not Implemented - Operation not supported", 503: "Service Unavailable - Dataverse service temporarily unavailable" }; return statusDescriptions[status] || "Unknown status code"; } var memoryCache = /* @__PURE__ */ new Map(); function generateCacheKey(url, options = {}) { const normalizedUrl = url.toLowerCase().trim(); const method = (options.method || "GET").toUpperCase(); const relevantHeaders = {}; if (options.headers) { const headers = options.headers; for (const [key, value] of Object.entries(headers)) { const lowerKey = key.toLowerCase(); if (lowerKey.includes("accept") || lowerKey.includes("prefer")) { relevantHeaders[lowerKey] = value; } } } const keyData = { url: normalizedUrl, method, headers: relevantHeaders }; return crypto.createHash("sha256").update(JSON.stringify(keyData)).digest("hex"); } async function getFromCache(url, options = {}) { const cacheKey = generateCacheKey(url, options); const memoryHit = memoryCache.get(cacheKey); if (memoryHit) { return new Response(memoryHit.body, { status: memoryHit.status, statusText: memoryHit.statusText, headers: new Headers(memoryHit.headers) }); } return null; } async function saveToCache(url, options = {}, response) { const cacheKey = generateCacheKey(url, options); if (response.ok && response.status === 200) { const body = await response.clone().text(); const headers = {}; response.headers.forEach((value, key) => { headers[key] = value; }); const cachedResponse = { body, status: response.status, statusText: response.statusText, headers }; memoryCache.set(cacheKey, cachedResponse); } } function clearMemoryCache() { const clearedCount = memoryCache.size; memoryCache.clear(); return clearedCount; } function getMemoryCacheStatus() { return { entryCount: memoryCache.size }; } var TokenManager = class { tokenCache = /* @__PURE__ */ new Map(); credential; bufferTimeMs = 5 * 60 * 1e3; // Refresh 5 minutes before expiry constructor(credential) { this.credential = credential || new DefaultAzureCredential(); } /** * Get a cached token or acquire a new one * This method is the key to performance - it ensures we only get 1 token per resource per run */ async getToken(resourceUrl) { const normalizedUrl = this.normalizeResourceUrl(resourceUrl); const cacheKey = normalizedUrl; const cached = this.tokenCache.get(cacheKey); if (cached && this.isTokenValid(cached)) { return cached.token; } try { const baseUrl = normalizedUrl.endsWith("/") ? normalizedUrl.slice(0, -1) : normalizedUrl; const scope = `${baseUrl}/.default`; const tokenResponse = await this.credential.getToken([scope]); if (!tokenResponse?.token) { throw new Error("No token received from credential"); } const cachedToken = { token: tokenResponse.token, expiresAt: tokenResponse.expiresOnTimestamp, resourceUrl: normalizedUrl }; this.tokenCache.set(cacheKey, cachedToken); return tokenResponse.token; } catch (error) { throw new Error( `Failed to acquire Azure access token for resource: ${resourceUrl} Error: ${error instanceof Error ? error.message : String(error)} Please authenticate via one of these methods: \u2022 Azure CLI: az login \u2022 Environment variables: AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID \u2022 Managed Identity (when running in Azure)` ); } } /** * Pre-acquire token for a resource to avoid delays during API calls */ async preAcquireToken(resourceUrl) { await this.getToken(resourceUrl); } /** * Check if a cached token is still valid (with buffer time) */ isTokenValid(cached) { const now = Date.now(); const expiresWithBuffer = cached.expiresAt - this.bufferTimeMs; return now < expiresWithBuffer; } /** * Normalize resource URL for consistent caching */ normalizeResourceUrl(resourceUrl) { if (!resourceUrl) { throw new Error("Resource URL is required for authentication"); } try { const url = new URL(resourceUrl); return `${url.protocol}//${url.host}`; } catch { throw new Error(`Invalid resource URL: ${resourceUrl}`); } } /** * Clear all cached tokens (useful for testing) */ clearCache() { this.tokenCache.clear(); } /** * Get cache statistics */ getCacheStats() { const totalTokens = this.tokenCache.size; let validTokens = 0; for (const cached of this.tokenCache.values()) { if (this.isTokenValid(cached)) { validTokens++; } } return { totalTokens, validTokens }; } }; var globalTokenManager = null; function getTokenManager(credential) { if (!globalTokenManager) { globalTokenManager = new TokenManager(credential); } return globalTokenManager; } async function preAcquireGlobalToken(resourceUrl, credential) { const manager = getTokenManager(credential); await manager.preAcquireToken(resourceUrl); } // src/auth/index.ts function createAuthenticatedFetcher(resourceUrl, options = {}) { const { maxRetries = 3, baseDelay = 1e3, credential } = options; return async function authenticatedFetch(url, requestOptions = {}) { const fullUrl = url.startsWith("http") ? url : `${resourceUrl.replace(/\/$/, "")}${url}`; let lastError = null; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const tokenManager = getTokenManager(credential); const accessToken = await tokenManager.getToken(resourceUrl); if (!accessToken) { throw new Error("Failed to acquire access token"); } if (!requestOptions.method || requestOptions.method.toUpperCase() === "GET") { const cachedResponse = await getFromCache(fullUrl, requestOptions); if (cachedResponse) { return cachedResponse; } } const headers = new Headers(requestOptions.headers); headers.set("Authorization", `Bearer ${accessToken}`); headers.set("Accept", "application/json"); headers.set("Content-Type", "application/json"); headers.set("Prefer", 'odata.include-annotations="*"'); const response = await fetch(fullUrl, { ...requestOptions, headers }); if (response.ok && response.headers.get("content-type")?.includes("application/json")) { } if (response.ok && (!requestOptions.method || requestOptions.method.toUpperCase() === "GET")) { await saveToCache(fullUrl, requestOptions, response.clone()); } if (response.status === 429) { const retryAfter = parseInt(response.headers.get("Retry-After") || "5") * 1e3; if (attempt < maxRetries) { await new Promise((resolve3) => setTimeout(resolve3, retryAfter)); continue; } } if (!response.ok) { await advancedLog(response, fullUrl, requestOptions.method || "GET", requestOptions.body); } return response; } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); if (attempt < maxRetries) { const delay = baseDelay * Math.pow(2, attempt); await new Promise((resolve3) => setTimeout(resolve3, delay)); } } } throw lastError || new Error("Authentication failed after all retries"); }; } // src/client/concurrent-queue.ts var ConcurrentRequestQueue = class { activeRequests = 0; maxConcurrent; queue = []; // eslint-disable-line @typescript-eslint/no-explicit-any rateLimitInfo = {}; requestHistory = []; totalRequestsCounter = 0; // Track total API calls made lastProgressLog = 0; // Track when we last logged progress constructor(maxConcurrent = 25) { const platformMaxConcurrent = process.platform === "win32" ? 15 : 25; this.maxConcurrent = Math.min(maxConcurrent, platformMaxConcurrent); } /** * Execute a request with automatic queuing and retry */ async execute(request, options = {}) { const { maxRetries = 3, priority = 0 } = options; return new Promise((resolve3, reject) => { const queuedRequest = { execute: async () => { try { this.activeRequests++; this.totalRequestsCounter++; this.recordRequest(); const response = await request(); this.updateRateLimitInfo(response); if (response.status === 429) { const retryAfter = this.parseRetryAfter(response); if (queuedRequest.retryCount < queuedRequest.maxRetries) { console.warn(`Rate limited (429), retrying after ${retryAfter}ms...`); await this.delay(retryAfter); queuedRequest.retryCount++; this.queue.unshift(queuedRequest); return; } else { throw new Error(`Rate limit exceeded after ${maxRetries} retries`); } } if (!response.ok) { if (options.url) { await advancedLog(response, options.url, options.method || "GET"); } throw new Error(`Request failed: ${response.status} ${response.statusText}`); } const result = await response.json(); resolve3(result); } catch (error) { if (queuedRequest.retryCount < queuedRequest.maxRetries && this.isRetryableError(error)) { console.warn(`Request failed, retrying (${queuedRequest.retryCount + 1}/${queuedRequest.maxRetries})...`, error); queuedRequest.retryCount++; await this.delay(Math.pow(2, queuedRequest.retryCount) * 1e3); this.queue.unshift(queuedRequest); } else { reject(error); } } finally { this.activeRequests--; this.processNext(); } }, resolve: resolve3, reject, retryCount: 0, maxRetries, priority }; const insertIndex = this.queue.findIndex((req) => req.priority < priority); if (insertIndex === -1) { this.queue.push(queuedRequest); } else { this.queue.splice(insertIndex, 0, queuedRequest); } this.processNext(); }); } /** * Process the next request in the queue if capacity allows */ async processNext() { if (this.shouldThrottle()) { const delay = this.calculateThrottleDelay(); if (delay > 0) { setTimeout(() => this.processNext(), delay); return; } } if (this.activeRequests >= this.maxConcurrent || this.queue.length === 0) { return; } const request = this.queue.shift(); if (request) { request.execute().catch((error) => { console.error("Queue request execution failed:", error); }); } } /** * Update rate limit information from response headers */ updateRateLimitInfo(response) { const remainingRequests = response.headers.get("x-ms-ratelimit-burst-remaining-xrm-requests"); const remainingDuration = response.headers.get("x-ms-ratelimit-time-remaining-xrm-requests"); const retryAfter = response.headers.get("Retry-After"); if (remainingRequests) { this.rateLimitInfo.remainingRequests = parseInt(remainingRequests, 10); } if (remainingDuration) { this.rateLimitInfo.remainingDuration = parseInt(remainingDuration, 10); } if (retryAfter) { this.rateLimitInfo.retryAfter = parseInt(retryAfter, 10); } } /** * Parse Retry-After header value */ parseRetryAfter(response) { const retryAfter = response.headers.get("Retry-After"); if (!retryAfter) return 1e3; const seconds = parseInt(retryAfter, 10); return isNaN(seconds) ? 1e3 : seconds * 1e3; } /** * Record request timestamp for rate limiting */ recordRequest() { const now = Date.now(); this.requestHistory.push(now); const fiveMinutesAgo = now - 5 * 60 * 1e3; while (this.requestHistory.length > 0 && this.requestHistory[0] < fiveMinutesAgo) { this.requestHistory.shift(); } } /** * Check if we should throttle requests based on rate limits */ shouldThrottle() { if (this.rateLimitInfo.remainingRequests !== void 0) { return this.rateLimitInfo.remainingRequests < 100; } const requestsInLast5Minutes = this.requestHistory.length; return requestsInLast5Minutes > 5e3; } /** * Calculate delay for throttling */ calculateThrottleDelay() { if (this.rateLimitInfo.retryAfter) { return this.rateLimitInfo.retryAfter * 1e3; } if (this.rateLimitInfo.remainingDuration && this.rateLimitInfo.remainingRequests) { const averageDelay = this.rateLimitInfo.remainingDuration / this.rateLimitInfo.remainingRequests; return Math.max(averageDelay * 1e3, 100); } const requestsInLast5Minutes = this.requestHistory.length; if (requestsInLast5Minutes > 5e3) { return Math.min((requestsInLast5Minutes - 5e3) * 10, 5e3); } return 0; } /** * Check if an error is retryable */ isRetryableError(error) { if (error instanceof Error) { const message = error.message.toLowerCase(); return message.includes("timeout") || message.includes("network") || message.includes("connection") || message.includes("429"); } return false; } /** * Delay utility */ delay(ms) { return new Promise((resolve3) => setTimeout(resolve3, ms)); } /** * Get current queue status */ getStatus() { return { activeRequests: this.activeRequests, queuedRequests: this.queue.length, rateLimitInfo: { ...this.rateLimitInfo }, requestsInLast5Minutes: this.requestHistory.length, totalRequestsMade: this.totalRequestsCounter }; } /** * Log final statistics */ logFinalStats() { } /** * Wait for all active requests to complete */ async waitForCompletion() { while (this.activeRequests > 0 || this.queue.length > 0) { await this.delay(100); } } }; var globalRequestQueue = new ConcurrentRequestQueue(); // src/client/or-filter-batching.ts function getAuthenticatedFetch() { const dataverseUrl = process.env.DATAVERSE_INSTANCE; if (!dataverseUrl) { throw new Error("DATAVERSE_INSTANCE environment variable is required"); } return createAuthenticatedFetcher(dataverseUrl); } var OR_BATCH_SIZE = 15; async function fetchEntitiesWithOrFilter(entityNames, select = [ "LogicalName", "SchemaName", "DisplayName", "Description", "HasActivities", "HasFeedback", "HasNotes", "IsActivity", "IsCustomEntity", "OwnershipType", "PrimaryIdAttribute", "PrimaryNameAttribute", "EntitySetName" ], onProgress) { if (entityNames.length === 0) { return []; } const allEntities = []; let processedCount = 0; for (let i = 0; i < entityNames.length; i += OR_BATCH_SIZE) { const batch = entityNames.slice(i, i + OR_BATCH_SIZE); try { const batchEntities = await fetchEntityBatchWithOrFilter(batch, select); allEntities.push(...batchEntities); processedCount += batch.length; if (onProgress) { const lastEntityInBatch = batch[batch.length - 1]; onProgress(processedCount, entityNames.length, lastEntityInBatch); } } catch (error) { console.warn(`Failed to fetch entity batch [${batch.join(", ")}]:`, error); console.log(`Falling back to individual requests for batch: ${batch.join(", ")}`); for (const entityName of batch) { try { const entity = await fetchSingleEntityWithQueue(entityName, select); if (entity) { allEntities.push(entity); } processedCount++; if (onProgress) { onProgress(processedCount, entityNames.length, entityName); } } catch (singleError) { console.warn(`Failed to fetch individual entity '${entityName}':`, singleError); processedCount++; if (onProgress) { onProgress(processedCount, entityNames.length, entityName); } } } } } return allEntities; } async function fetchEntityBatchWithOrFilter(entityNames, select) { const orConditions = entityNames.map((name) => `LogicalName eq '${name}'`); const filter = orConditions.join(" or "); const params = new URLSearchParams(); if (select.length > 0) { params.append("$select", select.join(",")); } params.append("$filter", filter); const url = `/api/data/v9.1/EntityDefinitions?${params.toString()}`; const response = await globalRequestQueue.execute( () => getAuthenticatedFetch()(url, { method: "GET" }), { url, method: "GET", priority: 1 // High priority for entity metadata } ); return response.value || []; } async function fetchSingleEntityWithQueue(entityLogicalName, select) { try { const params = new URLSearchParams(); if (select.length > 0) { params.append("$select", select.join(",")); } const url = `/api/data/v9.2/EntityDefinitions(LogicalName='${entityLogicalName}')${params.toString() ? `?${params.toString()}` : ""}`; const response = await globalRequestQueue.execute( () => getAuthenticatedFetch()(url, { method: "GET" }), { url, method: "GET", priority: 1 } ); return response; } catch (error) { if (error instanceof Error && error.message.includes("404")) { return null; } throw error; } } // src/client/index.ts function getAuthenticatedFetch2() { const dataverseUrl = process.env.DATAVERSE_INSTANCE; if (!dataverseUrl) { throw new Error("DATAVERSE_INSTANCE environment variable is required"); } return createAuthenticatedFetcher(dataverseUrl); } var COMPONENT_TYPES = { ENTITY: 1, ATTRIBUTE: 2, RELATIONSHIP: 10, OPTION_SET: 9 // Add more as needed }; async function fetchEntityMetadata(entityLogicalName, options = {}) { const { includeAttributes = true, includeRelationships = false, select = [ "LogicalName", "SchemaName", "DisplayName", "Description", "HasActivities", "HasFeedback", "HasNotes", "IsActivity", "IsCustomEntity", "OwnershipType", "PrimaryIdAttribute", "PrimaryNameAttribute", "EntitySetName" ] } = options; try { const params = new URLSearchParams(); if (select.length > 0) { params.append("$select", select.join(",")); } const expandItems = []; if (includeAttributes) { expandItems.push("Attributes"); } if (includeRelationships) { expandItems.push("OneToManyRelationships", "ManyToOneRelationships", "ManyToManyRelationships"); } if (expandItems.length > 0) { params.append("$expand", expandItems.join(",")); } const url = `/api/data/v9.2/EntityDefinitions(LogicalName='${entityLogicalName}')${params.toString() ? `?${params.toString()}` : ""}`; const authenticatedFetch = getAuthenticatedFetch2(); const response = await authenticatedFetch(url, { method: "GET" }); if (response.status === 404) { return null; } if (!response.ok) { await advancedLog(response, url, "GET"); const statusDescription = getStatusCodeDescription(response.status); throw new Error(`Failed to fetch entity metadata for '${entityLogicalName}': ${response.status} ${response.statusText} - ${statusDescription}`); } const entityData = await response.json(); if (includeAttributes && entityData) { try { const detailedAttributes = await fetchEntityAttributes(entityLogicalName); entityData.Attributes = detailedAttributes; } catch (error) { console.warn(`Failed to fetch detailed attributes for ${entityLogicalName}, using basic attributes:`, error); } } return entityData; } catch (error) { if (error instanceof Error) { throw new Error(`Error fetching entity metadata for '${entityLogicalName}': ${error.message}`); } throw error; } } async function fetchAllEntityMetadata(options = {}) { console.log(`\u{1F30D} Fetching ALL entities from Dataverse for complete type safety...`); try { const allEntities = await fetchAllEntities({ select: [ "LogicalName", "SchemaName", "DisplayName", "Description", "HasActivities", "HasFeedback", "HasNotes", "IsActivity", "IsCustomEntity", "OwnershipType", "PrimaryIdAttribute", "PrimaryNameAttribute", "EntitySetName" ] }); console.log(`Found ${allEntities.length} total entities in Dataverse`); if (options.includeAttributes) { const entitiesWithAttributes = await fetchMultipleEntities( allEntities.map((e) => e.LogicalName), { ...options, onProgress: options.onProgress } ); console.log(`\u2705 Successfully fetched detailed metadata for ${entitiesWithAttributes.length} entities`); return entitiesWithAttributes; } return allEntities; } catch (error) { if (error instanceof Error) { throw new Error(`Error fetching all entity metadata: ${error.message}`); } throw error; } } async function fetchEntityAttributes(entityLogicalName, attributeTypes = [ "Microsoft.Dynamics.CRM.PicklistAttributeMetadata", "Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata", "Microsoft.Dynamics.CRM.StateAttributeMetadata", "Microsoft.Dynamics.CRM.StatusAttributeMetadata", "Microsoft.Dynamics.CRM.LookupAttributeMetadata" ]) { try { const basicUrl = `/api/data/v9.2/EntityDefinitions(LogicalName='${entityLogicalName}')/Attributes?$select=LogicalName,SchemaName,DisplayName,Description,AttributeType,IsCustomAttribute,IsValidForCreate,IsValidForRead,IsValidForUpdate,RequiredLevel,IsPrimaryId,IsPrimaryName,AttributeOf`; const basicData = await globalRequestQueue.execute( () => getAuthenticatedFetch2()(basicUrl, { method: "GET" }), { url: basicUrl, method: "GET", priority: 2 // High priority for basic attributes } ); const basicAttributes = basicData.value; const existingAttributeTypes = /* @__PURE__ */ new Set(); for (const attr of basicAttributes) { if (attr.AttributeType === "Picklist") { existingAttributeTypes.add("Microsoft.Dynamics.CRM.PicklistAttributeMetadata"); } else if (attr.AttributeType === "MultiSelectPicklist") { existingAttributeTypes.add("Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata"); } else if (attr.AttributeType === "State") { existingAttributeTypes.add("Microsoft.Dynamics.CRM.StateAttributeMetadata"); } else if (attr.AttributeType === "Status") { existingAttributeTypes.add("Microsoft.Dynamics.CRM.StatusAttributeMetadata"); } else if (attr.AttributeType === "Lookup" || attr.AttributeType === "Customer" || attr.AttributeType === "Owner") { existingAttributeTypes.add("Microsoft.Dynamics.CRM.LookupAttributeMetadata"); } } const relevantAttributeTypes = attributeTypes.filter((type) => existingAttributeTypes.has(type)); if (relevantAttributeTypes.length === 0) { return basicAttributes; } const castPromises = relevantAttributeTypes.map(async (attributeType) => { try { const castUrl = `/api/data/v9.2/EntityDefinitions(LogicalName='${entityLogicalName}')/Attributes/${attributeType}`; const expandParams = attributeType.includes("Picklist") || attributeType.includes("State") || attributeType.includes("Status") ? "?$expand=OptionSet" : ""; const castData = await globalRequestQueue.execute( () => getAuthenticatedFetch2()(`${castUrl}${expandParams}`, { method: "GET" }), { url: `${castUrl}${expandParams}`, method: "GET", priority: 1, // Lower priority than basic attributes maxRetries: 2 // Fewer retries for attribute casting (non-critical) } ); return castData.value || []; } catch (castError) { console.warn(`Failed to cast attributes for type ${attributeType} on entity ${entityLogicalName}:`, castError); return []; } }); const castResults = await Promise.all(castPromises); for (const castAttributes of castResults) { for (const castAttr of castAttributes) { const basicIndex = basicAttributes.findIndex((attr) => attr.LogicalName === castAttr.LogicalName); if (basicIndex >= 0) { basicAttributes[basicIndex] = { ...basicAttributes[basicIndex], ...castAttr }; } } } return basicAttributes; } catch (error) { if (error instanceof Error) { throw new Error(`Error fetching entity attributes for '${entityLogicalName}': ${error.message}`); } throw error; } } async function fetchPublisherEntities(publisherPrefix) { try { const allCustomEntities = await fetchAllEntities({ customOnly: true, select: ["LogicalName", "SchemaName", "DisplayName", "Description", "IsCustomEntity", "PrimaryIdAttribute", "PrimaryNameAttribute"] }); const filterPattern = `${publisherPrefix}_`; const solutionEntities = allCustomEntities.filter( (entity) => entity.LogicalName.startsWith(filterPattern) ); return solutionEntities; } catch (error) { if (error instanceof Error) { throw new Error(`Error fetching publisher entities for publisher '${publisherPrefix}': ${error.message}`); } throw error; } } async function fetchMultipleEntities(entityNames, options = {}) { if (entityNames.length === 0) { return []; } const dataverseUrl = process.env.DATAVERSE_INSTANCE; if (!dataverseUrl) { throw new Error("DATAVERSE_INSTANCE environment variable is required"); } await preAcquireGlobalToken(dataverseUrl); const { onProgress, includeAttributes = true, includeRelationships = false } = options; let basicEntities = []; if (includeRelationships) { console.log(`Relationships needed - using individual entity calls for ${entityNames.length} entities`); const batchSize = 10; for (let i = 0; i < entityNames.length; i += batchSize) { const batch = entityNames.slice(i, i + batchSize); const batchPromises = batch.map( (entityName) => fetchEntityMetadata(entityName, options).catch((error) => { console.warn(`Failed to fetch entity '${entityName}':`, error); return null; }) ); const batchResults = await Promise.all(batchPromises); for (const result of batchResults) { if (result) { basicEntities.push(result); } } if (onProgress) { const currentCount = Math.min(i + batchSize, entityNames.length); const lastEntityInBatch = batch[batch.length - 1]; onProgress(currentCount, entityNames.length, lastEntityInBatch); } if (i + batchSize < entityNames.length) { await new Promise((resolve3) => setTimeout(resolve3, 500)); } } } else { console.log(`Using OR-filter batching for optimal performance`); basicEntities = await fetchEntitiesWithOrFilter(entityNames, [ "LogicalName", "SchemaName", "DisplayName", "Description", "HasActivities", "HasFeedback", "HasNotes", "IsActivity", "IsCustomEntity", "OwnershipType", "PrimaryIdAttribute", "PrimaryNameAttribute", "EntitySetName" ], onProgress ? (current, total, item) => { if (includeAttributes) { onProgress(current, total, `Basic: ${item || ""}`); } else { onProgress(current, total, item); } } : void 0); } if (includeAttributes && basicEntities.length > 0) { let completedAttributeEntities = 0; const batchSize = 50; const entitiesWithAttributes = []; for (let i = 0; i < basicEntities.length; i += batchSize) { const batch = basicEntities.slice(i, i + batchSize); const batchPromises = batch.map(async (entity) => { try { const detailedAttributes = await fetchEntityAttributes(entity.LogicalName); entity.Attributes = detailedAttributes; completedAttributeEntities++; if (onProgress) { const percentComplete = Math.floor(completedAttributeEntities / basicEntities.length * 100); const prevPercentComplete = Math.floor((completedAttributeEntities - 1) / basicEntities.length * 100); if (percentComplete !== prevPercentComplete || completedAttributeEntities === basicEntities.length) { onProgress(completedAttributeEntities, basicEntities.length, entity.LogicalName); } } return entity; } catch (error) { console.warn(`Failed to fetch attributes for ${entity.LogicalName}, using basic entity:`, error); completedAttributeEntities++; if (onProgress) { const percentComplete = Math.floor(completedAttributeEntities / basicEntities.length * 100); const prevPercentComplete = Math.floor((completedAttributeEntities - 1) / basicEntities.length * 100); if (percentComplete !== prevPercentComplete || completedAttributeEntities === basicEntities.length) { onProgress(completedAttributeEntities, basicEntities.length, entity.LogicalName); } } return entity; } }); const batchResults = await Promise.all(batchPromises); entitiesWithAttributes.push(...batchResults); } return entitiesWithAttributes; } return basicEntities; } async function fetchAllEntities(options = {}) { const { filter, customOnly = false, select = ["LogicalName", "SchemaName", "DisplayName", "IsCustomEntity"] } = options; try { const params = new URLSearchParams(); if (select.length > 0) { params.append("$select", select.join(",")); } const filters = []; if (customOnly) { filters.push("IsCustomEntity eq true"); } if (filter) { filters.push(filter); } if (filters.length > 0) { params.append("$filter", filters.join(" and ")); } const url = `/api/data/v9.2/EntityDefinitions?${params.toString()}`; const authenticatedFetch = getAuthenticatedFetch2(); const response = await authenticatedFetch(url, { method: "GET" }); if (!response.ok) { await advancedLog(response, url, "GET"); const statusDescription = getStatusCodeDescription(response.status); throw new Error(`Failed to fetch all entities: ${response.status} ${response.statusText} - ${statusDescription}`