UNPKG

pcf-vite-harness

Version:

Modern Vite-based development harness for PowerApps Component Framework (PCF) with hot module replacement and PowerApps-like environment simulation

1,359 lines (1,346 loc) 180 kB
'use strict'; var React3 = require('react'); var client = require('react-dom/client'); var react = require('@fluentui/react'); var jsxRuntime = require('react/jsx-runtime'); function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React3__namespace = /*#__PURE__*/_interopNamespace(React3); var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/utils/datasetRecordConverter.ts var datasetRecordConverter_exports = {}; __export(datasetRecordConverter_exports, { convertEntitiesToDatasetRecords: () => convertEntitiesToDatasetRecords }); async function fetchEntityMetadata(entityLogicalName) { try { const url = `/api/data/v9.1/EntityDefinitions(LogicalName='${entityLogicalName}')?$select=LogicalName,PrimaryIdAttribute,PrimaryNameAttribute`; const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await response.json(); return data; } catch (error) { throw new Error(`Failed to fetch metadata for entity ${entityLogicalName}: ${error}`); } } function getEntityPrimaryKey(entity, metadata) { return entity[metadata.PrimaryIdAttribute] || null; } function getEntityPrimaryName(entity, metadata) { const primaryName = entity[metadata.PrimaryNameAttribute]; if (primaryName) return primaryName; return entity.name || entity.fullname || entity.title || `Record ${entity[metadata.PrimaryIdAttribute] || "Unknown"}`; } async function convertEntityToRecord(entity, metadata, webAPI) { const recordId = getEntityPrimaryKey(entity, metadata); const primaryName = getEntityPrimaryName(entity, metadata); const entityType = metadata.LogicalName; const record = { _record: { initialized: 2, identifier: { etn: entityType, id: { guid: recordId || "" } }, fields: {} }, _columnAliasNameMap: {}, _primaryFieldName: metadata.PrimaryNameAttribute, _isDirty: false, _entityReference: { _etn: entityType, _id: recordId || "", _name: primaryName } }; Object.entries(entity).forEach(([key, value]) => { if (key.startsWith("@")) { return; } const field = { value, timestamp: (/* @__PURE__ */ new Date()).toISOString(), validationResult: { errorId: null, errorMessage: null, isValueValid: true, userInput: null, isOfflineSyncError: false } }; const formattedKey = `${key}${FORMATTED_VALUE_SUFFIX}`; if (entity[formattedKey]) { field.formatted = entity[formattedKey]; } record._record.fields[key] = field; }); record[metadata.PrimaryNameAttribute] = primaryName; if (metadata.PrimaryNameAttribute !== "name") { record.name = primaryName; } return record; } async function convertEntitiesToDatasetRecords(entities, entityLogicalName, webAPI) { const records = {}; console.log(`\u{1F504} Converting ${entities.length} entities to dataset records`); const recordIds = /* @__PURE__ */ new Set(); let duplicateCount = 0; const entityMetadata = await fetchEntityMetadata(entityLogicalName); for (const [index, entity] of entities.entries()) { const recordId = getEntityPrimaryKey(entity, entityMetadata); if (recordId) { if (recordIds.has(recordId)) { duplicateCount++; console.warn(`\u26A0\uFE0F Duplicate record ID found: ${recordId} (entity ${index + 1})`); } recordIds.add(recordId); records[recordId] = await convertEntityToRecord(entity, entityMetadata); if (index < 5) { console.log(`\u2705 Converted entity ${index + 1}: ${entityMetadata.PrimaryIdAttribute} = ${recordId}`); } } else { console.warn(`\u26A0\uFE0F Could not find primary key for entity ${index + 1}:`, Object.keys(entity).slice(0, 10)); } } console.log(`\u{1F511} Using primary ID field: ${entityMetadata.PrimaryIdAttribute} for entity ${entityLogicalName}`); if (duplicateCount > 0) { console.warn(`\u26A0\uFE0F Found ${duplicateCount} duplicate record IDs!`); } console.log(`\u2705 Converted ${Object.keys(records).length} records successfully`); return records; } var FORMATTED_VALUE_SUFFIX; var init_datasetRecordConverter = __esm({ "src/utils/datasetRecordConverter.ts"() { FORMATTED_VALUE_SUFFIX = "@OData.Community.Display.V1.FormattedValue"; } }); // src/utils/manifestExtractor.ts var manifestExtractor_exports = {}; __export(manifestExtractor_exports, { autoDetectManifest: () => autoDetectManifest, createTestProjectManifest: () => createTestProjectManifest, extractDatasetsFromXml: () => extractDatasetsFromXml, extractManifestFromBuiltXml: () => extractManifestFromBuiltXml, extractManifestFromComponentClass: () => extractManifestFromComponentClass, extractManifestFromXml: () => extractManifestFromXml }); function extractDatasetsFromXml(xmlContent) { try { const datasets = []; const datasetPattern = /<data-set\s+name=["']([^"']+)["'](?:\s+display-name-key=["']([^"']+)["'])?/g; let match; while ((match = datasetPattern.exec(xmlContent)) !== null) { datasets.push({ name: match[1], displayNameKey: match[2] }); } return datasets; } catch (error) { console.error("Error parsing datasets from manifest XML:", error); return []; } } function extractManifestFromXml(xmlContent) { try { const namespaceMatch = xmlContent.match(/namespace=["']([^"']+)["']/); const constructorMatch = xmlContent.match(/constructor=["']([^"']+)["']/); const versionMatch = xmlContent.match(/version=["']([^"']+)["']/); const displayNameMatch = xmlContent.match(/display-name-key=["']([^"']+)["']/); const descriptionMatch = xmlContent.match(/description-key=["']([^"']+)["']/); if (!namespaceMatch || !constructorMatch || !versionMatch) { return null; } const datasets = extractDatasetsFromXml(xmlContent); return { namespace: namespaceMatch[1], constructor: constructorMatch[1], version: versionMatch[1], displayName: displayNameMatch?.[1], description: descriptionMatch?.[1], datasets: datasets.length > 0 ? datasets : void 0 }; } catch (error) { console.error("Error parsing manifest XML:", error); return null; } } function extractManifestFromBuiltXml(xmlContent) { try { const namespaceMatch = xmlContent.match(/namespace=["']([^"']+)["']/); const constructorMatch = xmlContent.match(/constructor=["']([^"']+)["']/); const versionMatch = xmlContent.match(/version=["']([^"']+)["']/); const displayNameMatch = xmlContent.match(/display-name-key=["']([^"']+)["']/); const descriptionMatch = xmlContent.match(/description-key=["']([^"']+)["']/); if (!namespaceMatch || !constructorMatch || !versionMatch) { return null; } const datasets = extractDatasetsFromXml(xmlContent); return { namespace: namespaceMatch[1], constructor: constructorMatch[1], version: versionMatch[1], displayName: displayNameMatch?.[1], description: descriptionMatch?.[1], datasets: datasets.length > 0 ? datasets : void 0 }; } catch (error) { console.error("Error parsing built manifest XML:", error); return null; } } async function autoDetectManifest() { try { const possiblePaths = [ "./ControlManifest.Input.xml", "./src/ControlManifest.Input.xml", "./*/ControlManifest.Input.xml", "./out/controls/*/ControlManifest.xml" ]; if (typeof window !== "undefined") { console.warn( "Auto-detection not supported in browser environment. Please provide manifestInfo manually." ); return null; } return null; } catch (error) { console.warn("Error auto-detecting manifest:", error); return null; } } function extractManifestFromComponentClass(pcfClass, fallbackNamespace = "default") { const className = pcfClass.name; return { namespace: fallbackNamespace, constructor: className, version: "1.0.0", displayName: className, description: `${className} PCF Control` }; } function createTestProjectManifest() { return { namespace: "test", constructor: "dataset", version: "0.0.1", displayName: "dataset", description: "dataset description" }; } var init_manifestExtractor = __esm({ "src/utils/manifestExtractor.ts"() { } }); // src/createMockContext.ts function formatGuid(guid) { return guid.replace(/[{}]/g, ""); } async function getEntityMetadata(entityLogicalName) { if (!entityLogicalName || entityLogicalName === "unknown" || entityLogicalName.trim() === "") { console.warn(`\u26A0\uFE0F Invalid entity name for metadata fetch: "${entityLogicalName}"`); return null; } const response = await fetch( `/api/data/v9.2/EntityDefinitions(LogicalName='${entityLogicalName}')` ); if (!response.ok) { throw new Error(`Failed to get metadata for ${entityLogicalName}: ${response.status}`); } return response.json(); } function extractViewId(options) { if (options.includes("savedQuery=")) { const match = options.match(/savedQuery=([^&]+)/); return { viewId: match?.[1], isUserView: false }; } if (options.includes("userQuery=")) { const match = options.match(/userQuery=([^&]+)/); return { viewId: match?.[1], isUserView: true }; } return { isUserView: false }; } async function getViewInfo(viewId, isUserView) { try { const entitySet = isUserView ? "userqueries" : "savedqueries"; const response = await fetch( `/api/data/v9.2/${entitySet}(${viewId})?$select=name,returnedtypecode` ); if (response.ok) { const data = await response.json(); return { name: data.name, entityName: data.returnedtypecode }; } } catch (error) { console.warn("Failed to get view info:", error); } return {}; } function createDefaultWebAPI() { return { retrieveMultipleRecords: async (entityLogicalName, options) => { try { console.log(`\u{1F504} retrieveMultipleRecords called for ${entityLogicalName}`, { options }); const metadata = await getEntityMetadata(entityLogicalName); const collectionName = metadata.LogicalCollectionName; let url = `/api/data/v9.2/${collectionName}`; if (options) { url += options.startsWith("?") ? options : `?${options}`; } const isViewQuery = options && (options.includes("savedQuery=") || options.includes("userQuery=") || options.includes("fetchXml=")); let viewInfo = {}; if (isViewQuery && options) { const { viewId, isUserView } = extractViewId(options); if (viewId) { viewInfo = await getViewInfo(viewId, isUserView); console.log( `\u{1F50D} ${isUserView ? "User" : "System"} view query detected for ${entityLogicalName}`, { viewId, viewName: viewInfo.name || "Unknown View" } ); } else if (options.includes("fetchXml=")) { console.log(`\u{1F50D} FetchXML query detected for ${entityLogicalName}`); } } const response = await fetch(url); if (!response.ok) { const errorText = await response.text(); console.error(`API Error Response:`, errorText); throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); const recordCount = result.value?.length || 0; if (isViewQuery) { const viewDescription = viewInfo.name ? ` via "${viewInfo.name}"` : " via view"; console.log( `\u2705 Retrieved ${recordCount} records for ${entityLogicalName}${viewDescription}` ); } else { console.log(`\u2705 Retrieved ${recordCount} records for ${entityLogicalName}`); } if (result.value && !result.entities) { return { entities: result.value, nextLink: result["@odata.nextLink"] }; } return result; } catch (error) { console.error(`Error retrieving multiple records for ${entityLogicalName}:`, error); throw error; } }, retrieveRecord: async (entityLogicalName, id, options) => { try { const metadata = await getEntityMetadata(entityLogicalName); const collectionName = metadata.LogicalCollectionName; let url = `/api/data/v9.2/${collectionName}(${id})`; if (options) { url += options.startsWith("?") ? options : `?${options}`; } const response = await fetch(url); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); console.log(`\u2705 Retrieved record ${id} for ${entityLogicalName}`); return result; } catch (error) { console.error(`Error retrieving record ${id} for ${entityLogicalName}:`, error); throw error; } }, createRecord: async (entityLogicalName, data) => { try { const metadata = await getEntityMetadata(entityLogicalName); const collectionName = metadata.LogicalCollectionName; const url = `/api/data/v9.2/${collectionName}`; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Prefer: "return=representation" }, body: JSON.stringify(data) }); if (!response.ok) { const errorText = await response.text(); console.error(`Create Error Response:`, errorText); throw new Error(`HTTP error! status: ${response.status}`); } const result = await response.json(); const primaryIdAttribute = metadata.PrimaryIdAttribute; if (!primaryIdAttribute) { throw new Error(`No PrimaryIdAttribute found in metadata for ${entityLogicalName}`); } console.log(`\u2705 Created record for ${entityLogicalName}`); return { id: formatGuid(result[primaryIdAttribute]), name: result[metadata.PrimaryNameAttribute] || "", entityType: entityLogicalName }; } catch (error) { console.error(`Error creating record for ${entityLogicalName}:`, error); throw error; } }, updateRecord: async (entityLogicalName, id, data) => { try { const metadata = await getEntityMetadata(entityLogicalName); const collectionName = metadata.LogicalCollectionName; const url = `/api/data/v9.2/${collectionName}(${id})`; const response = await fetch(url, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }); if (!response.ok) { const errorText = await response.text(); console.error(`Update Error Response:`, errorText); throw new Error(`HTTP error! status: ${response.status}`); } console.log(`\u2705 Updated record ${id} for ${entityLogicalName}`); return { id: formatGuid(id), name: "", // For updates, we don't have the name without additional fetch entityType: entityLogicalName }; } catch (error) { console.error(`Error updating record ${id} for ${entityLogicalName}:`, error); throw error; } }, deleteRecord: async (entityLogicalName, id) => { try { const metadata = await getEntityMetadata(entityLogicalName); const collectionName = metadata.LogicalCollectionName; const url = `/api/data/v9.2/${collectionName}(${id})`; const response = await fetch(url, { method: "DELETE" }); if (!response.ok) { const errorText = await response.text(); console.error(`Delete Error Response:`, errorText); throw new Error(`HTTP error! status: ${response.status}`); } console.log(`\u2705 Deleted record ${id} for ${entityLogicalName}`); return { id: formatGuid(id), name: "", // For deletes, we don't have the name without additional fetch entityType: entityLogicalName }; } catch (error) { console.error(`Error deleting record ${id} for ${entityLogicalName}:`, error); throw error; } } }; } function createMockDataSet(options) { const viewId = undefined.VITE_PCF_VIEW_ID || undefined.VITE_PCF_DEFAULT_VIEW_ID || void 0; if (viewId) { console.log(`\u{1F4CB} Using view ID from environment: ${viewId}`); } else { console.log(`\u{1F4CA} No view ID found in environment, dataset will use default view`); } const defaultColumns = [ { name: "name", displayName: "Name", dataType: "SingleLine.Text", alias: "name", order: 1, visualSizeFactor: 1 } ]; return { getViewId: () => viewId || "", getTargetEntityType: () => options?.entityLogicalName || "account", isUserView: () => false, loading: false, paging: { totalResultCount: 0, hasNextPage: false, hasPreviousPage: false }, sorting: [], columns: options?.columns || defaultColumns, records: {}, clearSelectedRecordIds: () => { }, getSelectedRecordIds: () => [], setSelectedRecordIds: () => { }, refresh: () => Promise.resolve(), openDatasetItem: () => { }, ...options }; } function createMockContext(options) { const { controlId = `id-${crypto.randomUUID()}`, viewId = crypto.randomUUID(), displayName = "Dev User", userName = "devuser@contoso.com", userId = "dev-user-id", datasetOptions = {}, webAPI: customWebAPI = {}, entityType = "unknown", manifestInfo } = options || {}; console.log("\u{1F527} Creating mock context...", { entityType }); const envTargetTable = undefined.VITE_PCF_TARGET_TABLE || entityType; const envPageTable = undefined.VITE_PCF_PAGE_TABLE || entityType; const envPageRecordId = undefined.VITE_PCF_PAGE_RECORD_ID; if (envTargetTable !== entityType) { console.log(`\u{1F4CB} Using VITE_PCF_TARGET_TABLE environment variable: ${envTargetTable}`); } if (envPageRecordId) { console.log(`\u{1F4CB} Using VITE_PCF_PAGE_RECORD_ID environment variable: ${envPageRecordId}`); } else { console.log(`\u26A0\uFE0F VITE_PCF_PAGE_RECORD_ID not found, will generate random UUID`); } console.log("\u{1F50D} All PCF environment variables:", { VITE_PCF_PAGE_TABLE: undefined.VITE_PCF_PAGE_TABLE, VITE_PCF_PAGE_TABLE_NAME: undefined.VITE_PCF_PAGE_TABLE_NAME, VITE_PCF_PAGE_RECORD_ID: undefined.VITE_PCF_PAGE_RECORD_ID, VITE_PCF_TARGET_TABLE: undefined.VITE_PCF_TARGET_TABLE, VITE_PCF_TARGET_TABLE_NAME: undefined.VITE_PCF_TARGET_TABLE_NAME, VITE_PCF_VIEW_ID: undefined.VITE_PCF_VIEW_ID, VITE_PCF_VIEW_NAME: undefined.VITE_PCF_VIEW_NAME }); if (!manifestInfo?.datasets || manifestInfo.datasets.length === 0) { throw new Error( "\u274C No dataset found in manifest. Dataset components require a <data-set> element in ControlManifest.Input.xml.\n Please check your manifest file or use a field component instead." ); } const firstDataset = manifestInfo.datasets[0]; if (!firstDataset || !firstDataset.name) { throw new Error( '\u274C Invalid dataset configuration in manifest. Dataset must have a name attribute.\n Example: <data-set name="myDataset" display-name-key="Dataset_Display_Key" />' ); } const datasetName = firstDataset.name; const datasetDisplayName = firstDataset.displayNameKey || firstDataset.name; console.log(`\u{1F4CB} Using dataset from manifest: "${datasetName}" (${datasetDisplayName})`); const dataset = createMockDataSet({ name: datasetName, displayName: datasetDisplayName, entityLogicalName: envTargetTable !== "unknown" ? envTargetTable : "unknown", columns: [], // Will be populated based on discovered entity ...datasetOptions }); dataset._targetEntityType = envTargetTable !== "unknown" ? envTargetTable : void 0; Object.defineProperty(dataset, "getTargetEntityType", { value: function() { const envValue = undefined.VITE_PCF_TARGET_TABLE; if (envValue && envValue !== "unknown") { return envValue; } return this._targetEntityType || this.entityLogicalName || "unknown"; }, writable: true, configurable: true }); console.log(`\u{1F527} Created dataset "${datasetName}" with structure:`, { entityType, hasRecords: "records" in dataset, hasColumns: "columns" in dataset, recordCount: Object.keys(dataset.records || {}).length, columnCount: dataset.columns?.length || 0 }); return { accessibility: { _customControlProperties: { descriptor: { DomId: controlId, UniqueId: controlId + "_unique" } } }, page: { entityTypeName: envPageTable, entityId: (() => { const pageEntityId = envPageRecordId || crypto.randomUUID(); console.log(`\u{1F527} Page entity ID set to: ${pageEntityId} (from ${envPageRecordId ? "environment" : "generated UUID"})`); return pageEntityId; })(), isVisible: true }, parameters: { [datasetName]: dataset }, factory: { requestRender: () => { }, getPopupService: () => ({}) }, events: {}, copilot: {}, mode: { trackContainerResize: () => { }, isControlDisabled: false, isVisible: true }, client: { getClient: () => "Web", disableScroll: () => { }, getFormFactor: () => 0, isOffline: false, isNetworkAvailable: true }, device: { captureAudio: () => Promise.resolve(), captureImage: () => Promise.resolve(), captureVideo: () => Promise.resolve(), getBarcodeValue: () => Promise.resolve(), getCurrentPosition: () => Promise.resolve(), pickFile: () => Promise.resolve() }, formatting: { formatCurrency: (value) => `$${value}`, formatDateLong: (value) => value.toLocaleDateString(), formatDateShort: (value) => value.toLocaleDateString(), formatDateYearMonth: (value) => value.toLocaleDateString(), formatDecimal: (value) => value.toString(), formatInteger: (value) => Math.round(value).toString(), formatLanguage: (value) => value.toString(), formatTime: (value) => value.toLocaleTimeString(), getWeekOfYear: (value) => 1 }, navigation: { openAlertDialog: () => Promise.resolve(), openConfirmDialog: () => Promise.resolve(), openErrorDialog: () => Promise.resolve(), openFile: () => Promise.resolve(), openForm: () => Promise.resolve(), openUrl: () => { }, openWebResource: () => { } }, resources: { getString: (id) => id, getResource: (id) => "" }, updatedProperties: [], userSettings: { dateFormattingInfo: { abbreviatedDayNames: [], abbreviatedMonthNames: [], amDesignator: "AM", calendar: {}, calendarWeekRule: 0, dayNames: [], firstDayOfWeek: 0, fullDateTimePattern: "", longDatePattern: "", longTimePattern: "", monthDayPattern: "", monthNames: [], pmDesignator: "PM", shortDatePattern: "", shortTimePattern: "", shortestDayNames: [], sortableDateTimePattern: "", timeSeparator: ":", universalSortableDateTimePattern: "", yearMonthPattern: "" }, displayName, isRTL: false, languageId: 1033, numberFormattingInfo: { currencyDecimalDigits: 2, currencyDecimalSeparator: ".", currencyGroupSeparator: ",", currencyGroupSizes: [3], currencyNegativePattern: 0, currencyPositivePattern: 0, currencySymbol: "$", naNSymbol: "NaN", nativeDigits: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], negativeInfinitySymbol: "-\u221E", negativeSign: "-", numberDecimalDigits: 2, numberDecimalSeparator: ".", numberGroupSeparator: ",", numberGroupSizes: [3], numberNegativePattern: 1, percentDecimalDigits: 2, percentDecimalSeparator: ".", percentGroupSeparator: ",", percentGroupSizes: [3], percentNegativePattern: 0, percentPositivePattern: 0, percentSymbol: "%", perMilleSymbol: "\u2030", positiveInfinitySymbol: "\u221E", positiveSign: "+" }, securityRoles: [], userId, userName }, utils: { getEntityMetadata: () => Promise.resolve({}), hasEntityPrivilege: () => false, lookupObjects: () => Promise.resolve([]) }, webAPI: { ...createDefaultWebAPI(), ...customWebAPI } }; } // src/createViteConfig.ts async function validateDataverseToken(dataverseUrl) { try { const { getAzureToken } = await import('dataverse-utilities'); console.log("\u{1F510} Fetching Dataverse token..."); console.log(` Resource URL: ${dataverseUrl}`); const token = await getAzureToken({ resourceUrl: dataverseUrl, enableLogging: false // We'll handle our own logging }); if (!token) { throw new Error( "Failed to obtain Dataverse token. Make sure you are authenticated with Azure CLI (az login) or have managed identity configured." ); } console.log("\u2705 Dataverse token acquired successfully"); console.log(" Server will start with authentication ready"); } catch (error) { if (error instanceof Error && error.message.includes("Cannot resolve module")) { throw new Error( "\u274C dataverse-utilities package is required for Dataverse integration.\n Please install it: npm install dataverse-utilities" ); } console.error("\u274C Dataverse token validation failed:"); console.error(` ${error instanceof Error ? error.message : "Unknown error"}`); console.error(""); console.error("\u{1F4A1} Troubleshooting steps:"); console.error(" 1. Ensure you are logged in to Azure CLI: az login"); console.error(" 2. Check that VITE_DATAVERSE_URL is correct"); console.error(" 3. Verify you have access to the Dataverse environment"); throw error; } } async function createPCFViteConfig(options = {}) { const { defineConfig, loadEnv } = await import('vite'); const react = (await import('@vitejs/plugin-react')).default; const fs = await import('fs'); const path = await import('path'); return defineConfig(async ({ mode }) => { const envDir = process.cwd().endsWith("/dev") ? path.resolve(process.cwd(), "..") : process.cwd(); const env = loadEnv(mode, envDir, ""); const { dataverseUrl = env.VITE_DATAVERSE_URL, port = 3e3, hmrPort = 3001, open = true, viteConfig = {} } = options; let baseConfig = { root: "./dev", // Set root to dev directory for index.html resolution envDir, // Tell Vite where to look for .env files plugins: [react()], optimizeDeps: { exclude: ["fsevents"] // Prevent ESBuild native module errors on macOS }, server: { port, open, hmr: { port: hmrPort }, // Add middleware for /setup route configureServer(server) { server.middlewares.use((req, res, next) => { if (req.url === "/setup") { const indexPath = path.resolve(server.config.root, "index.html"); const html = fs.readFileSync(indexPath, "utf-8"); res.statusCode = 200; res.setHeader("Content-Type", "text/html"); res.end(html); } else { next(); } }); } }, resolve: { alias: { "@": "../../" // Default alias for PCF source } } }; if (!dataverseUrl) { throw new Error( "\u274C VITE_DATAVERSE_URL environment variable is required.\n Please set VITE_DATAVERSE_URL in your .env file or pass dataverseUrl in the config options.\n Example: VITE_DATAVERSE_URL=https://yourorg.crm.dynamics.com/" ); } await validateDataverseToken(dataverseUrl); const { createDataverseConfig } = __require("dataverse-utilities/vite"); const dataverseConfig = createDataverseConfig({ dataverseUrl }); baseConfig = { ...dataverseConfig, ...baseConfig, plugins: [ ...baseConfig.plugins || [], ...Array.isArray(dataverseConfig.plugins) ? dataverseConfig.plugins : [] ], server: { ...baseConfig.server, ...dataverseConfig.server || {} } }; return { ...baseConfig, ...viteConfig, plugins: [ ...baseConfig.plugins || [], ...Array.isArray(viteConfig.plugins) ? viteConfig.plugins : [] ], server: { ...baseConfig.server, ...viteConfig.server || {} } }; }); } // src/utils/pcfLifecycle.ts function createPCFManager(pcfClass, context, container) { return { instance: null, container, context, pcfClass }; } async function initPCF(manager) { if (!manager.container) { console.warn("Container not available for PCF initialization"); return; } try { if (manager.instance) { console.log("\u{1F504} Destroying existing PCF component before reinit"); manager.instance.destroy(); manager.instance = null; } manager.container.innerHTML = ""; console.log("\u{1F504} Initializing PCF component"); manager.instance = new manager.pcfClass(); await manager.instance.init( manager.context, () => console.log("PCF notifyOutputChanged called"), {}, manager.container ); await manager.instance.updateView(manager.context); console.log("\u2705 PCF Component initialized successfully"); } catch (error) { console.error("\u274C PCF Init failed:", error); throw error; } } async function updatePCFView(manager) { if (!manager.instance) { console.warn("No PCF component instance available for updateView"); return; } try { console.log("\u{1F501} Calling PCF updateView"); await manager.instance.updateView(manager.context); console.log("\u2705 PCF updateView completed"); } catch (error) { console.error("\u274C PCF updateView failed:", error); throw error; } } async function destroyPCF(manager) { if (!manager.instance) { console.warn("No PCF component instance available for destroy"); return; } try { console.log("\u{1F525} Destroying PCF component"); manager.instance.destroy(); manager.instance = null; manager.container.innerHTML = ""; console.log("\u2705 PCF Component destroyed"); } catch (error) { console.error("\u274C PCF destroy failed:", error); throw error; } } function isPCFInitialized(manager) { return !!manager.instance; } // src/utils/envValidation.ts function checkRequiredEnvVars() { const required = ["VITE_PCF_TARGET_TABLE"]; const missing = []; for (const varName of required) { const value = undefined[varName]; if (!value || value.trim() === "") { missing.push(varName); } } const isValid = missing.length === 0; console.log("\u{1F50D} Environment variable validation:", { required, missing, isValid, currentValues: { VITE_PCF_PAGE_TABLE: undefined.VITE_PCF_PAGE_TABLE, VITE_PCF_TARGET_TABLE: undefined.VITE_PCF_TARGET_TABLE } }); return { isValid, missing }; } function redirectToSetupIfNeeded(componentType = "field") { if (window.location.pathname === "/setup") { return false; } if (componentType === "field") { console.log("\u{1F527} Field component detected - skipping setup wizard"); return false; } const { isValid, missing } = checkRequiredEnvVars(); if (!isValid) { console.log(`\u26A0\uFE0F Missing required environment variables: ${missing.join(", ")}`); console.log("\u{1F504} Redirecting to setup wizard..."); window.history.replaceState(null, "", "/setup"); window.location.reload(); return true; } return false; } // src/utils/simpleDatasetLoader.ts function detectDatasets(context) { const datasets = []; if (!context?.parameters) { return { datasets, totalRecords: 0 }; } Object.keys(context.parameters).forEach((key) => { const param = context.parameters[key]; if (param && typeof param === "object" && "records" in param && "columns" in param) { const entityType = undefined.VITE_PCF_TARGET_TABLE || "unknown"; datasets.push({ name: key, entityLogicalName: entityType, recordCount: param.sortedRecordIds?.length || 0 }); } }); const totalRecords = datasets.reduce((sum, ds) => sum + ds.recordCount, 0); return { datasets, totalRecords }; } async function fetchDataForEntity(context, entityLogicalName) { try { const parentEntityId = undefined.VITE_PCF_PAGE_RECORD_ID; const parentEntityType = undefined.VITE_PCF_PAGE_TABLE || "pum_initiative"; let options = `$select=*&$top=5000`; if (parentEntityId && parentEntityType) { const lookupField = `_${parentEntityType}_value`; options += `&$filter=${lookupField} eq ${parentEntityId}`; } console.log(`\u{1F50D} Fetching data for ${entityLogicalName} with options: ${options}`); const result = await context.webAPI.retrieveMultipleRecords( entityLogicalName, options ); return result.entities || []; } catch (error) { console.error(`\u274C Failed to fetch ${entityLogicalName}:`, error); return []; } } async function injectRecordsIntoDataset(dataset, entities, entityLogicalName, webAPI) { try { const { convertEntitiesToDatasetRecords: convertEntitiesToDatasetRecords2 } = await Promise.resolve().then(() => (init_datasetRecordConverter(), datasetRecordConverter_exports)); const existingIds = dataset.sortedRecordIds || []; existingIds.forEach((id) => { if (dataset.records[id]) { delete dataset.records[id]; } }); const newRecords = await convertEntitiesToDatasetRecords2(entities, entityLogicalName, webAPI); const newIds = Object.keys(newRecords); Object.assign(dataset.records, newRecords); dataset.sortedRecordIds = newIds; console.log(`\u2705 Injected ${entities.length} records into dataset`); return true; } catch (error) { console.error("\u274C Failed to inject records:", error); return false; } } async function loadDatasetData(context, onComplete) { try { console.log("\u{1F680} Simple dataset loader starting..."); const analysis = detectDatasets(context); if (analysis.datasets.length === 0) { console.log("\u{1F4ED} No datasets found"); return; } console.log(`\u{1F4CA} Found ${analysis.datasets.length} datasets`); for (const datasetInfo of analysis.datasets) { console.log(`\u{1F504} Loading ${datasetInfo.name} (${datasetInfo.entityLogicalName})`); const entities = await fetchDataForEntity(context, datasetInfo.entityLogicalName); if (entities.length > 0) { const dataset = context.parameters[datasetInfo.name]; await injectRecordsIntoDataset(dataset, entities, datasetInfo.entityLogicalName, context.webAPI); console.log(`\u2705 Loaded ${entities.length} records for ${datasetInfo.name}`); } else { console.log(`\u{1F4ED} No records found for ${datasetInfo.name}`); } } console.log("\u{1F504} Dataset loading complete, triggering callback..."); if (onComplete) { onComplete(); } } catch (error) { console.error("\u274C Simple dataset loader failed:", error); } } function startAutoLoad(context, onComplete) { const autoRefreshEnabled = undefined.VITE_PCF_AUTO_REFRESH !== "false"; if (!autoRefreshEnabled) { console.log("\u{1F515} Auto-refresh disabled"); return; } console.log("\u{1F680} Starting auto-load immediately..."); loadDatasetData(context, onComplete); } var PowerAppsContainerInner = ({ context, pcfClass, className = "", manifestInfo, containerRef }) => { const pcfManagerRef = React3__namespace.useRef(null); React3__namespace.useEffect(() => { if (containerRef.current && !pcfManagerRef.current) { pcfManagerRef.current = createPCFManager(pcfClass, context, containerRef.current); initPCF(pcfManagerRef.current).catch(console.error); } return () => { if (pcfManagerRef.current) { destroyPCF(pcfManagerRef.current).catch(console.error); pcfManagerRef.current = null; } }; }, [pcfClass, context]); React3__namespace.useEffect(() => { if (context && pcfManagerRef.current) { startAutoLoad(context, () => { console.log("\u{1F504} Dataset loaded, triggering updateView..."); if (pcfManagerRef.current) { updatePCFView(pcfManagerRef.current).catch(console.error); } }); } }, [context]); return /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "tab-section2", className: `pa-g pa-ae pa-h pa-ht pa-cf pa-pb pa-du pa-bx flexbox ${className}`, style: { height: "100vh", width: "100vw", overflow: "hidden", backgroundColor: "#f3f2f1", fontFamily: '"Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif' }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pa-op pa-gm flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "id-875", "aria-label": "PCF Control", role: "tabpanel", className: "pa-g pa-cf pa-no pa-qm pa-bx pa-bf pa-pd forceNewStackContext flexbox", style: { height: "100%", width: "100%", flex: "1 1 auto" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "id-872_2", className: "pa-kr pa-cf pa-bu pa-bx pa-oe pa-qn flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { className: "pa-g pa-ct pa-h pa-j pa-hq flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "dataSetRoot_Subgrid_new_2_outer", className: "pa-g pa-ct pa-h pa-nk pa-nh flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "DataSetHostContainer_dataSetRoot_Subgrid_new_2", className: "pa-g pa-ct pa-h pa-nk pa-nh flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "dataSetRoot_Subgrid_new_2", className: "pa-g pa-ct pa-h pa-nk pa-nh flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { className: "pa-g pa-ct pa-bf pa-oe pa-bx pa-of pa-og flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { className: "pa-g pa-ct pa-k pa-ol flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { className: "pa-cf pa-ct pa-ox flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "id-c98688f0-2676-483b-ae68-504795de5dfe-121_outer", className: "pa-cf flexbox", style: { height: "100%", width: "100%", position: "relative" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { id: "id-c98688f0-2676-483b-ae68-504795de5dfe-12", className: "pa-cf flexbox", style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx( "div", { className: "customControl pcf-component flexbox", "data-id": "pcf_container", style: { width: "100%", height: "100%" }, ref: containerRef } ) } ) } ) } ) } ) } ) } ) } ) } ) } ) } ) } ) }) } ); }; var PowerAppsContainer = (props) => { const containerRef = React3__namespace.useRef(null); return /* @__PURE__ */ jsxRuntime.jsx(PowerAppsContainerInner, { ...props, containerRef }); }; // src/utils/manifestReader.ts function readManifestFromFileSystem() { if (typeof window !== "undefined") { console.log("\u{1F310} Browser environment detected - cannot read manifest from filesystem directly"); return null; } try { const fs = __require("fs"); const path = __require("path"); const possiblePaths = [ path.resolve(process.cwd(), "ControlManifest.Input.xml"), path.resolve(process.cwd(), "dataset/ControlManifest.Input.xml"), path.resolve(process.cwd(), "../dataset/ControlManifest.Input.xml"), path.resolve(process.cwd(), "src/ControlManifest.Input.xml"), path.resolve(process.cwd(), "control/ControlManifest.Input.xml") ]; const builtPaths = [ path.resolve(process.cwd(), "out/controls/dataset/ControlManifest.xml"), path.resolve(process.cwd(), "out/controls/control/ControlManifest.xml"), path.resolve(process.cwd(), "../out/controls/dataset/ControlManifest.xml") ]; const allPaths = [...possiblePaths, ...builtPaths]; for (const filePath of allPaths) { try { if (fs.existsSync(filePath)) { const content = fs.readFileSync(filePath, "utf-8"); console.log(`\u{1F4C4} Found manifest file at ${filePath}`); console.log(`\u{1F4C4} File content preview:`, content.substring(0, 200) + "..."); const manifest = parseManifestXml(content); if (manifest) { console.log(`\u2705 Successfully parsed manifest from ${filePath}:`, manifest); return manifest; } else { console.log(`\u274C Failed to parse manifest from ${filePath}`); } } } catch (error) { } } console.log("\u{1F4DD} No manifest file found in any of the searched paths:", allPaths); return null; } catch (error) { console.warn("Error reading manifest from file system:", error); return null; } } function parseManifestXml(xmlContent) { try { const { extractManifestFromXml: extractManifestFromXml2 } = (init_manifestExtractor(), __toCommonJS(manifestExtractor_exports)); const manifest = extractManifestFromXml2(xmlContent); if (!manifest) { const controlMatch = xmlContent.match(/<control[^>]*>/i); if (!controlMatch) return null; const controlElement = controlMatch[0]; const namespaceMatch = controlElement.match(/namespace=["']([^"']+)["']/i); const constructorMatch = controlElement.match(/constructor=["']([^"']+)["']/i); const versionMatch = controlElement.match(/version=["']([^"']+)["']/i); const displayNameMatch = controlElement.match(/display-name-key=["']([^"']+)["']/i); const descriptionMatch = controlElement.match(/description-key=["']([^"']+)["']/i); if (!namespaceMatch || !constructorMatch || !versionMatch) { return null; } const hasDataSet2 = xmlContent.includes("<data-set"); const componentType2 = hasDataSet2 ? "dataset" : "field"; return { namespace: namespaceMatch[1], constructor: constructorMatch[1], version: versionMatch[1], displayName: displayNameMatch?.[1], description: descriptionMatch?.[1], componentType: componentType2 }; } const hasDataSet = manifest.datasets && manifest.datasets.length > 0; const componentType = hasDataSet ? "dataset" : "field"; return { namespace: manifest.namespace, constructor: manifest.constructor, version: manifest.version, displayName: manifest.displayName, description: manifest.description, componentType, datasets: manifest.datasets }; } catch (error) { console.error("Error parsing manifest XML:", error); return null; } } function detectManifestInfo(pcfClass) { const fileSystemManifest = readManifestFromFileSystem(); if (fileSystemManifest) { return fileSystemManifest; } const className = pcfClass.name; console.log(`\u{1F4DD} No manifest file found, falling back to class name detection: ${className}`); let namespace = "default"; let constructor = className.toLowerCase(); if (className.includes("_")) { const parts = className.split("_"); namespace = parts[0] || "default"; constructor = parts.slice(1).join("_") || className; } else if (/^[A-Z][a-z]+[A-Z]/.test(className)) { const matches = className.match(/^([A-Z][a-z]+)(.+)$/); if (matches) { namespace = matches[1].toLowerCase(); constructor = matches[2].toLowerCase(); } } return { namespace, constructor, version: "1.0.0", displayName: constructor, description: `${constructor} PCF Control`, componentType: "field" // Default to field when manifest not found }; } // src/utils/viewDiscovery.ts async function getSystemViewsForEntity(entityLogicalName) { const url = `/api/data/v9.2/savedqueries?$filter=returnedtypecode eq '${entityLogicalName}'&$select=savedqueryid,name,returne